Direct3D 11에서 Direct3D 12로 포팅

이 섹션은 사용자 지정 Direct3D 11 그래픽 엔진에서 Direct3D 12로 포팅하는 방법에 대한 지침을 제공합니다.

디바이스 만들기

Direct3D 11과 Direct3D 12 모두 유사한 디바이스 생성 패턴을 공유합니다. 기존 Direct3D 12 드라이버는 모두 D3D_FEATURE_LEVEL_11_0 이상이므로 이전 기능 수준 및 관련 제한 사항을 무시할 수 있습니다.

또한 Direct3D 12에서는 DXGI 인터페이스를 사용하여 디바이스 정보를 명시적으로 열거해야 합니다. Direct3D 11에서는 Direct3D 디바이스에서 DXGI 디바이스에 다시 연결할 수 있으며 Direct3D 12에서는 지원되지 않습니다.

Direct3D 12에서 WARP 소프트웨어 디바이스를 만드는 작업은 IDXGIFactory4::EnumWarpAdapter에서 가져온 명시적 어댑터를 제공하여 수행됩니다. Direct3D 12용 WARP 디바이스는 그래픽 도구 선택적 기능이 사용하도록 설정된 시스템에서만 사용할 수 있습니다.

참고

D3D11CreateDeviceAndSwapChain과 동일하지 않습니다. Direct3D 11에서도 디바이스를 만들고 별도의 단계로 스왑 체인을 만드는 것이 더 낫기 때문에 이 함수를 사용하지 않는 것이 좋습니다.

커밋된 리소스

Direct3D 11에서 다음 인터페이스로 생성된 개체는 Direct3D 12에서 "커밋된 리소스"로 변환됩니다. 커밋된 리소스는 연결된 가상 주소 공간과 실제 페이지가 둘 다 있는 리소스입니다. 이것은 Direct3D 12의 기반이 되는 Microsoft WDD2(Windows Device Driver 2) 메모리 모델의 개념입니다.

Direct3D 11 리소스:

Direct3D 12에서 이들은 모두 ID3D12ResourceID3D12Device::CreateCommittedResource로 나타납니다.

예약된 리소스

예약된 리소스는 가상 주소 공간만 할당된 리소스이며 ID3D12Device::CreateHeap에 대한 호출이 있을 때까지 실제 메모리는 할당되지 않습니다. 이것은 Direct3D 11의 타일식 리소스와 본질적으로 동일한 개념입니다.

Direct3D 11에서 타일형 리소스를 설정한 다음 실제 메모리에 매핑하는 데 사용되는 플래그(D3D11_RESOURCE_MISC_FLAG)입니다.

  • D3D11_RESOURCE_MISC_TILED
  • D3D11_RESOURCE_MISC_TILE_POOL

데이터 업로드

Direct3D 11에는 단일 타임라인 표시됩니다(예: D3D11_SUBRESOURCE_DATA 사용하여 초기화된 데이터와 같이 시퀀스를 따라 호출한 다음 ID3D11DeviceContext::UpdateSubresource를 호출한 다음 ID3D11DeviceContext::Map 호출). 데이터의 복사본 수는 Direct3D 11 개발자에게 분명하지 않습니다.

Direct3D 12에는 GPU 타임라인(매핑 가능한 메모리에서 CopyTextureRegionCopyBufferRegion에 대한 호출로 설정)과 CPU 타임라인(Map에 대한 호출에 의해 결정)이라는 두 가지 타임라인이 있습니다. 공유 타임라인을 사용하는 Updatesubresources라는 도우미 함수가 제공됩니다(d3dx12.h 파일에). 도우미 함수에는 여러 가지 변형이 있습니다. 하나는 ID3D12Device::GetCopyableFootprints를 사용하고, 다른 하나는 힙 할당 메커니즘을 사용하고 또 다른 하나는 스택 할당 메커니즘을 사용합니다. 도우미 함수는 메모리의 중간 스테이징 영역을 통해 GPU와 CPU 모두에 리소스를 복사합니다.

