공부중

[DirectX12]Hello Wolrd Sample - D3D12HelloWindow - 2 본문

Programing/DirectX

[DirectX12]Hello Wolrd Sample - D3D12HelloWindow - 2

곤란 2018. 4. 7. 21:43
반응형

이어서...

 

다시 Main.cpp를 살펴보고 Run 멤버 함수를 살펴보자.

//*********************************************************
//
//FileName : Main.cpp
//
//*********************************************************
#include "stdafx.h"
#include "D3D12HelloWindow.h"

_Use_decl_annotations_
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow)
{
	D3D12HelloWindow sample(1280, 720, L"D3D12 Hello Window");
	return Win32Application::Run(&sample, hInstance, nCmdShow);
}

Run을 보면 Win32Application에서 바로 Run을 호출하는데 먼저 Win32Application class를 보자.

//*********************************************************
//
//FileName : Win32Application.h
//
//*********************************************************

#pragma once

#include "DXSample.h"

class DXSample;

class Win32Application
{
public:
	static int Run(DXSample* pSample, HINSTANCE hInstance, int nCmdShow);
	static HWND GetHwnd() { return m_hwnd; }

protected:
	static LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

private:
	static HWND m_hwnd;
};

전부 멤버함수들은 static함수이고.

윈도우 프로시저함수 하나와 핸들을 가지고 있고 Run함수를 가지고 있는것이 끝.

Run에서 모든 Init과 메시지 루프 등을 가지고 있을것이다. (저기밖에 없....)

 

Run함수의 파라미터로 DXSample* 형을 받는데 D3D12HelloWindow class의 부모가 DXSample이므로 문제없이 파라미터 전달을 받았다.

 

이제 Run함수를 살펴보자.

int Win32Application::Run(DXSample* pSample, HINSTANCE hInstance, int nCmdShow)
{
        // 명령 줄 매개 변수를 구문 분석합니다.
	int argc;
	LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
	pSample->ParseCommandLineArgs(argv, argc);
	LocalFree(argv);

	// 윈도우 클래스를 초기화합니다.
	WNDCLASSEX windowClass = { 0 };
	windowClass.cbSize = sizeof(WNDCLASSEX);
	windowClass.style = CS_HREDRAW | CS_VREDRAW;
	windowClass.lpfnWndProc = WindowProc;
	windowClass.hInstance = hInstance;
	windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	windowClass.lpszClassName = L"DXSampleClass";
	RegisterClassEx(&windowClass);

	RECT windowRect = { 0, 0, static_cast<LONG>(pSample->GetWidth()), static_cast<LONG>(pSample->GetHeight()) };
	AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE);

	// 윈도우를 만들고 핸들을 저장하십시오.
	m_hwnd = CreateWindow(
		windowClass.lpszClassName,
		pSample->GetTitle(),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		windowRect.right - windowRect.left,
		windowRect.bottom - windowRect.top,
		nullptr,		// 부모 윈도우 없음
		nullptr,		// 메뉴를 사용 안함.
		hInstance,
		pSample);

	// Sample을 초기화하십시오. OnInit은 DXSample의 각 하위 구현에서 정의됩니다.
	pSample->OnInit();

	ShowWindow(m_hwnd, nCmdShow);

	// Main sample 루프.
	MSG msg = {};
	while (msg.message != WM_QUIT)
	{
		// 대기열에있는 메시지를 처리합니다.
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	pSample->OnDestroy();

	// WM_QUIT 메시지의이 부분을 Windows로 반환하십시오.
	return static_cast<char>(msg.wParam);
}

영문주석은 대강 구글번역을 돌렸다.

크게 Run함수의 내부를 나누어보면

1. 명령줄 매개변수 부분

2. 윈도우 클래스 초기화 부분

3. 윈도우 생성

4. 전달받은 Sample을 초기화 하기 (내부에 D3D 초기화, 변수명이 sample이라서 sample초기화라고 적은듯 하다.)

5. 메시지 루프 돌리기.

6. 종료시 처리부분.

 

1~3번부분은 넘어가고 4번 부분을 살펴볼 예정이다.

4번부분은 중간의 pSample->OnInit( );  라인이고 내부를 살펴보자.

void D3D12HelloWindow::OnInit()
{
	LoadPipeline();
	LoadAssets();
}

