C++を使ってDirectX12を初期化

DirextX11を使用してましたがDX12もいいかなと思い、覚書
■Step1:環境構築
---
VisualStudio2017を使用しています。

■Step2：プロジェクト作成
---
VisualStudio2017を起動して「ファイル(F)」「新規作成(N)」「プロジェクト(p)」を選択します
![2018-12-06 (2).png](https://qiita-image-store.s3.amazonaws.com/0/122121/238cbca2-c1c2-03be-263f-ed4ebd9b066f.png)
今回はC++で構築するので「VisualC++」の「その他」から「空のプロジェクト」を選択します。
名前は適当に（今回はtest01としています）
![2018-12-06 (5).png](https://qiita-image-store.s3.amazonaws.com/0/122121/f98fe420-0342-c151-80fe-812d0e3db311.png)
「OK」を押しますと以下のようになります、ここで今後の為「X64」に変更しておきましょう
![2018-12-06 (6).png](https://qiita-image-store.s3.amazonaws.com/0/122121/ff8d4961-b569-49ad-1769-7425ad4a331f.png)

■Step3：test01.cppを作成
---
test01.cppを追加するために「ソースファイル」で右クリックするとサブメニューがでるので、そこで「追加（D)」「新しい項目（W)」を選択してください。
![2018-12-06 (7).png](https://qiita-image-store.s3.amazonaws.com/0/122121/a16f881c-f397-3ec0-0f79-23a3d2ff28ba.png)
名前を「test01.cpp」にし「C++ファイル（.cpp）」が選択されていることを確認してから「追加（A）」を押します。
![2018-12-06 (8).png](https://qiita-image-store.s3.amazonaws.com/0/122121/7c7175ca-ad3c-f2a5-07c0-8cc26f1afa59.png)
test01.cppが作成できたので
![2018-12-06 (9).png](https://qiita-image-store.s3.amazonaws.com/0/122121/059a1be9-330c-971a-76bd-46469e583c50.png)

```c++:test01.cpp
#include	<Windows.h>
#include	<tchar.h>

#define	WINDOW_WIDTH  720
#define	WINDOW_HEIGHT 480

LRESULT CALLBACK	WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
#define	CLASS_NAME	"CLASS TEST01"
#define	PROC_NAME	"test01"

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst, LPTSTR lpCmdLine, int nCmdShow)
{
	HWND			hwnd;
	MSG				msg;
	if (!hPreInst) {
		WNDCLASS	my_prog;
		my_prog.style			= CS_HREDRAW | CS_VREDRAW;
		my_prog.lpfnWndProc		= WndProc;
		my_prog.cbClsExtra		= 0;
		my_prog.cbWndExtra		= 0;
		my_prog.hInstance		= hInstance;
		my_prog.hIcon			= NULL;
		my_prog.hCursor			= LoadCursor(NULL, IDC_ARROW);
		my_prog.hbrBackground	= NULL;
		my_prog.lpszMenuName	= NULL;
		my_prog.lpszClassName	= _T(CLASS_NAME);

		if (!RegisterClass(&my_prog)) {
			return FALSE;
		}
	}

	RECT rect = {
		(LONG)0, 
		(LONG)0, 
		(LONG)(WINDOW_WIDTH), 
		(LONG)(WINDOW_HEIGHT)
	};
	AdjustWindowRect(
		&rect,                                    // クライアント矩形
		WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION,  // ウィンドウスタイル
		FALSE                                     // メニューフラグ
	);
	hwnd = CreateWindow(_T(CLASS_NAME),
		_T(PROC_NAME),
		WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		rect.right - rect.left,           // ウィンドウの幅
		rect.bottom - rect.top,           // ウィンドウの高さ
		NULL,
		NULL,
		hInstance,
		NULL);

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	do {
		if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	} while (msg.message != WM_QUIT);
	return (int)(msg.wParam);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return(DefWindowProc(hwnd, msg, wParam, lParam));
	}
	return (0L);
}
```
このようにします。
■Step4：仮実行
---
実行するとこんな感じ
![2018-12-06 (10).png](https://qiita-image-store.s3.amazonaws.com/0/122121/41056a25-ac92-84f7-eb66-9e6331415fde.png)

■Step5：DirextX12を制御
----
DirectX12を制御するクラス「DX12Renderere.cpp」「DX12Renderere.h」を追加します。
![2018-12-06 (11).png](https://qiita-image-store.s3.amazonaws.com/0/122121/7c1b6f65-81b5-4da0-2878-51c08cf05e96.png)

```c++:DX12Renderer.cpp
#include <fstream>
#include "DX12Renderer.h"

DX12Renderer::DX12Renderer(HWND hwnd, int window_width, int window_height) :
	_window_hwnd(hwnd),
	_window_width(window_width),
	_window_height(window_height){

	UINT flagsDXGI = 0;
	ID3D12Debug* debug = nullptr;
	HRESULT hr;
#if _DEBUG
	D3D12GetDebugInterface(IID_PPV_ARGS(&debug));
	if (debug) {
		debug->EnableDebugLayer();
		debug->Release();
	}
	flagsDXGI |= DXGI_CREATE_FACTORY_DEBUG;
#endif
	hr = CreateDXGIFactory2(flagsDXGI, IID_PPV_ARGS(_factory.ReleaseAndGetAddressOf()));
	if (FAILED(hr)) {
		return;
	}
	ComPtr<IDXGIAdapter> adapter;
	hr = _factory->EnumAdapters(0, adapter.GetAddressOf());
	if (FAILED(hr)) {
		return;
	}
	// デバイス生成.
	// D3D12は 最低でも D3D_FEATURE_LEVEL_11_0 を要求するようだ.
	hr = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(_device.GetAddressOf()));
	if (FAILED(hr)) {
		return;
	}
	// コマンドアロケータを生成.
	hr = _device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(_command_allocator.GetAddressOf()));
	if (FAILED(hr)) {
		return;
	}
	// コマンドキューを生成.
	D3D12_COMMAND_QUEUE_DESC desc_command_queue;
	ZeroMemory(&desc_command_queue, sizeof(desc_command_queue));
	desc_command_queue.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
	desc_command_queue.Priority = 0;
	desc_command_queue.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
	hr = _device->CreateCommandQueue(&desc_command_queue, IID_PPV_ARGS(_command_queue.GetAddressOf()));
	if (FAILED(hr)) {
		return;
	}
	// コマンドキュー用のフェンスを準備.
	_fence_event = CreateEvent(NULL, FALSE, FALSE, NULL);
	hr = _device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(_queue_fence.GetAddressOf()));
	if (FAILED(hr)) {
		return;
	}

	// スワップチェインを生成.
	DXGI_SWAP_CHAIN_DESC desc_swap_chain;
	ZeroMemory(&desc_swap_chain, sizeof(desc_swap_chain));
	desc_swap_chain.BufferCount = 2;
	desc_swap_chain.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	desc_swap_chain.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	desc_swap_chain.OutputWindow = _window_hwnd;
	desc_swap_chain.SampleDesc.Count = 1;
	desc_swap_chain.Windowed = TRUE;
	desc_swap_chain.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
	desc_swap_chain.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
	hr = _factory->CreateSwapChain(_command_queue.Get(), &desc_swap_chain, (IDXGISwapChain**)_swap_chain.GetAddressOf());
	if (FAILED(hr)) {
		return;
	}
	// コマンドリストの作成.
	hr = _device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, _command_allocator.Get(), nullptr, IID_PPV_ARGS(_command_list.GetAddressOf()));
	if (FAILED(hr)) {
		return;
	}

	// ディスクリプタヒープ(RenderTarget用)の作成.
	D3D12_DESCRIPTOR_HEAP_DESC desc_heap;
	ZeroMemory(&desc_heap, sizeof(desc_heap));
	desc_heap.NumDescriptors = 2;
	desc_heap.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
	desc_heap.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
	hr = _device->CreateDescriptorHeap(&desc_heap, IID_PPV_ARGS(_descriptor_heap.GetAddressOf()));
	if (FAILED(hr)) {
		return;
	}

	// レンダーターゲット(プライマリ用)の作成.
	UINT strideHandleBytes = _device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
	for (UINT i = 0;i < desc_swap_chain.BufferCount;++i) {
		hr = _swap_chain->GetBuffer(i, IID_PPV_ARGS(_render_target[i].GetAddressOf()));
		if (FAILED(hr)) {
			return;
		}
		_rtv_handle[i] = _descriptor_heap->GetCPUDescriptorHandleForHeapStart();
		_rtv_handle[i].ptr += i * strideHandleBytes;
		_device->CreateRenderTargetView(_render_target[i].Get(), nullptr, _rtv_handle[i]);
	}
	return;
}

DX12Renderer::~DX12Renderer() {}


void DX12Renderer::SetResourceBarrier(ID3D12GraphicsCommandList* commandList, ID3D12Resource* resource, D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after)
{
	D3D12_RESOURCE_BARRIER descBarrier;
	ZeroMemory(&descBarrier, sizeof(descBarrier));
	descBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
	descBarrier.Transition.pResource = resource;
	descBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
	descBarrier.Transition.StateBefore = before;
	descBarrier.Transition.StateAfter = after;
	commandList->ResourceBarrier(1, &descBarrier);
}

void DX12Renderer::WaitForCommandQueue(ID3D12CommandQueue* pCommandQueue) {
	static UINT64 frames = 0;
	_queue_fence->SetEventOnCompletion(frames, _fence_event);
	pCommandQueue->Signal(_queue_fence.Get(), frames);
	WaitForSingleObject(_fence_event, INFINITE);
	frames++;
}

//	描画
HRESULT DX12Renderer::Render() {
	float clearColor[4] = { 1.0f,0.0f,0.0f,0.0f };
	D3D12_VIEWPORT viewport;
	viewport.TopLeftX	= 0; viewport.TopLeftY = 0;
	viewport.Width		= (FLOAT)_window_width;
	viewport.Height		= (FLOAT)_window_height;
	viewport.MinDepth	= 0;
	viewport.MaxDepth	= 1;

	int targetIndex = _swap_chain->GetCurrentBackBufferIndex();

	SetResourceBarrier(
		_command_list.Get(),
		_render_target[targetIndex].Get(),
		D3D12_RESOURCE_STATE_PRESENT,
		D3D12_RESOURCE_STATE_RENDER_TARGET);

	// レンダーターゲットのクリア処理.
	_command_list->RSSetViewports(1, &viewport);
	_command_list->ClearRenderTargetView(_rtv_handle[targetIndex], clearColor, 0, nullptr);

	// Presentする前の準備.
	SetResourceBarrier(
		_command_list.Get(),
		_render_target[targetIndex].Get(),
		D3D12_RESOURCE_STATE_RENDER_TARGET,
		D3D12_RESOURCE_STATE_PRESENT);

	_command_list->Close();

	// 積んだコマンドの実行.
	ID3D12CommandList* pCommandList = _command_list.Get();
	_command_queue->ExecuteCommandLists(1, &pCommandList);
	_swap_chain->Present(1, 0);

	WaitForCommandQueue(_command_queue.Get());
	_command_allocator->Reset();
	_command_list->Reset(_command_allocator.Get(), nullptr);
	return S_OK;
}
```

```c:DX12Renderer.h
#pragma once

#include <Windows.h>
#include <d3d12.h>
#include <d3d12shader.h>
#include <dxgi1_4.h>
#include <d3dcompiler.h>
#include <DirectXMath.h>
#include <wrl/client.h>


using namespace DirectX;
using namespace Microsoft::WRL;

class DX12Renderer {
public:
	static constexpr int RTV_NUM = 2;

public:
	DX12Renderer(HWND hwnd, int window_width, int window_height);
	~DX12Renderer();
	HRESULT Render();

private:
	HWND	_window_hwnd;
	int		_window_width;
	int		_window_height;

	HANDLE	_fence_event;

	ComPtr<ID3D12Device> _device;
	ComPtr<ID3D12CommandQueue> _command_queue;
	ComPtr<ID3D12CommandAllocator> _command_allocator;
	ComPtr<ID3D12GraphicsCommandList> _command_list;
	ComPtr<IDXGISwapChain3> _swap_chain;
	ComPtr<ID3D12Fence>  _queue_fence;
	ComPtr<IDXGIFactory3> _factory;
	ComPtr<ID3D12DescriptorHeap> _descriptor_heap;
	ComPtr<ID3D12Resource> _render_target[2];
	D3D12_CPU_DESCRIPTOR_HANDLE _rtv_handle[2];

	void SetResourceBarrier(ID3D12GraphicsCommandList* commandList, ID3D12Resource* resource, D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after);
	void WaitForCommandQueue(ID3D12CommandQueue* pCommandQueue);
};
```

■Setp6：test01.cppの編集
---
test01.cppからDX12Rendererを呼び出すよう修正します。

```c++:test01.cpp
#include	<Windows.h>
#include	<tchar.h>
#include	"DX12Renderer.h"  //<追加>

#define	WINDOW_WIDTH  720
#define	WINDOW_HEIGHT 480

LRESULT CALLBACK	WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
#define	CLASS_NAME	"CLASS TEST01"
#define	PROC_NAME	"test01"

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst, LPTSTR lpCmdLine, int nCmdShow)
{
	HWND			hwnd;
	MSG				msg;
	if (!hPreInst) {
		WNDCLASS	my_prog;
		my_prog.style			= CS_HREDRAW | CS_VREDRAW;
		my_prog.lpfnWndProc		= WndProc;
		my_prog.cbClsExtra		= 0;
		my_prog.cbWndExtra		= 0;
		my_prog.hInstance		= hInstance;
		my_prog.hIcon			= NULL;
		my_prog.hCursor			= LoadCursor(NULL, IDC_ARROW);
		my_prog.hbrBackground	= NULL;
		my_prog.lpszMenuName	= NULL;
		my_prog.lpszClassName	= _T(CLASS_NAME);

		if (!RegisterClass(&my_prog)) {
			return FALSE;
		}
	}

	RECT rect = {
		(LONG)0, 
		(LONG)0, 
		(LONG)(WINDOW_WIDTH), 
		(LONG)(WINDOW_HEIGHT)
	};
	AdjustWindowRect(
		&rect,                                    // クライアント矩形
		WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION,  // ウィンドウスタイル
		FALSE                                     // メニューフラグ
	);
	hwnd = CreateWindow(_T(CLASS_NAME),
		_T(PROC_NAME),
		WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		rect.right - rect.left,           // ウィンドウの幅
		rect.bottom - rect.top,           // ウィンドウの高さ
		NULL,
		NULL,
		hInstance,
		NULL);

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	DX12Renderer*	renderer = new DX12Renderer(hwnd, WINDOW_WIDTH, WINDOW_HEIGHT);	//初期化<追加>
	do {
		if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		renderer->Render();	//描画 <追加>
	} while (msg.message != WM_QUIT);
	delete	renderer;	//消去<追加>
	return (int)(msg.wParam);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return(DefWindowProc(hwnd, msg, wParam, lParam));
	}
	return (0L);
}
```

■Step7：ライブラリの追加
---
そのままではDX12はリンクしていないのでライブラリを追加します。
「test01」を右クリックしてサブメニューがでたら「プロパティ（R)」を選択してください。
![2018-12-06 (12).png](https://qiita-image-store.s3.amazonaws.com/0/122121/cca1e48e-d55c-e083-46cd-4110400fe32f.png)
「リンカー」「入力」の「追加の依存ファイル」にライブラリを追加します。
（このときX64になっていることを確認してください）
![2018-12-06 (13).png](https://qiita-image-store.s3.amazonaws.com/0/122121/d45dc808-3dff-13ee-f0ac-03d808e51d21.png)
「d3d12.lib」「dxgi.lib」「dxguid.lib」を追加し「OK」を押します。・
![2018-12-06 (14).png](https://qiita-image-store.s3.amazonaws.com/0/122121/6d453fa3-242f-be70-2bca-810bc71ff49b.png)

■Step8：実行
---
実行すると以下のようになります。
![2018-12-06 (15).png](https://qiita-image-store.s3.amazonaws.com/0/122121/43419e60-6699-7c6f-46a8-321fda9ca46b.png)