Direct3D 12 렌더링 패스
렌더링 전달 기능은 Windows 10 버전 1809(10.0; 빌드 17763) Direct3D 12 렌더링 패스의 개념을 소개합니다. 렌더링 패스는 명령 목록에 기록하는 명령의 하위 집합으로 구성됩니다.
각 렌더링 패스가 시작되고 끝나는 위치를 선언하려면 id3D12GraphicsCommandList4::BeginRenderPass 및 EndRenderPass대한 호출 내에서 해당 전달에 속하는 명령을 중첩합니다. 따라서 명령 목록에는 0개, 하나 이상의 렌더링 패스가 포함됩니다.
시나리오
렌더링 패스는 다른 기술 중에서 Tile-Based TBDR(지연 렌더링)을 기반으로 하는 경우 렌더러의 성능을 향상시킬 수 있습니다. 더 구체적으로 말하자면, 이 기술은 애플리케이션이 리소스 렌더링 순서 요구 사항 및 데이터 종속성을 더 잘 식별할 수 있도록 하여 메모리 트래픽을 오프칩 메모리로/오프 칩 메모리로 줄여 GPU 효율성을 개선하는 데 도움이 됩니다.
렌더링 패스 기능을 활용하기 위해 명시적으로 작성된 디스플레이 드라이버가 최상의 결과를 제공합니다. 그러나 렌더링 패스 API는 기존 드라이버에서도 실행할 수 있습니다(성능 향상이 반드시 있는 것은 아님).
렌더링 패스가 값을 제공하도록 설계된 시나리오입니다.
애플리케이션이 TBDR(Tile-Based 지연 렌더링) 아키텍처에서 주 메모리에서 리소스의 불필요한 로드/저장소를 방지하도록 허용
렌더링 패스의 가치 제안 중 하나는 렌더링 작업 집합에 대한 애플리케이션의 데이터 종속성을 나타내는 중앙 위치를 제공한다는 것입니다. 이러한 데이터 종속성을 통해 디스플레이 드라이버는 바인딩/장벽 시간에 이 데이터를 검사하고 주 메모리에서/주 메모리로의 리소스 로드/저장소를 최소화하는 지침을 발행할 수 있습니다.
TBDR 아키텍처가 렌더링 패스에서 온칩 캐시의 리소스를 기회적으로 영구적으로 유지하도록 허용합니다(별도의 명령 목록에서도).
메모
특히 이 시나리오는 여러 명령 목록에서 동일한 렌더링 대상에 쓰는 경우로 제한됩니다.
일반적인 렌더링 패턴은 렌더링 명령이 병렬로 생성되더라도 애플리케이션이 여러 명령 목록에서 동일한 렌더링 대상으로 직렬로 렌더링하는 것입니다. 이 시나리오에서 렌더링 패스를 사용하면 디스플레이 드라이버가 명령 목록 경계의 주 메모리에 플러시를 방지할 수 있도록 이러한 패스를 이러한 방식으로 결합할 수 있습니다(애플리케이션이 즉시 성공하는 명령 목록에서 렌더링을 다시 시작할 것임을 알고 있기 때문에).
애플리케이션의 책임
렌더링 패스 기능이 있더라도 Direct3D 12 런타임이나 디스플레이 드라이버는 부하 및 저장소의 순서를 변경/방지할 수 있는 기회를 추론할 책임이 없습니다. 렌더링 패스 기능을 올바르게 활용하려면 애플리케이션에 이러한 책임이 있습니다.
- 작업에 대한 데이터/순서 지정 종속성을 올바르게 식별합니다.
- 플러시를 최소화하는 방식으로 제출 순서를 지정합니다(따라서 _PRESERVE 플래그 사용을 최소화).
- 리소스 장벽을 올바르게 사용하고 리소스 상태를 추적합니다.
- 불필요한 복사본/지우기 방지 이러한 항목을 식별하기 위해 windows 도구 PIX에서 자동화된 성능 경고를 사용할 수 있습니다.
렌더링 패스 기능 사용
렌더링 패스무엇인가요?
렌더링 패스는 이러한 요소에 의해 정의됩니다.
- 렌더링 패스 기간 동안 고정된 출력 바인딩 집합입니다. 이러한 바인딩은 하나 이상의 RTV(렌더링 대상 뷰) 및/또는 DSV(깊이 스텐실 뷰)에 대한 것입니다.
- 출력 바인딩 집합을 대상으로 하는 GPU 작업 목록입니다.
- 렌더링 패스가 대상으로 하는 모든 출력 바인딩에 대한 부하/저장소 종속성을 설명하는 메타데이터입니다.
출력 바인딩 선언
렌더링 패스가 시작될 때 렌더링 대상 및/또는 깊이/스텐실 버퍼에 대한 바인딩을 선언합니다. 렌더링 대상에 바인딩하는 것은 선택 사항이며 깊이/스텐실 버퍼에 바인딩하는 것은 선택 사항입니다. 그러나 둘 중 하나 이상에 바인딩해야 하며 아래 코드 예제에서는 둘 다에 바인딩합니다.
ID3D12GraphicsCommandList4::BeginRenderPass호출에서 이러한 바인딩을 선언합니다.
void render_passes(::ID3D12GraphicsCommandList4 * pIGCL4,
D3D12_CPU_DESCRIPTOR_HANDLE const& rtvCPUDescriptorHandle,
D3D12_CPU_DESCRIPTOR_HANDLE const& dsvCPUDescriptorHandle)
{
const float clearColor4[]{ 0.f, 0.f, 0.f, 0.f };
CD3DX12_CLEAR_VALUE clearValue{ DXGI_FORMAT_R32G32B32_FLOAT, clearColor4 };
D3D12_RENDER_PASS_BEGINNING_ACCESS renderPassBeginningAccessClear{ D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR, { clearValue } };
D3D12_RENDER_PASS_ENDING_ACCESS renderPassEndingAccessPreserve{ D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE, {} };
D3D12_RENDER_PASS_RENDER_TARGET_DESC renderPassRenderTargetDesc{ rtvCPUDescriptorHandle, renderPassBeginningAccessClear, renderPassEndingAccessPreserve };
D3D12_RENDER_PASS_BEGINNING_ACCESS renderPassBeginningAccessNoAccess{ D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, {} };
D3D12_RENDER_PASS_ENDING_ACCESS renderPassEndingAccessNoAccess{ D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS, {} };
D3D12_RENDER_PASS_DEPTH_STENCIL_DESC renderPassDepthStencilDesc{ dsvCPUDescriptorHandle, renderPassBeginningAccessNoAccess, renderPassBeginningAccessNoAccess, renderPassEndingAccessNoAccess, renderPassEndingAccessNoAccess };
pIGCL4->BeginRenderPass(1, &renderPassRenderTargetDesc, &renderPassDepthStencilDesc, D3D12_RENDER_PASS_FLAG_NONE);
// Record command list.
pIGCL4->EndRenderPass();
// Begin/End further render passes and then execute the command list(s).
}
D3D12_RENDER_PASS_RENDER_TARGET_DESC 구조체의 첫 번째 필드를 하나 이상의 RTV(렌더링 대상 뷰)에 해당하는 CPU 설명자 핸들로 설정합니다. 마찬가지로 D3D12_RENDER_PASS_DEPTH_STENCIL_DESC 깊이 스텐실 뷰(DSV)에 해당하는 CPU 설명자 핸들을 포함합니다. 이러한 CPU 설명자 핸들은 ID3D12GraphicsCommandList::OMSetRenderTargets전달한 것과 동일합니다. OMSetRenderTargets마찬가지로 CPU 설명자는 beginRenderPass 호출 시 해당(CPU 설명자) 힙에서 스냅됩니다.
RTV 및 DSV는 렌더링 패스에 상속되지 않습니다. 오히려 설정해야 합니다. BeginRenderPass에서 선언된 RTV 및 DSV도 명령 목록으로 전파되지. 대신 렌더링 패스 다음에 정의되지 않은 상태입니다.
렌더링 패스 및 워크로드
렌더링 패스를 중첩할 수 없으며 둘 이상의 명령 목록에 걸쳐 있는 렌더링 패스를 사용할 수 없습니다(단일 명령 목록에 기록하는 동안 시작 및 종료해야 합니다). 효율적인 다중 스레드 렌더링 패스 생성을 사용하도록 설계된 최적화는 아래의 렌더링 패스 플래그 섹션에서 설명합니다.
렌더링 패스 내에서 수행하는 쓰기는 후속 렌더링 통과까지 읽을 수 있는 유효한 없습니다. 따라서 렌더링 패스 내에서 일부 유형의 장벽(예: 현재 바인딩된 렌더링 대상에서 RENDER_TARGETSHADER_RESOURCE 장벽)이 배제됩니다. 자세한 내용은 아래의 렌더링 패스 및 리소스 장벽 섹션을 참조하세요.
방금 언급한 쓰기-읽기 제약 조건의 한 가지 예외에는 깊이 테스트 및 렌더링 대상 혼합의 일부로 발생하는 암시적 읽기가 포함됩니다. 따라서 이러한 API는 렌더링 패스 내에서 허용되지 않습니다(코어 런타임은 기록 중에 호출되는 경우 명령 목록을 제거함).
- ID3D12GraphicsCommandList1::AtomicCopyBufferUINT
- ID3D12GraphicsCommandList1::AtomicCopyBufferUINT64
- ID3D12GraphicsCommandList4::BeginRenderPass
- ID3D12GraphicsCommandList::ClearDepthStencilView
- ID3D12GraphicsCommandList::ClearRenderTargetView
- ID3D12GraphicsCommandList::ClearState
- ID3D12GraphicsCommandList::ClearUnorderedAccessViewFloat
- ID3D12GraphicsCommandList::ClearUnorderedAccessViewUint
- ID3D12GraphicsCommandList::CopyBufferRegion
- ID3D12GraphicsCommandList::CopyResource
- ID3D12GraphicsCommandList::CopyTextureRegion
- ID3D12GraphicsCommandList::CopyTiles
- ID3D12GraphicsCommandList::D iscardResource
- ID3D12GraphicsCommandList::D디스패치
- ID3D12GraphicsCommandList::OMSetRenderTargets
- ID3D12GraphicsCommandList::ResolveQueryData
- ID3D12GraphicsCommandList::ResolveSubresource
- ID3D12GraphicsCommandList1::ResolveSubresourceRegion
- ID3D12GraphicsCommandList3::SetProtectedResourceSession
렌더링 패스 및 리소스 장벽
동일한 렌더링 패스 내에서 발생한 쓰기를 읽거나 사용할 수 없습니다. 특정 장벽은 이 제약 조건을 준수하지 않습니다(예: 현재 바인딩된 렌더링 대상에서 D3D12_RESOURCE_STATE_RENDER_TARGET *_SHADER_RESOURCE ). 디버그 계층은 해당 효과에 오류가 발생합니다. 그러나 현재 렌더링 패스가 현재 렌더링 패스 시작 전에 완료되기 때문에 외부에서 작성된 렌더링 대상에 대한 동일한 장벽이 준수됩니다. 이와 관련하여 디스플레이 드라이버가 수행할 수 있는 특정 최적화를 알면 도움이 될 수 있습니다. 준수 워크로드가 있는 경우 디스플레이 드라이버는 렌더링 패스에서 발생하는 모든 장벽을 렌더링 패스의 시작 부분으로 이동할 수 있습니다. 그곳에서 병합할 수 있으며 타일링/범주화 작업을 방해하지 않습니다. 현재 렌더링 패스가 시작되기 전에 모든 쓰기가 완료된 경우 유효한 최적화입니다.
다음은 사전 Direct3D 12 스타일 리소스 바인딩 디자인이 있는 렌더링 엔진이 있다고 가정하는 보다 완전한 드라이버 최적화 예제입니다. 리소스 바인딩 방법에 따라 요청 시 장벽을 수행합니다. 프레임 끝(다음 프레임에서 사용)을 향해 정렬되지 않은 액세스 뷰(UAV)에 쓸 때 엔진은 프레임이 끝날 때 리소스를 D3D12_RESOURCE_STATE_UNORDERED_ACCESS 상태로 둘 수 있습니다. 다음 프레임에서 엔진이 리소스를 SRV(셰이더 리소스 뷰)로 바인딩할 때 리소스가 올바른 상태가 아니라는 것을 알게 되며 D3D12_RESOURCE_STATE_UNORDERED_ACCESSD3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE장벽을 발생합니다. 렌더링 패스 내에서 해당 장벽이 발생하면 모든 쓰기가 이 현재 렌더링 패스의 외부에서 이미 발생했다고 가정하면 디스플레이 드라이버가 렌더링 패스의 시작 부분까지 장벽 이동할 있습니다. 코드가 이 섹션과 마지막 섹션에 설명된 쓰기-읽기 제약 조건을 준수하는 한 이는 유효합니다.
다음은 준수 장벽의 예입니다.
- D3D12_RESOURCE_STATE_INDIRECT_ARGUMENTD3D12_RESOURCE_STATE_UNORDERED_ACCESS.
- *_SHADER_RESOURCE D3D12_RESOURCE_STATE_COPY_DEST.
그리고 이들은 비준수 장벽의 예입니다.
- 현재 바인딩된 RTV/DSV의 모든 읽기 상태로 D3D12_RESOURCE_STATE_RENDER_TARGET.
- 현재 바인딩된 RTV/DSV의 모든 읽기 상태로 D3D12_RESOURCE_STATE_DEPTH_WRITE.
- 모든 별칭 장벽.
- UAV(순서가 지정되지 않은 액세스 뷰) 장벽입니다.
리소스 액세스 선언
BeginRenderPass 시간 내에 RTV 및/또는 DSV 역할을 하는 모든 리소스를 선언할 뿐만 아니라 시작 및 종료 액세스 특성을 지정해야 합니다. 위의 출력 바인딩 선언 섹션의 코드 예제에서 볼 수 있듯이 D3D12_RENDER_PASS_RENDER_TARGET_DESC 및 D3D12_RENDER_PASS_DEPTH_STENCIL_DESC 구조로 이 작업을 수행합니다.
자세한 내용은 D3D12_RENDER_PASS_BEGINNING_ACCESS 및 D3D12_RENDER_PASS_ENDING_ACCESS 구조, D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE 및 D3D12_RENDER_PASS_ENDING_ACCESS_TYPE 열거형을 참조하세요.
렌더링 패스 플래그
BeginRenderPass 전달된 마지막 매개 변수는 렌더링 패스 플래그(D3D12_RENDER_PASS_FLAGS 열거형의 값)입니다.
enum D3D12_RENDER_PASS_FLAGS
{
D3D12_RENDER_PASS_FLAG_NONE = 0,
D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES = 0x1,
D3D12_RENDER_PASS_FLAG_SUSPENDING_PASS = 0x2,
D3D12_RENDER_PASS_FLAG_RESUMING_PASS = 0x4
};
렌더링 패스 내에서 UAV 쓰기
UAV(순서가 지정되지 않은 액세스 뷰) 쓰기는 렌더링 패스 내에서 허용되지만, 필요한 경우 디스플레이 드라이버가 타일링을 옵트아웃할 수 있도록 D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES지정하여 렌더링 패스 내에서 UAV 쓰기를 실행함을 구체적으로 나타내야 합니다.
UAV 액세스는 위에서 설명한 쓰기-읽기 제약 조건을 따라야 합니다(렌더링 패스의 쓰기는 후속 렌더링 통과까지 읽을 수 없음). UAV 장벽은 렌더링 패스 내에서 허용되지 않습니다.
UAV 바인딩(루트 테이블 또는 루트 설명자를 통해)은 렌더링 패스로 상속되고 렌더링 패스에서 전파됩니다.
일시 중단 패스 및 재시도 패스
전체 렌더링 패스를 suspending-pass 및/또는 resuming-pass로 나타낼 수 있습니다. 일시 중단 후 다시 시작 쌍은 패스 간에 동일한 보기/액세스 플래그가 있어야 하며 일시 중단 렌더링 패스와 다시 시작 렌더링 패스 간에 중간 GPU 작업(예: 그리기, 디스패치, 삭제, 지우기, 복사, 업데이트-타일 매핑, 쓰기 버퍼-직접 실행, 쿼리, 쿼리 확인)이 없을 수 있습니다.
의도된 사용 사례는 다중 스레드 렌더링으로, 네 개의 명령 목록(각각 자체 렌더링 패스가 있는)이 동일한 렌더링 대상을 대상으로 지정할 수 있습니다. 명령 목록에서 렌더링 패스가 일시 중단/다시 시작되면 ID3D12CommandQueue::ExecuteCommandLists동일한 호출에서 명령 목록을 실행해야 합니다.
렌더링 패스는 다시 시작 및 일시 중단이 될 수 있습니다. 방금 지정된 다중 스레드 예제에서 명령 목록 2와 3은 각각 1과 2에서 다시 시작됩니다. 그리고 동시에 2와 3은 각각 3과 4로 일시 중단됩니다.
렌더링 패스 기능 지원에 대한 쿼리
ID3D12Device::CheckFeatureSupport 호출하여 디바이스 드라이버 및/또는 하드웨어가 렌더링 패스를 효율적으로 지원하는 정도를 쿼리할 수 있습니다.
D3D12_RENDER_PASS_TIER get_render_passes_tier(::ID3D12Device * pIDevice)
{
D3D12_FEATURE_DATA_D3D12_OPTIONS5 featureSupport{};
winrt::check_hresult(
pIDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &featureSupport, sizeof(featureSupport))
);
return featureSupport.RenderPassesTier;
}
...
D3D12_RENDER_PASS_TIER renderPassesTier{ get_render_passes_tier(pIDevice) };
런타임의 매핑 논리로 인해 렌더링은 항상 함수를 전달합니다. 그러나 기능 지원에 따라 항상 혜택을 제공하는 것은 아닙니다. 위의 코드 예제와 유사한 코드를 사용하여 렌더링 패스로 명령을 실행할 가치가 있는지 여부와 확실히 이점이 아닌 경우(즉, 런타임이 기존 API 화면에 매핑되는 경우)를 결정할 수 있습니다. D3D11On12 )사용하는 경우 이 검사를 수행하는 것이 특히 중요합니다.
세 가지 지원 계층에 대한 설명은 D3D12_RENDER_PASS_TIER 열거형을 참조하세요.