컴퓨터 그래픽스/DirectX

4. Direct3D의 초기화와 Tutorial02 코드 분석1

san10 2023. 5. 12. 22:36

https://github.com/walbourn/directx-sdk-samples

 

GitHub - walbourn/directx-sdk-samples: This repo contains Direct3D 11, XInput, and XAudio2 samples C++ samples from the legacy D

This repo contains Direct3D 11, XInput, and XAudio2 samples C++ samples from the legacy DirectX SDK updated to build using the Windows 10 SDK - GitHub - walbourn/directx-sdk-samples: This repo cont...

github.com

여기에 DirectX11 샘플 코드가 있는데..

오늘은 이중에 삼각형을 그리는 Tutorial02의 코드 분석을 해보려고 한다!

 

물방울책과 https://www.youtube.com/watch?v=NTvhVxSC_80 

이분의 유튜브 강의를 참고했다!

 

 

Direct3D의 초기화 과정

  1. 1. D3D11CreateDevice 함수를 이용해서 장치, 즉 ID3D11Device 인터페이스와
    장치 문맥, 즉 ID3D11DeviceContext 인터페이스를 생성한다.
  2. ID3D11Device::CheckMultisampleQualityLevels 메서드를 사용해서 4XMSAA 품질 수준 지원 여부를 점검한다.
    (다중표본화를 지원하는지 확인한다)
  3. swap chain의 특석을 서술하는 DXGI_SWAP_CHAIN_DESC 구조체를 채운다.
  4. 장치를 생성하는데 사용했던 IDXGIFactory 인터페이스를 질의해서 IDXGISwapChain 인스턴스를 생성한다.
  5. swap chain의 후면 버퍼에 대한 렌더 대상 뷰를 생성한다.
  6. 깊이-스텐실 버퍼와 그에 연결되는 깊이-스텐실 뷰를 생성한다.
  7. 렌더 대상 뷰와 깊이-스텐실 뷰를 Direct3D가 사용할 수 있도록 렌더링 파이프라인의 출력 병합기 단계에 묶는다.
  8. 뷰포트를 설정한다.

 

Tutorial02.cpp

HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow )
{
    // Register class
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof( WNDCLASSEX );
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon( hInstance, ( LPCTSTR )IDI_TUTORIAL1 );
    wcex.hCursor = LoadCursor( nullptr, IDC_ARROW );
    wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 );
    wcex.lpszMenuName = nullptr;
    wcex.lpszClassName = L"TutorialWindowClass";
    wcex.hIconSm = LoadIcon( wcex.hInstance, ( LPCTSTR )IDI_TUTORIAL1 );
    if( !RegisterClassEx( &wcex ) )
        return E_FAIL;

    // Create window
    g_hInst = hInstance;
    RECT rc = { 0, 0, 800, 600 };
    AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );
    g_hWnd = CreateWindow( L"TutorialWindowClass", L"Direct3D 11 Tutorial 2: Rendering a Triangle",
                           WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
                           CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, nullptr, nullptr, hInstance,
                           nullptr );
    if( !g_hWnd )
        return E_FAIL;

    ShowWindow( g_hWnd, nCmdShow );

    return S_OK;
}

 

InitWindow는 윈도우 창을 초기화 하는 함수이다.

InitWindow에서 창 크기나 제목같은 설정을 하고 윈도우를 생성한다.

 

 

그 다음으로는 initDevice()인데..

코드가 꽤 길기에 나누어서 분석하려 한다!

D3D_DRIVER_TYPE driverTypes[] =
{
    D3D_DRIVER_TYPE_HARDWARE,
    D3D_DRIVER_TYPE_WARP,
    D3D_DRIVER_TYPE_REFERENCE,
};
UINT numDriverTypes = ARRAYSIZE( driverTypes );

D3D_FEATURE_LEVEL featureLevels[] =
{
    D3D_FEATURE_LEVEL_11_1,
    D3D_FEATURE_LEVEL_11_0,
    D3D_FEATURE_LEVEL_10_1,
    D3D_FEATURE_LEVEL_10_0,
};
UINT numFeatureLevels = ARRAYSIZE( featureLevels );

for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
{
    g_driverType = driverTypes[driverTypeIndex];
    hr = D3D11CreateDevice( nullptr, g_driverType, nullptr, createDeviceFlags, featureLevels, numFeatureLevels,
                            D3D11_SDK_VERSION, &g_pd3dDevice, &g_featureLevel, &g_pImmediateContext );

    if ( hr == E_INVALIDARG )
    {
        // DirectX 11.0 platforms will not recognize D3D_FEATURE_LEVEL_11_1 so we need to retry without it
        hr = D3D11CreateDevice( nullptr, g_driverType, nullptr, createDeviceFlags, &featureLevels[1], numFeatureLevels - 1,
                                D3D11_SDK_VERSION, &g_pd3dDevice, &g_featureLevel, &g_pImmediateContext );
    }

    if( SUCCEEDED( hr ) )
        break;
}
if( FAILED( hr ) )
    return hr;