파이프라인과 에셋을 로드하는 함수 두개를 call한다.

//*********************************************************
//
//FileName : DXSample.h
//
//*********************************************************

class DXSample
{
public:
	DXSample(UINT width, UINT height, std::wstring name);
	virtual ~DXSample();

	virtual void OnInit() = 0;
	virtual void OnUpdate() = 0;
	virtual void OnRender() = 0;
	virtual void OnDestroy() = 0;
        // 중략...

D3D12HelloWindow의 부모(DXSample) OnInit 함수에는 순수 가상함수로 선언되어 있으므로 볼 내용이 없다.

 

먼저 LoadPipeline 함수를 살펴보자.

void D3D12HelloWindow::LoadPipeline()
{
	UINT dxgiFactoryFlags = 0;

#if defined(_DEBUG)
	// 디버그 레이어를 활성화합니다 (그래픽 도구의 "선택적 기능"필요).
        // 참고 : 장치 생성 후 디버그 레이어를 활성화하면 활성 장치가 무효화됩니다.
	{
		ComPtr<ID3D12Debug> debugController;
		if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
		{
			debugController->EnableDebugLayer();

			// 추가 디버그 레이어를 사용합니다.
			dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
		}
	}
#endif

	ComPtr<IDXGIFactory4> factory;
	ThrowIfFailed(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)));

	if (m_useWarpDevice)
	{
		ComPtr<IDXGIAdapter> warpAdapter;
		ThrowIfFailed(factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter)));

		ThrowIfFailed(D3D12CreateDevice(
			warpAdapter.Get(),
			D3D_FEATURE_LEVEL_11_0,
			IID_PPV_ARGS(&m_device)
			));
	}
	else
	{
		ComPtr<IDXGIAdapter1> hardwareAdapter;
		GetHardwareAdapter(factory.Get(), &hardwareAdapter);

		ThrowIfFailed(D3D12CreateDevice(
			hardwareAdapter.Get(),
			D3D_FEATURE_LEVEL_11_0,
			IID_PPV_ARGS(&m_device)
			));
	}

	// 명령 대기열을 설명하고 작성하십시오.
	D3D12_COMMAND_QUEUE_DESC queueDesc = {};
	queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
	queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;

	ThrowIfFailed(m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue)));

	// 스왑 체인(swap chain)을 기술하고 생성하십시오.
	DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
	swapChainDesc.BufferCount = FrameCount;
	swapChainDesc.Width = m_width;
	swapChainDesc.Height = m_height;
	swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
	swapChainDesc.SampleDesc.Count = 1;

	ComPtr<IDXGISwapChain1> swapChain;
	ThrowIfFailed(factory->CreateSwapChainForHwnd(
		m_commandQueue.Get(),		// Swap chain needs the queue so that it can force a flush on it.
		Win32Application::GetHwnd(),
		&swapChainDesc,
		nullptr,
		nullptr,
		&swapChain
		));

	// 이 샘플은 전체 화면 전환을 지원하지 않습니다.
	ThrowIfFailed(factory->MakeWindowAssociation(Win32Application::GetHwnd(), DXGI_MWA_NO_ALT_ENTER));

	ThrowIfFailed(swapChain.As(&m_swapChain));
	m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();

	// 설명자 힙(descriptor heaps)을 작성하십시오.
	{
		// 렌더 타겟 뷰 (RTV) 서술자 힙을 기술하고 생성하십시오.
		D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
		rtvHeapDesc.NumDescriptors = FrameCount;
		rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
		rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
		ThrowIfFailed(m_device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&m_rtvHeap)));

		m_rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
	}

	// 프레임 리소스를 만듭니다.
	{
		CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart());

		// 각 프레임에 대해 RTV(렌더 타겟 뷰)를 만듭니다.
		for (UINT n = 0; n < FrameCount; n++)
		{
			ThrowIfFailed(m_swapChain->GetBuffer(n, IID_PPV_ARGS(&m_renderTargets[n])));
			m_device->CreateRenderTargetView(m_renderTargets[n].Get(), nullptr, rtvHandle);
			rtvHandle.Offset(1, m_rtvDescriptorSize);
		}
	}

	ThrowIfFailed(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocator)));
}

드럽게 길다 -_-

다음 글에서 위에서부터 천천히 살펴보자.

 

다음글로!

반응형