단막 DirectX 9.0 활용
DirectX 9.0 객체의 생성과 해제
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비트이다.
);
실제 코드에 적용
그래픽 어댑터에서 해당 색상 포맷으로 몇 개의 모드를 지원하는지를 확인해 보겠다.
/* 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<void **>(&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);
return ret;
}
IDirect3D9::EnumAdapterModes
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
구조체로 전달한다.
IDirect3D9::CreateDevice
IDirect3D9::CreateDevice
의 사용 방법은 다음과 같다.
/* C++ Source */
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "dxgi.lib")
#include <d3d9.h>
#include <dxgi.h>
LPDIRECT3D9 lpDirect3D9 = NULL;
LPDIRECT3DDEVICE9 lpDirect3DDevice9 = NULL;
HRESULT hResult;
/* 그래픽을 리셋하는 부분에서 */
hResult = lpDirect3D9->CreateDevice
(
D3DADAPTER_DEFAULT, // UINT Adapter
D3DDEVTYPE_HAL, // D3DDEVTYPE DeviceType
&d3dCaps // D3DCAPS9 * pCaps
);
실제 코드에 적용
본 함수를 시험하기 전에 새 멤버변수를 클래스의 헤더에 선언하였다.
static const DWORD CTestFrame::GRAPHIC_WIDTH = 800;
- 윈도우의 폭이면서 그래픽 버퍼의 폭이다. 앞서 그래픽 어댑터에서 지원 가능한 것으로 확인된 800 픽셀을 상수로 지정한 것이다.
static const DWORD CTestFrame::GRAPHIC_HEIGHT = 600;
- 윈도우의 높이면서 그래픽 버퍼의 높이이다. 앞서 그래픽 어댑터에서 지원 가능한 것으로 확인된 600 픽셀을 상수로 지정한 것이다.
IDirect3DDevice * m_pDirect3DDevice = NULL
- 새로 생성될 장치 객체이다.
/* 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;
}
// 윈도우 크기를 조정한다.
RECT rect;
this->GetWindowRect(&rect);
this->SetWindowPos(NULL, rect.left, rect.top, CTestFrame::GRAPHIC_WIDTH, CTestFrame::GRAPHIC_HEIGHT, SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW);
D3DCAPS9 d3dCaps;
D3DPRESENT_PARAMETERS d3dPresentParameters;
ZeroMemory(&d3dCaps, sizeof(D3DCAPS9));
ZeroMemory(&d3dPresentParameters, sizeof(D3DPRESENT_PARAMETERS));
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"));
// 그래픽 어댑터가 하드웨어 버텍스 처리를 지원하는 경우
ZeroMemory(&d3dPresentParameters, sizeof(D3DPRESENT_PARAMETERS));
d3dPresentParameters.BackBufferWidth = CTestFrame::GRAPHIC_WIDTH;
d3dPresentParameters.BackBufferHeight = CTestFrame::GRAPHIC_HEIGHT;
d3dPresentParameters.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dPresentParameters.BackBufferCount = 1;
d3dPresentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dPresentParameters.hDeviceWindow = this->m_hWnd; // 윈도우 핸들
d3dPresentParameters.Windowed = TRUE; // 어플리케이션을 창 모드로 연다.
d3dPresentParameters.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
hResult = m_pDirect3D->CreateDevice
(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
this->m_hWnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING, // 하드웨어 버텍스 처리
&d3dPresentParameters,
&this->m_pDirect3DDevice
);
if (FAILED(hResult))
{
TRACE(TEXT("[FAILURE] IDirect3D9::CreateDevice = %x\n"), hResult);
return ret;
}
TRACE(TEXT("[SUCCESS] IDirect3D9::CreateDevice\n"));
}
else
{
TRACE(TEXT("[DEVCAPS] D3DCAPS9.DevCaps not D3DDEVCAPS_HWTRANSFORMANDLIGHT\n"));
// 그래픽 어댑터가 하드웨어 버텍스 처리를 지원하지 않는 경우
ZeroMemory(&d3dPresentParameters, sizeof(D3DPRESENT_PARAMETERS));
d3dPresentParameters.BackBufferWidth = CTestFrame::GRAPHIC_WIDTH;
d3dPresentParameters.BackBufferHeight = CTestFrame::GRAPHIC_HEIGHT;
d3dPresentParameters.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dPresentParameters.BackBufferCount = 1;
d3dPresentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dPresentParameters.hDeviceWindow = this->m_hWnd; // 윈도우 핸들
d3dPresentParameters.Windowed = TRUE; // 어플리케이션을 창 모드로 연다.
d3dPresentParameters.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
// 장치 객체를 생성한다.
hResult = m_pDirect3D->CreateDevice
(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
this->m_hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING, // 소프트웨어로 버텍스 연산한다. (느림)
&d3dPresentParameters,
&this->m_pDirect3DDevice
);
if (FAILED(hResult))
{
TRACE(TEXT("[FAILURE] IDirect3D9::CreateDevice = %x\n"), hResult);
return ret;
}
TRACE(TEXT("[SUCCESS] IDirect3D9::CreateDevice\n"));
}
return ret;>>
}