일반적으로 GPU와 CPU에는 각각 자체 타임라인에 연결된 자체 리소스 복사본이 있습니다. 공유 타임라인 방식은 두 가지 복사본을 유사하게 유지합니다.

셰이더 및 셰이더 개체

Direct3D 11에서는 ID3D11Device 생성 메서드와 ID3D11DeviceContext 설정 메서드를 사용하여 셰이더 및 상태 개체를 많이 생성하고 이러한 개체의 상태를 설정합니다. 일반적으로 이러한 메서드에 많은 호출이 이루어지면, 호출은 드라이버가 그리는 시간에 결합되어 올바른 파이프라인 상태를 설정합니다.

Direct3D 12에서 파이프라인 상태 설정은 단일 개체(컴퓨팅 엔진의 경우 CreateComputePipelineState 및 그래픽 엔진의 경우 CreateGraphicsPipelineState)로 결합된 다음, SetPipelineState를 호출하여 그리기 호출 전에 명령 목록에 연결됩니다.

이러한 호출은 Direct3D 11에서 셰이더, 입력 레이아웃, 혼합 상태, 래스터라이저 상태, 깊이 스텐실 상태 등을 설정하는 모든 개별 호출을 대체합니다.

  • 디바이스 11 메서드: CreateInputLayout, CreateXShader, CreateDepthStencilStateCreateRasterizerState.
  • 디바이스 컨텍스트 11 메서드: IASetInputLayout, xxSetShader, OMSetBlendState, OMSetDepthStencilStateRSSetState.

Direct3D 12는 이전 컴파일된 셰이더 Blob을 지원할 수 있지만 FXC/D3DCompile API와 함께 셰이더 모델 5.1을 사용하거나 DXIL DXC 컴파일러를 사용하여 셰이더 모델 6을 사용하여 셰이더를 빌드해야 합니다. CheckFeatureSupportD3D12_FEATURE_SHADER_MODEL 셰이더 모델 6 지원의 유효성을 검사해야 합니다.

GPU로 작업 제출

Direct3D 11에서는 실제로 작업을 제출하는 방식에 대한 제어가 거의 없고 대부분 드라이버에 의해 처리되지만, 일부 컨트롤은 ID3D11DeviceContext::FlushIDXGISwapChain1::Present1 호출을 통해 활성화됩니다.

Direct3D 12에서는 작업 제출이 매우 명시적이며 애플리케이션에 의해 제어됩니다. 작업 제출을 위한 기본 구문은 ID3D12GraphicsCommandList이며, 모든 앱 명령을 기록하는 데 사용됩니다(ID3D11 지연된 컨텍스트와 개념이 매우 유사함). 명령 목록에 대한 백업 저장소는 ID3D12CommandAllocator에 의해 제공되며, Direct3D 12 드라이버가 명령 목록을 저장하는 데 사용할 메모리를 실제로 노출하기 때문에 앱이 명령 목록의 메모리 사용을 관리할 수 있습니다.

마지막으로 ID3D12CommandQueue는 GPU에 제출하는 명령 목록의 올바른 순서를 저장하는 선입 선출 큐입니다. GPU에서 명령 목록 하나가 실행을 완료한 경우에만 큐의 다음 명령 목록이 드라이버에 의해 제출됩니다.

Direct3D 11에는 명령 큐에 대한 명시적 개념이 없습니다. Direct3D 12에 대한 일반적인 설정에서 현재 프레임에 대해 현재 열려 있는 D3D12_COMMAND_LIST_TYPE_DIRECT 명령 목록은 Direct3D 11 직접 컨텍스트와 유사한 것으로 간주될 수 있습니다. 이렇게 하면 동일한 함수가 많이 제공됩니다.