driverTypes[]에는 설정할 드라이버들의 값을 지정한다.

여기에서는 hardware, wrap, reference 3가지 값이 들어가있다.

 

hardware는 GPU를 사용해서 렌더링하고,

wrap은 CPU를 이용해서 렌더링 하기에 빠르다고 한다.

(그러나 wrap은 Direct11을 지원하지 않는다고 한다?)

refenrence는 reference device를 생성하며, 개발자 전용으로 만들어 졌다고 한다.

그렇기에 실제 출시할 때는 이것을 표시하면 안된다!

 

일반적으로는 D3D_DRIVER_TYPE_HARDWARE로 설정한다.

 

그리고 featureLevels[]에는 기능 수준의 값을 저장하는 배열이다.

이후 for문을 돌면서, 드라이버와 featureLevel을 결정하는데,

일반적으로는 성공하면 바로 break로 for문을 빠져나오기에

배열의 첫번째에 있는 Hardware와 11_1이 설정되겠지만

만약 실패한다면 그 다음의 값을 설정하게 된다.

 

// Create swap chain
    IDXGIFactory2* dxgiFactory2 = nullptr;
    hr = dxgiFactory->QueryInterface( __uuidof(IDXGIFactory2), reinterpret_cast<void**>(&dxgiFactory2) );
    if ( dxgiFactory2 )
    {
        // DirectX 11.1 or later
        hr = g_pd3dDevice->QueryInterface( __uuidof(ID3D11Device1), reinterpret_cast<void**>(&g_pd3dDevice1) );
        if (SUCCEEDED(hr))
        {
            (void) g_pImmediateContext->QueryInterface( __uuidof(ID3D11DeviceContext1), reinterpret_cast<void**>(&g_pImmediateContext1) );
        }

        DXGI_SWAP_CHAIN_DESC1 sd = {};
        sd.Width = width;
        sd.Height = height;
        sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        sd.SampleDesc.Count = 1;
        sd.SampleDesc.Quality = 0;
        sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        sd.BufferCount = 1;

        hr = dxgiFactory2->CreateSwapChainForHwnd( g_pd3dDevice, g_hWnd, &sd, nullptr, nullptr, &g_pSwapChain1 );
        if (SUCCEEDED(hr))
        {
            hr = g_pSwapChain1->QueryInterface( __uuidof(IDXGISwapChain), reinterpret_cast<void**>(&g_pSwapChain) );
        }

        dxgiFactory2->Release();
    }
    else
    {
        // DirectX 11.0 systems
        DXGI_SWAP_CHAIN_DESC sd = {};
        sd.BufferCount = 1;
        sd.BufferDesc.Width = width;
        sd.BufferDesc.Height = height;
        sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        sd.BufferDesc.RefreshRate.Numerator = 60;
        sd.BufferDesc.RefreshRate.Denominator = 1;
        sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        sd.OutputWindow = g_hWnd;
        sd.SampleDesc.Count = 1;
        sd.SampleDesc.Quality = 0;
        sd.Windowed = TRUE;

        hr = dxgiFactory->CreateSwapChain( g_pd3dDevice, &sd, &g_pSwapChain );
    }

이부분은 swap chain을 생성하는 부분이다.

swap chain을 생성하기 위해선 DXGI_SWAP_CHAIN_DESC 구조체의 인스턴스를 만들어야 하는데..

이 코드에서는 우선 DirectX 가 11.1이상일 경우 DXGI_SWAP_CHAIN_DESC1 구조체로 만들고,

그 미만이라면 DXGI_SWAP_CHAIN_DESC으로 생성한다.

 

둘의 차이가 뭔가 했는데

DXGI_SWAP_CHAIN_DESC1 같은 경우에는 11.1 이상에서의 기능을 지원하기위해

더 많은 멤버 변수를 가지고 있다고 한다.

 

 DXGI_SWAP_CHAIN_DESC에는 여러 멤버변수들이 있다.

  1.  BufferCount : swap chain에서 사용할 후면 버퍼의 개수이다.
  2. BufferDesc: 후면 버퍼의 속성들을 서술하는 구조체이다.
    너비, 높이, 포맷등을 지정할 수 있다.
  3. BufferUsage : 버퍼의 용도를 지정하는 구조체
    DXGI_USAGE_RENDER_TARGET_OUTPUT는 렌더 대상으로 버퍼를 사용한다는 뜻이다!

이렇게 swap chain 인스턴스를 만들고, IDXGIFactory 인스턴스를 통해

swap chain 인터페이스(IDXGISwapChain)을 생성한다

(11.1 이상의 버전이라면 IDXGISwapChain1을 생성)

'컴퓨터 그래픽스 > DirectX' 카테고리의 다른 글

Tutorial02 코드 분석2  (0) 2023.05.26
DirectX11 개발 환경 설정  (0) 2023.05.12
4. Direct3D 기본 지식  (0) 2023.05.12