WM_PAINT(MFC: OnPaint) 이벤트는 창의 내용을 그릴 때 호출되는 이벤트이다. 이 이벤트에 DirectX 9.0을 적용하여 보겠다.
IDirect3DDevice9::TestCooperativeLevel
로스트 상태는, DirectX 객체와 실제 그래픽 어댑터 사이의 연결이 끊어진 상태를 뜻한다. DirectX 객체가 생성된 시점과 이 객체를 이용하여 실제로 그리는 시각 시점의 상대적으로 긴 시간이 있는 경우 이 사이에 여러가지 이유(예: 로그오프, 절전모드 등)로 객체와 장치간 연결이 끊어질 수 있는데 이는 IDirect3DDevice9::TestCooperativeLevel함수로 체크할 수 있다.
만일 DirectX와 장치간 연결이 정상이 아닐 경우 그 원인에 따라 D3DERR_DRIVERINTERNALERROR, D3DERR_DEVICELOST, D3DERR_DEVICENOTRESET 중 하나를 반환하는데, 이 때는 IDirect3DDevice9::CreateDevice에 사용되었던 D3DPRESENT_PARAMETERS 구조체를 다시 사용하여 IDirect3DDevice9::Reset 함수를 실행하여 장치와 객체의 연결을 리셋한다.
/* C++ Source */
IDirect3DDevice * pDirect3DDevice;
D3DPRESENT_PARAMETERS d3dPresentParameters;
HRESULT hResult;
// ...
hResult = pDirect3DDevice->TestCooperativeLevel();
switch (hResult)
{
case D3DERR_DRIVERINTERNALERROR:
pDirect3DDevice->Reset(&d3dPresentParameters); // D3DPRESENT_PARAMETERS *
// Reset 실패 시 각종 조치
break;
case D3DERR_DEVICELOST:
// Reset 실패 시 각종 조치
pDirect3DDevice->Reset(&d3dPresentParameters); // D3DPRESENT_PARAMETERS *
break;
case D3DERR_DEVICENOTRESET:
// Reset 실패 시 각종 조치
pDirect3DDevice->Reset(&d3dPresentParameters); // D3DPRESENT_PARAMETERS *
break;
}
IDirect3DDevice9::Clear
장치의 로스트 상태 확인 및 조치를 완료하였다면 지정된 위치의 버퍼를 특정 색상으로 지운다.
/* C++ Source */
IDirect3DDevice * pDirect3DDevice;
D3DPRESENT_PARAMETERS d3dPresentParameters;
DWORD dwRects;
D3DRECT * pRects;
HRESULT hResult;
// ...
hResult = pDirect3DDevice->Clear
(
0, // DWORD Count : pRects 배열의 원소 수
NULL, // const D3DRECT * pRects
D3DCLEAR_TARGET, //
D3DCOLOR_XRGB(0x00, 0xFF, 0xFF), // D3DCOLOR Color : 리셋 후 버퍼의 색상
0.0f, // float Z
0 // DWORD Stencil
);
if (FAILED(hResult))
{
// Clear 실패 시 각종 조치
}
DirectX 9.0을 시작하기 위해 객체의 생성을 하고 창을 닫을 때의 해제는 다음과 같이 한다. DirectX 객체는 COMComponent Object Model으로 제공되므로 C++ 언어를 사용한다.
Direct3DCreate9, IDirect3D9::Release
DirectX 9.0의 객체 생성은 Direct3DCreate9 함수를 실행한다. 성공하면 LPDIRECT3D9(struct IDirect3D9 *) 형식의 객체를 반환하고, 실패하면 NULL을 반환한다.
/* C++ Source */
#pragma comment(lib, "d3d9.lib") // DirectX 9.0 라이브러리를 링크
#include <Windows.h> // Windows API에서 제공되는 기본 자료형들을 불러오기
#include <d3d9.h> // DirectX 9.0 API를 불러오기
LPDIRECT3D9 lpDirect3D = (LPDIRECT3D9)NULL; // DirectX 9.0 객체
/* 창을 생성하거나 그래픽을 리셋하는 단계에서 */
lpDirect3D = Direct3DCreate9(D3D_SDK_VERSION); // DirectX 9.0 버전으로 객체를 생성한다.
if (!lpDirect3D)
{
// 생성에 실패한 경우 실행 작업
}
/* 생성에 성공한 경우 실행 작업 */
DirectX 9.0 객체의 해제는 IDirect3D9::Release를 호출한다.
/* C++ Source */
#pragma comment(lib, "d3d9.lib") // DirectX 9.0 라이브러리를 링크
#include <Windows.h> // Windows API에서 제공되는 기본 자료형들을 불러오기
#include <d3d9.h> // DirectX 9.0 API를 불러오기
LPDIRECT3D9 lpDirect3D = (LPDIRECT3D9)NULL; // DirectX 9.0 객체
/* 창을 닫거나 그래픽을 종료하는 단계에서 */
lpDirect3D->Release(); // 객체 해제
/* 이후 작업 */
실제 코드에 적용
다음은 MFC에서 DirectX 객체를 생성하고 해제하는 예이다. CTestFrame은 CWndFrame에서 상속된 클래스이고 m_pDirect3D는 사전에 클래스 헤더에 선언된 멤버 변수이다.
/* MFC Source */
int CTestFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
HRESULT hResult = NULL;
int ret = 0;
TRACE(TEXT("[SUCCESS] CTestFrame::OnCreate called.\n"));
ret = CFrameWnd::OnCreate(lpCreateStruct);
if (ret != 0)
{
TRACE(TEXT("[FAILURE] CTestFrame::OnCreate.\n"));
return ret;
}
this->m_pDirect3D = Direct3DCreate9(D3D_SDK_VERSION);
if (this->m_pDirect3D == NULL)
{
TRACE(TEXT("[FAILURE] Direct3DCreate9.\n"));
return ret;
}
return ret;
}
void CTestFrame::OnDestroy()
{
TRACE(TEXT("[SUCCESS] CTestFrame::OnDestroy called.\n"));
if (this->m_pDirect3D != NULL)
{
this->m_pDirect3D->Release();
}
CFrameWnd::OnDestroy();
}
어댑터 종류 열거하기
DirectX를 사용하기 위해 컴퓨터에서 지원 가능한 그래픽 어댑터의 종류를 모두 가져오려면 CreateDXGIFactory 함수로 생성된 IDXGIFactory형 객체를 사용한다.
CreateDXGIFactory
/* C++ Source */
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "dxgi.lib")
#include <d3d9.h>
#include <dxgi.h>
IDXGIFactory * pDXGIFactory = NULL;
IDXGIAdapter * pDXGIAdapter = NULL;
SIZE_T nDXGIAdapter = 0;
/* 창을 생성하거나 그래픽을 리셋하는 단계에서 */
hResult = CreateDXGIFactory
(
__uuidof(IDXGIFactory), // REFIID riid
reinterpret_cast<void **>(&pDXGIFactory) // void ** ppFactory
);
// 해당 인덱스 번호에 해당하는 어댑터가 없다면 DXGI_ERROR_NOT_FOUND를 반환하기 시작한다.
// 0번 어댑터부터 검색을 시작한다.
nDXGIAdapter = 0;
while (pDXGIFactory->EnumAdapter(nDXGIAdapter, &pDXGIAdapter) != DXGI_ERROR_NOT_FOUND)
{
nDXGIAdapter++; // 다음 번호의 어댑터를 확인한다.
}
실제 코드에 적용
그래픽 어댑터의 종류를 열거하는 위 코드를 응용하여 디버그 문자열로 그래픽 어댑터의 이름을 출력해보겠다.
/* MFC Source */
int CTestFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
HRESULT hResult = NULL;
int ret = 0;
TRACE(TEXT("[SUCCESS] CTestFrame::OnCreate called.\n"));
ret = CFrameWnd::OnCreate(lpCreateStruct);
if (ret != 0)
{
TRACE(TEXT("[FAILURE] OnCreate::OnCreate.\n"));
return ret;
}
// DirectX 객체를 생성
this->m_pDirect3D = Direct3DCreate9(D3D_SDK_VERSION);
if (this->m_pDirect3D == NULL)
{
TRACE(TEXT("[FAILURE] Direct3DCreate9.\n"));
return ret;
}
// 어댑터를 열거할 수 있는 IDXGIFactory 객체 생성
IDXGIFactory * pDXGIFactory = NULL;
hResult = CreateDXGIFactory
(
__uuidof(IDXGIFactory), // REFIID riid
reinterpret_cast<void **>(&pDXGIFactory) // void ** ppFactory
);
if (FAILED(hResult))
{
TRACE(TEXT("[FAILURE] CreateDXGIFactory.\n"));
return ret;
}
// 열거된 어댑터를 하나씩 배열에 보관
CArray<IDXGIAdapter *> dxgiAdapters;
IDXGIAdapter * pDXGIAdapter = NULL;
UINT i = 0;
while (TRUE)
{
hResult = pDXGIFactory->EnumAdapters
(
i++, // UINT Adapter
&pDXGIAdapter // IDXGIAdapter ** ppAdapter
);
if (hResult == DXGI_ERROR_NOT_FOUND) break;
dxgiAdapters.Add(pDXGIAdapter);
pDXGIAdapter = NULL;
}
// 배열에 보관된 어댑터의 이름을 디버그 문자열로 출력
for (INT_PTR i = 0; i < dxgiAdapters.GetCount(); i++)
{
DXGI_ADAPTER_DESC dxgiAdapterDesc;
TCHAR szBuffer[256];
dxgiAdapters[i]->GetDesc(&dxgiAdapterDesc);
TRACE(TEXT("[%2d] DXGI_ADAPTER_DESC.Description = %s\n"), i, dxgiAdapterDesc.Description);
}
// 사용이 끝난 어댑터 객체와 Factory 객체 해제
for (INT_PTR i = 0; i < dxgiAdapters.GetCount(); i++)
{
dxgiAdapters[i]->Release();
}
dxgiAdapters.RemoveAll();
pDXGIFactory->Release();
return ret;
}
본 컴퓨터에 장착된 그래픽 어댑터의 종류가 열거되는 것을 확인할 수 있다. 다만 [장치 관리자]에는 없는 Microsoft Basic Render Driver가 마지막에 있을 수도 있다.
그래픽 어댑터의 색상 포맷의 지원 여부 확인
ARGB, RGB, HSV 등등의 색상 포맷을 사용하고자 할 때 그래픽 어댑터가 이를 지원하는지 여부를 확인한다. 여기서 XRGB는 RGB와 같은 것을 의미한다.
IDirect3D9::EnumAdapterModes
/* C++ Source */
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "dxgi.lib")
#include <d3d9.h>
#include <dxgi.h>
LPDIRECT3D9 lpDirect3D = (LPDIRECT3D9)NULL; // DirectX 9.0 객체
UINT uAdapterMode = 0;
/* 창 또는 그래픽을 리셋하는 단계에서 */
uAdapterMode = lpDirect3D->GetAdapterModeCount
(
D3DADAPTER_DEFAULT, // UINT Adapter : 시스템 기본 어댑터를 사용하겠다
D3DFMT_X8R8G8B8 // D3DFORMAT Format : 픽셀당 32비트 색상 포맷을 쓴다. 단, 실제 사용되는 비트는 R, G, B 24비트이다.
);
IDirect3D9::EnumAdapterModes 함수는 그래픽 어댑터가 지원 가능한 모드를 상세히 열거하는 함수이다. 지원 모드는 D3DDISPLAYMODE 구조체를 통해 반환된다.
/* C++ Source */
UINT uAdapterMode = this->m_pDirect3D->GetAdapterModeCount
(
D3DADAPTER_DEFAULT, // UINT Adapter
D3DFMT_X8R8G8B8 // D3DFORMAT Format
);
for (UINT i = 0; i < uAdapterMode; i++)
{
D3DDISPLAYMODE d3dDisplayMode;
hResult = this->m_pDirect3D->EnumAdapterModes
(
D3DADAPTER_DEFAULT, // UINT Adapter
D3DFMT_X8R8G8B8, // D3DFORMAT Format
i, // UINT Mode : 어댑터에 대해 얻고자 하는 모드의 번호
&d3dDisplayMode // D3DDISPLAYMODE * pMode : 반환 정보
);
}
실제 코드에 적용
기본 어댑터(D3DADAPTER_DEFAULT)의 RGB 컬러(D3DFMT_X8R8G8B8)로 몇 가지의 어댑터 모드가 지원 가능한지 조회해 보겠다. 실행 결과는 컴퓨터마다 상이하다.
/* MFC Source */
int CTestFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
HRESULT hResult = NULL;
int ret = 0;
TRACE(TEXT("[SUCCESS] CTestFrame::OnCreate called.\n"));
ret = CFrameWnd::OnCreate(lpCreateStruct);
if (ret != 0)
{
TRACE(TEXT("[FAILURE] OnCreate::OnCreate.\n"));
return ret;
}
this->m_pDirect3D = Direct3DCreate9(D3D_SDK_VERSION);
if (this->m_pDirect3D == NULL)
{
TRACE(TEXT("[FAILURE] Direct3DCreate9.\n"));
return ret;
}
IDXGIFactory * pDXGIFactory = NULL;
hResult = CreateDXGIFactory
(
__uuidof(IDXGIFactory), // REFIID riid
reinterpret_cast(&pDXGIFactory) // void ** ppFactory
);
if (FAILED(hResult))
{
TRACE(TEXT("[FAILURE] CreateDXGIFactory.\n"));
return ret;
}
UINT uAdapterMode = this->m_pDirect3D->GetAdapterModeCount
(
D3DADAPTER_DEFAULT, // UINT Adapter
D3DFMT_X8R8G8B8 // D3DFORMAT Format
);
TRACE(TEXT("[SUCCESS] GetAdapterModeCount = %u\n"), uAdapterMode);
for (UINT i = 0; i < uAdapterMode; i++)
{
D3DDISPLAYMODE d3dDisplayMode;
hResult = this->m_pDirect3D->EnumAdapterModes
(
D3DADAPTER_DEFAULT, // UINT Adapter
D3DFMT_X8R8G8B8, // D3DFORMAT Format
i, // UINT Mode : 어댑터에 대해 얻고자 하는 모드의 번호
&d3dDisplayMode // D3DDISPLAYMODE * pMode : 반환 정보
);
if (SUCCEEDED(hResult))
{
TRACE(TEXT("[%2u] EnumAdapterModes\n"), i);
TRACE(TEXT(" D3DDISPLAYMODE.Format = %x\n"), d3dDisplayMode.Format);
TRACE(TEXT(" D3DDISPLAYMODE.RefreshRate = %u\n"), d3dDisplayMode.RefreshRate);
TRACE(TEXT(" D3DDISPLAYMODE.Width = %u\n"), d3dDisplayMode.Width);
TRACE(TEXT(" D3DDISPLAYMODE.Height = %u\n"), d3dDisplayMode.Height);
}
}
return ret;
}
어댑터 장치의 성능 확인하기
장치의 성능 중에서 특히 자주하게 확인할 것은 하드웨어로 버텍스 처리가 가능한지 여부이다. 하드웨어에서 버텍스 프로세싱이 가능하면 그대로 이용하고, 그렇지 않을 경우 소프트웨어(CPU)로 버텍스를 연산해야 한다. 이 경우 체감 속도가 다소 느릴 수 있다.
IDirect3D9::GetDeviceCaps
IDirect3D9::GetDeviceCaps 함수가 반환하는 구조체인 D3DCAPS9의 DevCaps 멤버를 통해 확인이 가능하며 D3DDEVCAPS_HWTRANSFORMANDLIGHT 비트의 설정 여부로 하드웨어의 버텍스 처리 성능을 판단할 수 있다.
/* C++ Source */
D3DCAPS9 d3dCaps;
ZeroMemory(&d3dCaps, sizeof(D3DCAPS9));
hResult = this->m_pDirect3D->GetDeviceCaps
(
D3DADAPTER_DEFAULT, // UINT Adapter
D3DDEVTYPE_HAL, // D3DDEVTYPE DeviceType
&d3dCaps // D3DCAPS9 * pCaps
);
if (FAILED(hResult))
{
// 장치 정보 얻기에 실패시 조치
}
if ((d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) != 0)
{
// 하드웨어로 버텍스 처리가 가능하다.
}
else
{
// 소프트웨어로 버텍스 처리가 가능하다. (속도 느림)
}
실제 코드에 적용
실제 코드에 적용하여 보겠다.
/* MFC Source */
int CTestFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
HRESULT hResult = NULL;
int ret = 0;
TRACE(TEXT("[SUCCESS] CTestFrame::OnCreate called.\n"));
ret = CFrameWnd::OnCreate(lpCreateStruct);
if (ret != 0)
{
TRACE(TEXT("[FAILURE] OnCreate::OnCreate.\n"));
return ret;
}
this->m_pDirect3D = Direct3DCreate9(D3D_SDK_VERSION);
if (this->m_pDirect3D == NULL)
{
TRACE(TEXT("[FAILURE] Direct3DCreate9.\n"));
return ret;
}
D3DCAPS9 d3dCaps;
ZeroMemory(&d3dCaps, sizeof(D3DCAPS9));
hResult = this->m_pDirect3D->GetDeviceCaps
(
D3DADAPTER_DEFAULT, // UINT Adapter
D3DDEVTYPE_HAL, // D3DDEVTYPE DeviceType
&d3dCaps // D3DCAPS9 * pCaps
);
if (FAILED(hResult))
{
TRACE(TEXT("[FAILURE] IDirect3D9::GetDeviceCaps = %u"), hResult);
return ret;
}
if ((d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) != 0)
{
TRACE(TEXT("[DEVCAPS] D3DCAPS9.DevCaps has D3DDEVCAPS_HWTRANSFORMANDLIGHT\n"));
}
else
{
TRACE(TEXT("[DEVCAPS] D3DCAPS9.DevCaps not D3DDEVCAPS_HWTRANSFORMANDLIGHT\n"));
}
return ret;
}
IDirect3DDevice9 객체 생성
그래픽 어댑터를 호출할 수 있는 장치 인터페이스인 IDirect3DDevice9 객체를 생성한다. 앞서 확인한 각종 하드웨어 사항과 본인이 선정한 색상 포맷, 기타 필요한 사항들을 조합하여 하나의 컨텍스트를 만든 다음 이를 IDirect3D9::CreateDevice함수에 D3DPRESENT_PARAMETERS 구조체로 전달한다.