D3D11DeviceContext ID3D12Graphics명령 목록
ClearDepthStencilView ClearDepthStencilView
ClearRenderTargetView ClearRenderTargetView
ClearUnorderedAccess* ClearUnorderedAccess*
그리기, DrawInstanced DrawInstanced
DrawIndexed, DrawIndexedInstanced DrawIndexedInstanced
Dispatch Dispatch
IASetInputLayout, xxSetShader 등 SetPipelineState
OMSetBlendState OMSetBlendFactor
OMSetDepthStencilState OMSetStencilRef
OMSetRenderTargets OMSetRenderTargets
RSSetViewports RSSetViewports
RSSetScissorRects RSSetScissorRects
IASetPrimitiveTopology IASetPrimitiveTopology
IASetVertexBuffers IASetVertexBuffers
IASetIndexBuffer IASetIndexBuffer
ResolveSubresource ResolveSubresource
CopySubresourceRegion CopyBufferRegion
UpdateSubresource CopyTextureRegion
CopyResource CopyResource

참고

D3D12_COMMAND_LIST_TYPE_BUNDLE 사용하여 만든 명령 목록은 지연된 컨텍스트에 대한 간소화입니다. 또한 Direct3D 12는 D3D12_COMMAND_LIST_TYPE_COPY 및 D3D12_COMMAND_LIST_TYPE_COMPUTE 명령 목록 형식을 통해 렌더링하는 즉시 컨텍스트의 일부 기능에 동시에 액세스할 수 있도록 지원합니다.

CPU/GPU 동기화

Direct3D 11에서는 CPU/GPU 동기화가 거의 자동이기 때문에 앱이 실제 메모리 상태를 유지할 필요가 없습니다.

Direct3D 12에서는 앱이 두 가지 타임라인(CPU 및 GPU)을 명시적으로 관리해야 합니다. 이렇게 하려면 GPU에 어떤 리소스가 얼마나 오래 필요한지에 대한 정보를 앱이 유지 관리해야 합니다. 또한, 애플리케이션은 GPU가 사용을 마칠 때까지 리소스(예: 커밋된 리소스, 힙, 명령 할당자) 콘텐츠가 변경되지 않도록 보장해야 합니다.

타임라인을 동기화하는 주요 개체는 ID3D12Fence 개체입니다. 펜스 작업은 매우 간단하며 GPU가 작업을 완료했을 때 신호를 보낼 수 있습니다. GPU와 CPU는 둘 다 신호를 보낼 수 있고 펜스를 기다릴 수도 있습니다.

일반적인 방식은 실행을 위한 명령 목록을 제출하면, 완료 시(데이터 읽기를 마치면) GPU가 펜스 신호를 전송하여, CPU가 리소스를 재사용하거나 삭제할 수 있도록 합니다.

Direct3D 11에서 ID3D11DeviceContext::Map 플래그 D3D11_MAP_WRITE_DISCARD 기본적으로 각 리소스를 앱이 쓸 수 있는 끝없는 메모리 공급으로 처리합니다("이름 바꾸기"라고 하는 프로세스). Direct3D 12에서는 프로세스가 명시적입니다. 추가 메모리를 할당해야 하며 작업을 동기화하기 위해 펜스를 사용해야 합니다. 링 버퍼(대형 버퍼로 구성됨)는 유용한 기법이 될 수 있습니다. 펜스 기반 리소스 관리의 링 버퍼 시나리오를 참조하세요.

링 버퍼 사용

리소스 바인딩

Direct3D 11의 뷰(셰이더 리소스 뷰, 렌더링 대상 뷰 등)는 Direct3D 12에서 주로 설명자 개념으로 대체되었습니다. 생성 메서드가 Direct3D 12에 여전히 존재하지만(예: CreateShaderResourceViewCreateRenderTargetView) 설명자 힙이 생성된 후에 호출되어 힙에 데이터를 씁니다. Direct3D 12의 바인딩은 이제 루트 서명에 기술된 설명자 핸들에 의해 처리되고 SetGraphicsRootDescriptorTable 또는 SetComputeRootDescriptorTable 메서드를 사용하여 제출됩니다.

루트 서명은 루트 서명 슬롯 번호와 설명자 테이블 간의 매핑을 자세히 설명하며, 여기서 설명자 테이블은 꼭지점 셰이더, 픽셀 셰이더 그리고 상수 버퍼, 셰이더 리소스 뷰 및 샘플러와 같은 다른 셰이더에서 사용할 수 있는 리소스에 대한 참조를 포함할 수 있습니다. 이러한 유연성은 HLSL 레지스터 공간과 Direct3D 12의 API 바인딩 공간의 연결을 끊습니다(Direct3D 11에는 이들 사이에 일대일 매핑이 있음).

