DXGI(DirectX Graphics Infrastructure): 모범 사례
Microsoft DXGI(DirectX Graphics Infrastructure)는 Direct3D 10, 10.1, 11 및 11.1에 필요한 하위 수준 작업 중 일부를 캡슐화하는 Windows Vista에서 도입된 새로운 하위 시스템입니다. Direct3D 9 프로그래머의 관점에서 DXGI는 이전에 Direct3D 9 API로 압축된 열거형, 스왑 체인 만들기 및 프레젠테이션을 위한 대부분의 코드를 포함합니다. 앱을 DXGI 및 Direct3D 10.x 및 Direct3D 11.x로 이식하는 경우 프로세스가 원활하게 실행되도록 몇 가지 사항을 고려해야 합니다.
이 문서에서는 주요 포팅 문제에 대해 설명합니다.
Full-Screen 문제
Direct3D 9에서 DXGI로, Direct3D 10.x 또는 Direct3D 11.x로 포팅할 때 창에서 전체 화면 모드로 이동하는 것과 관련된 문제는 개발자에게 골칫거리가 될 수 있습니다. DXGI 애플리케이션과 달리 Direct3D 9 애플리케이션은 창 스타일 및 창 상태를 추적하는 보다 실습 접근 방식이 필요하기 때문에 기본 문제가 발생합니다. 모드 변경 코드가 DXGI에서 실행되도록 이식되면 종종 예기치 않은 동작이 발생합니다.
종종 Direct3D 9 애플리케이션은 전면 버퍼의 해상도를 설정하고 디바이스를 전체 화면 전용 모드로 강제 적용한 다음 백 버퍼 해상도를 일치하도록 설정하여 전환을 전체 화면 모드로 처리했습니다. 애플리케이션이 WM_SIZE 메시지를 받을 때마다 창 프로세스에서 관리해야 했기 때문에 창 크기를 변경하는 데 별도의 경로가 사용되었습니다.
DXGI는 두 가지 경우를 결합하여 이 접근 방식을 단순화하려고 합니다. 예를 들어 창 테두리를 창 모드로 끌면 애플리케이션은 WM_SIZE 메시지를 받습니다. DXGI는 이 메시지를 가로채서 프런트 버퍼의 크기를 자동으로 조정합니다. 애플리케이션에서 수행해야 하는 작업은 IDXGISwapChain::ResizeBuffers 를 호출하여 백 버퍼의 크기를 WM_SIZE 매개 변수로 전달된 크기로 조정하는 것입니다. 마찬가지로 애플리케이션이 전체 화면 모드와 창 모드 간에 전환해야 하는 경우 애플리케이션은 IDXGISwapChain::SetFullscreenState를 호출하기만 하면 됩니다. DXGI는 새로 선택한 전체 화면 모드와 일치하도록 전면 버퍼의 크기를 조정하고 애플리케이션에 WM_SIZE 메시지를 보냅니다. 애플리케이션은 창 테두리를 끌 때와 마찬가지로 ResizeBuffers를 다시 호출합니다.
이전 설명의 방법론은 매우 특정한 경로를 따릅니다. DXGI는 기본적으로 전체 화면 해상도를 데스크톱 해상도로 설정합니다. 그러나 대부분의 애플리케이션은 기본 전체 화면 해상도로 전환합니다. 이러한 경우 DXGI는 IDXGISwapChain::ResizeTarget을 제공합니다. SetFullscreenState를 호출하기 전에 호출해야 합니다. 이러한 메서드를 반대 순서로 호출할 수 있지만(SetFullscreenState 먼저, ResizeTarget 뒤에) 이렇게 하면 추가 WM_SIZE 메시지가 애플리케이션으로 전송됩니다. (이렇게 하면 DXGI가 두 가지 모드 변경을 강제로 수행할 수 있으므로 깜박일 수도 있습니다.) SetFullscreenState를 호출한 후에는 DXGI_MODE_DESC RefreshRate 멤버가 0으로 설정된 상태에서 ResizeTarget을 다시 호출하는 것이 좋습니다. 이는 DXGI의 작업 없음 명령에 해당하지만 다음에 설명된 새로 고침 속도 문제를 방지할 수 있습니다.
전체 화면 모드에서 DWM(데스크톱 창 관리자)을 사용할 수 없습니다. DXGI는 블릿을 수행하는 대신 백 버퍼 콘텐츠를 표시하기 위해 대칭 이동 작업을 수행할 수 있습니다. 이는 창 모드에서 수행됩니다. 그러나 특정 요구 사항이 충족되지 않으면 이 성능 향상을 취소할 수 있습니다. DXGI가 블릿 대신 대칭 이동하도록 하려면 전면 버퍼와 백 버퍼의 크기를 동일하게 조정해야 합니다. 애플리케이션이 WM_SIZE 메시지를 올바르게 처리하는 경우 문제가 되지 않습니다. 또한 형식은 동일해야 합니다.
대부분의 애플리케이션에 대한 문제는 새로 고침 속도입니다. ResizeTarget 호출에 지정된 새로 고침 속도는 스왑 체인이 사용하는 IDXGIOutput 개체에 의해 열거되는 새로 고침 속도여야 합니다. 애플리케이션이 ResizeTarget에 전달되는 DXGI_MODE_DESCRefreshRate 멤버를 0으로 설정하면 DXGI에서 이 값을 자동으로 계산할 수 있습니다. 특정 새로 고침 속도가 항상 지원된다고 가정하지 말고 단순히 값을 하드 코딩하는 것이 중요합니다. 종종 개발자는 모니터에서 열거된 새로 고침 속도가 모니터에서 약 60,000/1,001Hz라는 것을 모르고 새로 고침 속도로 60Hz를 선택합니다. 새로 고침 속도가 예상 새로 고침 속도 60과 일치하지 않으면 DXGI는 대칭 이동 대신 전체 화면 모드에서 블릿을 수행해야 합니다.
개발자가 자주 직면하는 마지막 문제는 전체 화면 모드에 남아 있는 동안 전체 화면 해상도를 변경하는 방법입니다. ResizeTarget 및 SetFullscreenState를 호출하면 성공하는 경우도 있지만 전체 화면 해상도는 데스크톱 해상도로 유지됩니다. 또한 개발자는 전체 화면 스왑 체인을 만들고 특정 해상도를 제공할 수 있습니다. 단지 DXGI가 전달된 숫자에 관계없이 데스크톱 해상도를 기본값으로 설정한다는 것을 알 수 있습니다. 달리 지시하지 않는 한 DXGI는 기본적으로 전체 화면 스왑 체인에 대한 데스크톱 해상도로 설정됩니다. 전체 화면 스왑 체인을 만들 때 DXGI의 기본 동작을 재정의하려면 DXGI_SWAP_CHAIN_DESC 구조의 Flags 멤버를 DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH 설정해야 합니다. 이 플래그를 ResizeTarget 에 전달하여 이 기능을 동적으로 사용하거나 사용하지 않도록 설정할 수도 있습니다.
여러 모니터
여러 모니터에서 DXGI를 사용하는 경우 따라야 할 두 가지 규칙이 있습니다.
첫 번째 규칙은 여러 모니터에서 두 개 이상의 전체 화면 스왑 체인을 만드는 데 적용됩니다. 이러한 스왑 체인을 만들 때 모든 스왑 체인을 창으로 만든 다음 전체 화면으로 설정하는 것이 가장 좋습니다. 스왑 체인이 전체 화면 모드로 만들어지면 두 번째 스왑 체인을 만들면 모드 변경이 첫 번째 스왑 체인으로 전송되어 전체 화면 모드가 종료될 수 있습니다.
두 번째 규칙은 출력에 적용됩니다. 스왑 체인을 만들 때 사용되는 출력을 주의해야 합니다. DXGI를 사용하면 전체 화면이 될 때 스왑 체인을 모니터링하는 IDXGIOutput 개체가 사용됩니다. DXGI와 달리 Direct3D 9에는 출력 개념이 없습니다.
창 스타일 및 DXGI
Direct3D 9 애플리케이션은 전체 화면 모드와 창 모드 간에 전환할 때 수행해야 할 작업이 많았습니다. 이 작업의 대부분은 테두리를 추가 및 제거하고 스크롤 막대를 추가하도록 창 스타일을 변경하는 것과 관련이 있습니다. 애플리케이션이 DXGI 및 Direct3D 10.x 또는 Direct3D 11.x로 이식되는 경우 이 코드는 종종 제자리에 남아 있습니다. 변경 내용에 따라 모드 간에 전환하면 예기치 않은 동작이 발생할 수 있습니다. 예를 들어 창 모드로 전환할 때 이러한 스타일을 구체적으로 설정하는 코드가 있음에도 불구하고 애플리케이션에 더 이상 창 프레임 또는 창 테두리가 없을 수 있습니다. 이 문제는 이제 DXGI가 이 스타일 변경의 대부분을 자체적으로 처리하기 때문에 발생합니다. 창 스타일의 수동 설정은 DXGI를 방해할 수 있으며 이로 인해 예기치 않은 동작이 발생할 수 있습니다.
권장 동작은 가능한 한 적은 작업을 수행하고 DXGI가 창과의 대부분의 상호 작용을 처리하도록 하는 것입니다. 그러나 애플리케이션이 자체 창 동작을 처리해야 하는 경우 IDXGIFactory::MakeWindowAssociation 을 사용하여 DXGI에 자동 창 처리 중 일부를 사용하지 않도록 설정할 수 있습니다.
다중 스레딩 및 DXGI
교착 상태가 발생하지 않도록 다중 스레드 애플리케이션에서 DXGI를 사용하는 경우 특별한 주의를 기울여야 합니다. DXGI가 창과 긴밀하게 상호 작용하기 때문에 때때로 연결된 애플리케이션 창에 창 메시지를 보냅니다. DXGI는 계속하기 전에 창 변경이 발생해야 하므로 동기 호출인 SendMessage를 사용합니다. SendMessage가 반환되기 전에 애플리케이션에서 창 메시지를 처리해야 합니다.
DXGI 호출 및 메시지 펌프가 동일한 스레드(또는 단일 스레드 애플리케이션)에 있는 애플리케이션에서는 수행할 작업이 거의 필요하지 않습니다. DXGI 호출이 메시지 펌프와 동일한 스레드에 있는 경우 SendMessage 는 창의 WindowProc을 호출합니다. 이렇게 하면 메시지 펌프가 무시되고 SendMessage를 호출한 후에도 실행을 계속할 수 있습니다. IDXGISwapChain::P resent와 같은 IDXGISwapChain 호출도 DXGI 호출로 간주됩니다. DXGI는 Present가 호출될 때까지 ResizeBuffers 또는 ResizeTarget에서 작업을 연기할 수 있습니다.
DXGI 호출 및 메시지 펌프가 서로 다른 스레드에 있는 경우 교착 상태를 방지하려면 주의해야 합니다. 메시지 펌프와 SendMessage가 서로 다른 스레드에 있는 경우 SendMessage 는 창의 메시지 큐에 메시지를 추가하고 창이 해당 메시지를 처리할 때까지 기다립니다. 창 프로시저가 사용 중이거나 메시지 펌프에서 호출되지 않은 경우 메시지가 처리되지 않을 수 있으며 DXGI는 무기한 대기합니다.
예를 들어 한 스레드에 메시지 펌프가 있고 다른 스레드에서 렌더링되는 애플리케이션의 경우 모드를 변경할 수 있습니다. 메시지 펌프 스레드는 렌더링 스레드에 모드를 변경하도록 지시하고 모드 변경이 완료될 때까지 기다립니다. 그러나 렌더링 스레드는 DXGI 함수를 호출하며, 이 함수는 메시지 펌프가 메시지를 처리할 때까지 차단하는 SendMessage를 호출합니다. 이제 두 스레드가 모두 차단되고 서로 대기 중이므로 교착 상태가 발생합니다. 이를 방지하려면 메시지 펌프를 차단하지 마세요. 블록이 피할 수 없는 경우 모든 DXGI 상호 작용은 메시지 펌프와 동일한 스레드에서 발생해야 합니다.
감마 및 DXGI
감마는 SRGB 텍스처를 사용하여 Direct3D 10.x 또는 Direct3D 11.x에서 가장 잘 처리할 수 있지만 감마 램프는 2.2와 다른 감마 값을 원하거나 SRGB를 지원하지 않는 렌더링 대상 형식을 사용하는 개발자에게 여전히 유용할 수 있습니다. DXGI를 통해 감마 램프를 설정할 때 발생하는 두 가지 문제에 유의하세요. 첫 번째 문제는 IDXGIOutput::SetGammaControl 에 전달된 램프 값이 WORD 값이 아닌 float 값이라는 것입니다. 또한 Direct3D 9에서 이식된 코드가 SETGammaControl에 전달하기 전에 WORD 값으로 변환하지 않는지 확인합니다.
두 번째 문제는 전체 화면 모드로 변경한 후 사용 중인 IDXGIOutput 개체에 따라 SetGammaControl이 작동하지 않을 수 있다는 것입니다. 전체 화면 모드로 변경하는 경우 DXGI는 새 출력 개체를 만들고 출력에 대한 모든 후속 작업에 개체를 사용합니다. 전체 화면 모드 스위치 전에 열거된 출력에서 SetGammaControl 을 호출하는 경우 호출은 DXGI가 현재 사용 중인 출력으로 전달되지 않습니다. 이를 방지하려면 IDXGISwapChain::GetContainingOutput을 호출하여 현재 출력을 가져오고 이 출력에서 SetGammaControl 을 호출하여 올바른 동작을 가져옵니다.
감마 수정 사용에 대한 자세한 내용은 감마 수정 사용을 참조하세요.
DXGI 1.1
Windows 7에 포함되고 Windows Vista에 설치된 Direct3D 11 런타임에는 DXGI 버전 1.1이 포함되어 있습니다. 이 업데이트는 다양한 새 형식(특히 BGRA, 10비트 X2 바이어스 및 Direct3D 11의 BC6H 및 BC7 텍스처 압축)에 대한 정의와 원격 데스크톱 연결을 열거하기 위한 DXGI 팩터리 및 어댑터 인터페이스(CreateDXGIFactory1, IDXGIFactory1, IDXGIAdapter1)의 새 버전에 대한 정의를 추가합니다.
Direct3D 11을 사용하는 경우 런타임은 NULL IDXGIAdapter 포인터를 사용하여 D3D11CreateDevice 또는 D3D11CreateDeviceAndSwapChain을 호출할 때 기본적으로 DXGI 1.1을 사용합니다. 동일한 프로세스에서 DXGI 1.0 및 DXGI 1.1의 혼합 사용은 지원되지 않습니다. 동일한 프로세스의 여러 팩터리에서 DXGI 개체 인스턴스를 혼합하는 것도 지원되지 않습니다. 따라서 DirectX 11을 사용하는 경우 DXGI 인터페이스를 명시적으로 사용하는 경우 "DXGI.DLL"의 CreateDXGIFactory1 진입점에서 만든 IDXGIFactory1을 사용하여 애플리케이션이 항상 DXGI 1.1을 사용하고 있는지 확인합니다.
DXGI 1.2
Windows 8 포함된 Direct3D 11.1 런타임에는 DXGI 버전 1.2도 포함됩니다.
DXGI 1.2를 사용하면 다음 기능을 사용할 수 있습니다.
스테레오 렌더링
픽셀당 16비트 형식
- 이제 DXGI_FORMAT_B5G6R5_UNORM 및 DXGI_FORMAT_B5G5R5A1_UNORM 완전히 지원됩니다.
- 새 DXGI_FORMAT_B5G5R5A1_UNORM 형식이 추가되었습니다.
비디오 형식
새 DXGI 인터페이스
DXGI 1.2 기능에 대한 자세한 내용은 DXGI 1.2 개선 사항을 참조하세요.