C++でDirectX12を使って三角形を描画しよう

DirectX12で三角形を出す覚書
「C++を使ってDirectX12を初期化」を参照
https://qiita.com/sanoh/items/11b339daf2ff3a4d5e88

■Setp1:DX12Renderer.cppの編集
---
DX12Rendererに、頂点情報とシェーダーを登録して表示するように修正

```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 stride_handle_bytes = _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 * stride_handle_bytes;
		_device->CreateRenderTargetView(_render_target[i].Get(), nullptr, _rtv_handle[i]);
	}
	//	リソースの初期化
	ResourceSetup();
}

DX12Renderer::~DX12Renderer() {
	if (_g_vertex_shader.binaryPtr) { 
		free(_g_vertex_shader.binaryPtr);
	}
	if (_g_pixel_shader.binaryPtr) { 
		free(_g_pixel_shader.binaryPtr); 
	}
	CloseHandle(_fence_event);
}

BOOL	DX12Renderer::ResourceSetup() {
	HRESULT hr;
	//	頂点情報
	MyVertex vertices_array[] = {
		{ 0.0f, 1.0f, 0.0f },
		{ -1.0f,-1.0f, 0.0 },
		{ +1.0f,-1.0f, 0.0 },
	};
	// PipelineStateのための RootSignature の作成.
	D3D12_ROOT_SIGNATURE_DESC desc_root_signature;
	ZeroMemory(&desc_root_signature, sizeof(desc_root_signature));

	desc_root_signature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
	ComPtr<ID3DBlob> root_sig_blob, error_blob;
	hr = D3D12SerializeRootSignature(&desc_root_signature, D3D_ROOT_SIGNATURE_VERSION_1, root_sig_blob.GetAddressOf(), error_blob.GetAddressOf());
	hr = _device->CreateRootSignature(0, root_sig_blob->GetBufferPointer(), root_sig_blob->GetBufferSize(), IID_PPV_ARGS(_root_signature.GetAddressOf()));
	if (FAILED(hr)) {
		return FALSE;
	}

	// コンパイル済みシェーダーの読み込み.
	FILE* fpVS = nullptr;
	fopen_s(&fpVS, "VertexShader.cso", "rb");
	if (!fpVS) { 
		return FALSE;
	}
	fseek(fpVS, 0, SEEK_END);
	_g_vertex_shader.size = ftell(fpVS); 
	rewind(fpVS);
	_g_vertex_shader.binaryPtr = malloc(_g_vertex_shader.size);
	fread(_g_vertex_shader.binaryPtr, 1, _g_vertex_shader.size, fpVS);
	fclose(fpVS); 
	fpVS = nullptr;
	FILE* fpPS = nullptr;
	fopen_s(&fpPS, "PixelShader.cso", "rb");
	if (!fpPS) { 
		return FALSE;
	}
	fseek(fpPS, 0, SEEK_END);
	_g_pixel_shader.size = ftell(fpPS); 
	rewind(fpPS);
	_g_pixel_shader.binaryPtr = malloc(_g_pixel_shader.size);
	fread(_g_pixel_shader.binaryPtr, 1, _g_pixel_shader.size, fpPS);
	fclose(fpPS); 
	fpPS = nullptr;

	// 今回のための頂点レイアウト.
	D3D12_INPUT_ELEMENT_DESC desc_input_elements[] = {
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
	};
	// PipelineStateオブジェクトの作成.
	D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline_state;
	D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_state;
	ZeroMemory(&desc_pipeline_state, sizeof(desc_pipeline_state));
	desc_pipeline_state.VS.pShaderBytecode	= _g_vertex_shader.binaryPtr;
	desc_pipeline_state.VS.BytecodeLength	= _g_vertex_shader.size;
	desc_pipeline_state.PS.pShaderBytecode	= _g_pixel_shader.binaryPtr;
	desc_pipeline_state.PS.BytecodeLength	= _g_pixel_shader.size;
	desc_pipeline_state.SampleDesc.Count	= 1;
	desc_pipeline_state.SampleMask			= UINT_MAX;
	desc_pipeline_state.InputLayout.pInputElementDescs = desc_input_elements;
	desc_pipeline_state.InputLayout.NumElements = _countof(desc_input_elements);
	desc_pipeline_state.pRootSignature		= _root_signature.Get();
	desc_pipeline_state.NumRenderTargets	= 1;
	desc_pipeline_state.RTVFormats[0]		= DXGI_FORMAT_R8G8B8A8_UNORM;
	desc_pipeline_state.PrimitiveTopologyType		= D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
	desc_pipeline_state.RasterizerState.CullMode	= D3D12_CULL_MODE_NONE;
	desc_pipeline_state.RasterizerState.FillMode	= D3D12_FILL_MODE_SOLID;
	desc_pipeline_state.RasterizerState.DepthClipEnable		= TRUE;
	desc_pipeline_state.RasterizerState.ConservativeRaster	= D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
	for (int i = 0; i < _countof(desc_state.BlendState.RenderTarget); ++i) {
		desc_pipeline_state.BlendState.RenderTarget[i].BlendEnable	= FALSE;
		desc_pipeline_state.BlendState.RenderTarget[i].SrcBlend		= D3D12_BLEND_ONE;
		desc_pipeline_state.BlendState.RenderTarget[i].DestBlend	= D3D12_BLEND_ZERO;
		desc_pipeline_state.BlendState.RenderTarget[i].BlendOp		= D3D12_BLEND_OP_ADD;
		desc_pipeline_state.BlendState.RenderTarget[i].SrcBlendAlpha	= D3D12_BLEND_ONE;
		desc_pipeline_state.BlendState.RenderTarget[i].DestBlendAlpha	= D3D12_BLEND_ZERO;
		desc_pipeline_state.BlendState.RenderTarget[i].BlendOpAlpha = D3D12_BLEND_OP_ADD;
		desc_pipeline_state.BlendState.RenderTarget[i].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
	}
	desc_pipeline_state.DepthStencilState.DepthEnable = FALSE;

	hr = _device->CreateGraphicsPipelineState(&desc_pipeline_state, IID_PPV_ARGS(_pipeline_state.GetAddressOf()));
	if (FAILED(hr)) {
		return FALSE;
	}

	// 頂点データの作成.
	D3D12_HEAP_PROPERTIES heap_props;
	D3D12_RESOURCE_DESC   desc_resource;
	ZeroMemory(&heap_props, sizeof(heap_props));
	ZeroMemory(&desc_resource, sizeof(desc_resource));
	heap_props.Type					= D3D12_HEAP_TYPE_UPLOAD;
	heap_props.CPUPageProperty		= D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
	heap_props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
	heap_props.CreationNodeMask		= 0;
	heap_props.VisibleNodeMask		= 0;
	desc_resource.Dimension			= D3D12_RESOURCE_DIMENSION_BUFFER;
	desc_resource.Width				= sizeof(vertices_array);
	desc_resource.Height			= 1;
	desc_resource.DepthOrArraySize	= 1;
	desc_resource.MipLevels			= 1;
	desc_resource.Format			= DXGI_FORMAT_UNKNOWN;
	desc_resource.Layout			= D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
	desc_resource.SampleDesc.Count	= 1;

	hr = _device->CreateCommittedResource(
		&heap_props,
		D3D12_HEAP_FLAG_NONE,
		&desc_resource,
		D3D12_RESOURCE_STATE_GENERIC_READ,
		nullptr,
		IID_PPV_ARGS(_vertex_buffer.GetAddressOf())
	);
	if (FAILED(hr)) {
		return FALSE;
	}
	if (!_vertex_buffer) {
		return FALSE;
	}
	// 頂点データの書き込み
	void* mapped = nullptr;
	hr = _vertex_buffer->Map(0, nullptr, &mapped);
	if (SUCCEEDED(hr)) {
		memcpy(mapped, vertices_array, sizeof(vertices_array));
		_vertex_buffer->Unmap(0, nullptr);
	}
	if (FAILED(hr)) {
		return FALSE;
	}
	_buffer_position.BufferLocation	= _vertex_buffer->GetGPUVirtualAddress();
	_buffer_position.StrideInBytes	= sizeof(MyVertex);
	_buffer_position.SizeInBytes	= sizeof(vertices_array);

	return TRUE;
}

void DX12Renderer::SetResourceBarrier(ID3D12GraphicsCommandList* in_command_list, ID3D12Resource* in_resource, D3D12_RESOURCE_STATES in_before, D3D12_RESOURCE_STATES in_after)
{
	D3D12_RESOURCE_BARRIER desc_barrier;
	ZeroMemory(&desc_barrier, sizeof(desc_barrier));
	desc_barrier.Type					= D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
	desc_barrier.Transition.pResource	= in_resource;
	desc_barrier.Transition.Subresource	= D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
	desc_barrier.Transition.StateBefore	= in_before;
	desc_barrier.Transition.StateAfter	= in_after;
	in_command_list->ResourceBarrier(1, &desc_barrier);
}

void DX12Renderer::WaitForCommandQueue(ID3D12CommandQueue* in_command_queue) {
	static UINT64 frames = 0;
	_queue_fence->SetEventOnCompletion(frames, _fence_event);
	in_command_queue->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);	
	//-------------------------------------------
	//	
	D3D12_RECT rect = { 0, 0, _window_width, _window_height };
	_command_list->RSSetScissorRects(1, &rect);
	_command_list->OMSetRenderTargets(1, &_rtv_handle[targetIndex], TRUE, nullptr);

	// 頂点データをセット.
	_command_list->SetGraphicsRootSignature(_root_signature.Get());
	//	シェーダー設定
	_command_list->SetPipelineState(_pipeline_state.Get());
	_command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	_command_list->IASetVertexBuffers(0, 1, &_buffer_position);
	_command_list->DrawInstanced(3, 1, 0, 0);
	//-------------------------------------------
	// 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	<d3dcompiler.h>
#include	<dxgi1_4.h>
#include	<DirectXMath.h>
#include	<wrl/client.h>

using namespace DirectX;
using namespace Microsoft::WRL;

#ifndef _countof
#define _countof __crt_countof
#endif
// 今回のための頂点レイアウト.
struct MyVertex {
	float x, y, z;
};

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);
private:
	struct ShaderObject {
		void* binaryPtr;
		int   size;
	};
	ComPtr<ID3D12RootSignature> _root_signature;
	ComPtr<ID3D12PipelineState> _pipeline_state;
	ShaderObject _g_vertex_shader;
	ShaderObject _g_pixel_shader;
	ComPtr<ID3D12Resource>		_vertex_buffer;
	D3D12_VERTEX_BUFFER_VIEW	_buffer_position;

	BOOL	ResourceSetup();
};
```
■Step2:頂点シェーダーの作成
---
VertexShader.hlslを作成します。
「test02」を右クリックするとサブメニューが出てくるので「追加（D)」「新しい項目（W)」を選択します。
![2018-12-07 (1).png](https://qiita-image-store.s3.amazonaws.com/0/122121/615e9adf-580e-c34a-8dfd-374e25526871.png)
「HLSL」の「頂点シェーダーファイル（.hlsl）」を選択します。名前は「VertexShader.hlsl」
![2018-12-09 (3).png](https://qiita-image-store.s3.amazonaws.com/0/122121/3caf10b5-bb14-9650-cc0f-812bd844c59e.png)


```:VertexShader.hlsl
float4 main(float4 pos : POSITION) : SV_POSITION
{
	return pos;
}
```
![2018-12-07 (2).png](https://qiita-image-store.s3.amazonaws.com/0/122121/a21923d9-1eaf-1b4d-952d-7bbe8eab851c.png)
このままではエラーになってしまうので。プロパティーを開きます。
![2018-12-07 (3).png](https://qiita-image-store.s3.amazonaws.com/0/122121/90d6068a-7810-28f8-d5ac-23a90787e8bd.png)
まずは「全般」のなかの「シェーダー種類」を「頂点シェーダー(/vs)」に
そして今回は「シェーダーモデル」を「Shader Model 5.0(/5.0)」に設定してください。
![2018-12-07 (14).png](https://qiita-image-store.s3.amazonaws.com/0/122121/2cb7b750-c970-fb19-c43c-abcbaffe2c05.png)
実行時に「VertexShader.hlsl」は「VertexShader.cso」にコンバートされるので出力先を設定します。
![2018-12-07 (5).png](https://qiita-image-store.s3.amazonaws.com/0/122121/d942bd09-4e3c-6fb4-7407-8e2e6ed1a124.png)

```
$(OutDir)..\..\%(Filename).cso
```
を設定し「OK」を押してください。

■Step3:ピクセルシェーダーの作成
---
VertexShader.hlslと同様にPixelShader.hlslを作成します。
![2018-12-10.png](https://qiita-image-store.s3.amazonaws.com/0/122121/a016ac88-0a82-87eb-fa3f-59de39e24e11.png)

```:PixelShader.hlsl
float4 main() : SV_TARGET
{
	return float4(1.0, 1.0, 1.0, 1.0);
}
```
こちらもプロパティーを開き
まずは「全般」のなかの「シェーダー種類」を「ピクセルイェーダー(/ps)」に
そして「シェーダーモデル」を「Shader Model 5.0(/5.0)」に設定してください。
![2018-12-07 (9).png](https://qiita-image-store.s3.amazonaws.com/0/122121/2425dc62-2ec9-37ed-3ab1-b38be21d07e5.png)
コンパイルごの出力パスも
![2018-12-07 (10).png](https://qiita-image-store.s3.amazonaws.com/0/122121/615ff955-cce2-0b6e-8f5c-6d8f5596dc5b.png)

```
$(OutDir)..\..\%(Filename).cso
```
を設定し「OK」を押してください。

■Step4:実行
---
実行すると、シェーダーもコンパイルされこんな感じです。
![2018-12-10 (1).png](https://qiita-image-store.s3.amazonaws.com/0/122121/72a1813d-0421-1019-f3a0-056eec3a6ecc.png)