이러한 시스템이 미치는 영향 중 하나는 앱이 설명자 테이블의 이름을 바꾸기 때문에 그리기 호출당 설명자를 하나만 변경하는 경우라도 개발자가 성능 비용을 이해할 수 있습니다.

Direct3D 12의 새로운 기능은 어떤 셰이더 단계에서 어떤 설명자가 공유되는지를 앱이 제어할 수 있습니다. UAV와 같은 Direct3D 11 리소스는 모든 셰이더 단계에서 공유됩니다. 특정 셰이더 단계에서 설명자를 비활성화할 수 있도록 설정하면, 비활성화된 설명자가 사용하는 레지스터를 특정 셰이더 단계에 대해 활성화된 설명자가 사용할 수 있습니다.

다음 표에는 예제 루트 서명이 나와 있습니다.

루트 매개 변수 슬롯 설명자 테이블 항목
0 VS 설명자 범위 b0-b13
1 VS 설명자 범위 t0-t127
2 VS 설명자 범위 s0-s16
3 PS 설명자 범위 b0-b13
...
14 DS 설명자 범위 s0-16
15 공유 설명자 범위 u0-u63

 

리소스 상태

Direct3D 11에서는 리소스 상태가 앱에 의해 유지되지 않고 드라이버에 의해 유지됩니다.

Direct3D 12에서는 리소스 상태를 유지하는 것이 앱의 책임입니다. 이것은 명령 목록을 기록할 때 전체 병렬 처리가 가능하도록 하기 위해서 입니다. 앱은 명령 목록(병렬로 수행할 수 있음)에 대한 기록 타임라인과 실행 타임라인을 처리해야 하며, 순차적이어야 합니다.

리소스 상태 전환은 ResourceBarrier 메서드에 의해 처리됩니다. 리소스 사용량이 변화하면 주로 앱이 드라이버에 알려야 합니다. 예를 들어 리소스가 렌더링 대상으로 사용되는 경우에는 다음 그리기 호출에서 꼭지점 셰이더에 대한 입력으로 사용됩니다. 이렇게 되면 꼭지점 셰이더를 처리하기 전에 렌더링 대상 작업을 완료하기 위해 GPU 작업을 잠깐 중단해야 할 수 있습니다.

이 시스템에서는 그래픽 파이프라인에 대한 세분화된 동기화(GPU 스톨)뿐만 아니라 캐시 플러시 및 일부 메모리 레이아웃 변경(예: 렌더링 대상 뷰에서 깊이 스텐실 뷰 압축 풀기)이 가능합니다.

이것은 전환 장벽으로 알려져 있습니다. 다른 종류의 장벽도 있습니다. Direct3D 11에서는 ID3D11DeviceContext2::TiledResourceBarrier를 통해 두 가지 다른 타일식 리소스가 동일한 실제 메모리를 사용할 수 있습니다. Direct3D 12에서는 이것을 "앨리어싱 장벽"이라고 합니다. 앨리어싱 장벽은 Direct3D 12의 타일식 리소스 및 배치된 리소스 둘 다에 사용할 수 있습니다. UAV 장벽도 있습니다. Direct3D 11에서는 모든 UAV 디스패치 및 그리기 작업을 파이프라인으로 처리하거나 병렬로 작업할 수 있더라도 직렬화해야 합니다. Direct3D 12의 경우, UAV 장벽을 추가하여 이러한 제한이 제거됩니다. UAV 장벽은 UAV 작업이 순차적으로 진행되도록 합니다. 따라서 두 번째 작업 때문에 첫 번째 작업을 완료해야 하는 경우에는 장벽을 추가하여 두 번째 작업이 대기하도록 적용합니다. UAV의 기본 작업은 작업을 최대한 빨리 진행하는 것입니다.

워크로드를 병렬 처리할 수 있으면 성능은 확실히 향상됩니다.

Swapchain

