Direct3D 12 interop
D3D12를 사용하여 구성 요소화된 애플리케이션을 작성할 수 있습니다.
Interop 개요
D3D12는 매우 강력할 수 있으며 애플리케이션이 콘솔과 같은 효율성으로 그래픽 코드를 작성할 수 있지만 모든 애플리케이션이 휠을 재창조하고 렌더링 엔진 전체를 처음부터 작성할 필요는 없습니다. 경우에 따라 다른 구성 요소 또는 라이브러리가 이미 더 잘 수행되었거나 코드 일부의 성능이 정확성 및 가독성만큼 중요하지 않은 경우도 있습니다.
이 섹션에서는 다음과 같은 interop 기술에 대해 설명합니다.
- 동일한 디바이스의 D3D12 및 D3D12
- 다른 디바이스의 D3D12 및 D3D12
- 동일한 디바이스에서 D3D12 및 D3D11, D3D10 또는 D2D의 조합
- 다른 디바이스에서 D3D12 및 D3D11, D3D10 또는 D2D의 조합
- D3D12 및 GDI 또는 D3D12 및 D3D11 및 GDI
interop을 사용하는 이유
애플리케이션에서 다른 API와 D3D12 interop을 원하는 몇 가지 이유가 있습니다. 몇 가지 예는 다음과 같습니다.
- 증분 포팅: 전체 애플리케이션을 D3D10 또는 D3D11에서 D3D12로 포팅하는 동시에 포팅 프로세스의 중간 단계에서 작동합니다(테스트 및 디버깅을 사용하도록 설정).
- 블랙박스 코드: 나머지 코드를 포팅하는 동안 애플리케이션의 특정 부분을 있는 그대로 두려고 합니다. 예를 들어 게임의 UI 요소를 포트할 필요가 없을 수 있습니다.
- 변경되지 않는 구성 요소: 대상 D3D12에 기록되지 않은 애플리케이션이 소유하지 않은 구성 요소를 사용해야 합니다.
- 새 구성 요소: 전체 애플리케이션을 포팅하지 않고 D3D12를 사용하여 작성된 새 구성 요소를 사용하려고 합니다.
D3D12에는 interop에 대한 네 가지 기본 기술이 있습니다.
- 앱은 이미 바인딩된 렌더링 대상에 몇 가지 추가 렌더링 명령을 기록하는 열려 있는 명령 목록을 구성 요소에 제공하도록 선택할 수 있습니다. 이는 준비된 디바이스 컨텍스트를 D3D11의 다른 구성 요소에 제공하는 것과 동일하며 이미 바인딩된 백 버퍼에 UI/텍스트를 추가하는 것과 같은 작업에 적합합니다.
- 앱은 원하는 대상 리소스와 함께 구성 요소에 명령 큐를 제공하도록 선택할 수 있습니다. 이는 D3D11에서 ClearState 또는 DeviceContextState API를 사용하여 클린 디바이스 컨텍스트를 다른 구성 요소에 제공하는 것과 같습니다. D2D와 같은 구성 요소가 작동하는 방식입니다.
- 구성 요소는 잠재적으로 병렬로 명령 목록을 생성하는 모델을 선택할 수 있으며, 앱은 나중에 제출을 담당합니다. 구성 요소 경계를 넘어 하나 이상의 리소스를 제공해야 합니다. D3D12의 성능이 더 바람직하지만 지연된 컨텍스트를 사용하여 D3D11에서 동일한 기술을 사용할 수 있습니다.
- 각 구성 요소에는 자체 큐 및/또는 디바이스가 있으며 앱 및 구성 요소는 구성 요소 경계 간에 리소스 및 동기화 정보를 공유해야 합니다. 이는 레거시
ISurfaceQueue
및 최신 IDXGIKeyedMutex와 유사합니다.
이러한 시나리오 간의 차이점은 구성 요소 경계 간에 정확히 공유되는 것입니다. 디바이스는 공유된 것으로 가정되지만 기본적으로 상태 비 상태이므로 실제로 관련이 없습니다. 키 개체는 명령 목록, 명령 큐, 동기화 개체 및 리소스입니다. 이들 각각은 그(것)들을 공유할 때 그들의 자신의 합병증이 있습니다.
명령 목록 공유
가장 간단한 interop 메서드를 사용하려면 엔진의 일부와 명령 목록만 공유해야 합니다. 렌더링 작업이 완료되면 명령 목록 소유권이 호출자에게 돌아갑니다. 명령 목록의 소유권은 스택을 통해 추적할 수 있습니다. 명령 목록은 단일 스레드이므로 앱이 이 기술을 사용하여 독특하거나 혁신적인 작업을 수행할 수 있는 방법은 없습니다.
명령 큐 공유
동일한 프로세스에서 디바이스를 공유하는 여러 구성 요소에 대한 가장 일반적인 기술일 것입니다.
명령 큐가 공유 단위인 경우 모든 미해결 명령 목록을 명령 큐에 즉시 제출해야 하며 내부 명령 큐를 동기화해야 한다는 것을 알리기 위해 구성 요소에 대한 호출이 있어야 합니다. 이는 D3D11 Flush API와 동일하며 애플리케이션이 자체 명령 목록을 제출하거나 기본 형식을 동기화할 수 있는 유일한 방법입니다.
동기화 기본 형식 공유
자체 디바이스 및/또는 명령 큐에서 작동하는 구성 요소의 예상 패턴은 작업을 시작할 때 ID3D12Fence 또는 공유 핸들을 수락하고, 작업을 시작할 때 UINT64 쌍을 수락한 다음, 두 번째 ID3D12Fence 또는 공유 핸들 및 모든 작업이 완료되면 신호를 표시하는 UINT64 쌍을 수락하는 것입니다. 이 패턴은 IDXGIKeyedMutex 및 DWM/DXGI 대칭 이동 모델 동기화 디자인의 현재 구현과 일치합니다.
리소스 공유
지금까지 여러 구성 요소를 활용하는 D3D12 앱을 작성하는 가장 복잡한 부분은 구성 요소 경계를 넘어 공유되는 리소스를 처리하는 방법입니다. 이는 주로 리소스 상태의 개념 때문입니다. 리소스 상태 디자인의 일부 측면은 명령 목록 내 동기화를 처리하기 위한 것이지만, 다른 측면은 명령 목록 간에 영향을 미치며 리소스 레이아웃과 리소스 데이터 액세스의 유효한 작업 집합 또는 성능 특성에 영향을 줍니다.
이 합병증을 다루는 두 가지 패턴이 있으며, 둘 다 본질적으로 구성 요소 간의 계약을 포함합니다.
- 계약은 구성 요소 개발자가 정의하고 문서화할 수 있습니다. 이는 "작업이 시작될 때 리소스가 기본 상태에 있어야 하며 작업이 완료되면 기본 상태로 다시 배치될 것"처럼 간단하거나 중간 깊이 확인을 강제하지 않고 깊이 버퍼를 공유하는 것과 같은 작업을 허용하는 더 복잡한 규칙이 있을 수 있습니다.
- 리소스가 구성 요소 경계를 넘어 공유될 때 런타임에 애플리케이션에서 계약을 정의할 수 있습니다. 동일한 두 가지 정보로 구성됩니다. 즉, 구성 요소가 이 정보를 사용하기 시작할 때 리소스가 있는 상태와 구성 요소가 완료될 때 리소스를 그대로 두어야 하는 상태입니다.
interop 모델 선택
대부분의 D3D12 애플리케이션에서 명령 큐를 공유하는 것이 이상적인 모델일 것입니다. 중복 큐에서 추가 메모리 오버헤드 없이, GPU 동기화 기본 형식을 처리할 때의 성능 영향 없이 작업 만들기 및 제출에 대한 완전한 소유권을 허용합니다.
구성 요소가 형식 또는 우선 순위와 같은 다른 큐 속성을 처리해야 하거나 공유가 프로세스 경계를 넘어야 하는 경우 동기화 기본 형식을 공유해야 합니다.
명령 목록 공유 또는 생성은 타사 구성 요소에서 외부에서 널리 사용되지 않지만 게임 엔진 내부 구성 요소에서 널리 사용될 수 있습니다.
Interop API
12의 Direct3D 11 항목에서는 이 항목에 설명된 상호 운용의 종류와 관련된 많은 API 표면의 사용을 안내합니다.
Windows 그래픽 API 간에 표면을 공유하는 데 사용할 수 있는 ID3D12Device::CreateSharedHandle 메서드도 참조하세요.