DXGI 스왑 체인은 Direct3D 11과 12 모두에서 스왑 체인의 기반이 됩니다. Direct3D 11에서는 세 가지 유형의 스왑 체인이 SEQUENTIAL, DISCARD 및 FLIP_SEQUENTIAL 몇 가지 사소한 차이점이 있습니다. Direct3D 12의 경우 FLIP_SEQUENTIAL 및 FLIP_DISCARD 두 가지 유형만 있습니다. 위에서 설명한 것처럼 IDXGIFactory4 이상을 통해 스왑 체인을 명시적으로 만들고 어댑터 열거에 동일한 인터페이스를 사용해야 합니다.

Direct3D 11에는 자동 백 버퍼 순환이 있고, 백 버퍼 0에는 렌더링 대상 뷰가 하나만 필요합니다. Direct3D 12에서는 버퍼 순환이 명시적이며, 백 버퍼마다 렌더링 대상 뷰가 있어야 합니다. IDXGISwapChain3::GetCurrentBackBufferIndex 메서드를 사용하면 렌더링 대상을 선택할 수 있습니다. 이처럼 유연성이 추가되면 병렬 처리를 강화할 수 있습니다.

참고

애플리케이션을 설정하는 방법에는 여러 가지가 있지만 일반적으로 애플리케이션에는 스왑 체인 버퍼당 하나의 ID3D12CommandAllocator가 있습니다. 이렇게 하면 GPU가 이전 프레임을 렌더링하는 동안 애플리케이션이 다음 프레임에 대한 명령 집합을 계속 빌드할 수 있습니다.

고정 함수 렌더링

Direct3D 11에는 다양한 상위 수준 작업을 간소화하는 GenerateMips(전체 밉 체인 생성) 및 DrawAuto(스트림 출력을 앱의 추가 입력 없이 셰이더 입력으로 사용)와 같은 여러 가지 방법이 있었습니다. Direct3D 12에서는 이러한 메서드를 사용할 수 없기 때문에, 앱은 작업을 수행할 셰이더를 생성하여 작업을 처리해야 합니다.

기타 정보

다음 표에는 Direct3D 11과 12에서 유사하지만 동일하지 않은 여러 가지 기능이 나와 있습니다.

Direct3D 11 Direct3D 12
ID3D11Query ID3D12QueryHeap을 사용하면 쿼리를 그룹화하여 비용을 줄일 수 있습니다.
ID3D11Predicate 이제 완전히 투명한 버퍼에 데이터를 배치하여 예측이 가능합니다. Direct3D 11 ID3D11Predicate 개체가 ID3D12Resource::Map으로 바뀌었으며, 데이터 준비가 완료될 때까지 기다리는 펜스를 사용하여 GPU 동기화 작업 및 에 ResolveQueryData대한 호출을 따라야 합니다. 예측을 참조하세요.
UAV/SO 숨겨진 카운터 앱은 SO/UAV 카운터 할당 및 관리를 담당합니다. 스트림 출력 카운터UAV 카운터를 참조하세요.
리소스 동적 MinLOD(최소 수준의 세부 정보) SRV 설명자 정적 MinLOD로 옮겨졌습니다.
Draw*Indirect/DispatchIndirect 그리기 간접 메서드가 모두 하나의 ExecuteIndirect 메서드로 병합되었습니다.
DepthStencil 형식이 인터리브됨 DepthStencil 형식은 평면입니다. 예를 들어, 깊이 24비트, 스텐실 8비트 형식은 Direct3D 11에서 24/8/24/8... 등의 형식으로 저장되지만 Direct3D 12에서는 8/8/8... 후에 24/24/24... 형식으로 저장됩니다. 각 평면은 D3D12의 자체 하위 리소스입니다(하위 리소스 참조).
ResizeTilePool 예약된 리소스는 여러 힙에 매핑될 수 있습니다. 타일 풀이 D3D11로 증가하면, 추가 힙을 D3D12에 대신 할당할 수 있습니다.

 

DirectX 고급 학습 비디오 자습서: DirectX 11에서 DirectX 12로 포팅 가이드

Direct3D 12 이해

Direct3D 11, Direct3D 10 및 Direct2D 작업