DirectX12で三角形を出す覚書
「C++を使ってDirectX12を初期化」を参照
https://qiita.com/sanoh/items/11b339daf2ff3a4d5e88
■Setp1:DX12Renderer.cppの編集
DX12Rendererに、頂点情報とシェーダーを登録して表示するように修正
#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;
}
#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)」を選択します。
「HLSL」の「頂点シェーダーファイル(.hlsl)」を選択します。名前は「VertexShader.hlsl」
float4 main(float4 pos : POSITION) : SV_POSITION
{
return pos;
}
このままではエラーになってしまうので。プロパティーを開きます。
まずは「全般」のなかの「シェーダー種類」を「頂点シェーダー(/vs)」に
そして今回は「シェーダーモデル」を「Shader Model 5.0(/5.0)」に設定してください。
実行時に「VertexShader.hlsl」は「VertexShader.cso」にコンバートされるので出力先を設定します。
$(OutDir)..\..\%(Filename).cso
を設定し「OK」を押してください。
■Step3:ピクセルシェーダーの作成
VertexShader.hlslと同様にPixelShader.hlslを作成します。
float4 main() : SV_TARGET
{
return float4(1.0, 1.0, 1.0, 1.0);
}
こちらもプロパティーを開き
まずは「全般」のなかの「シェーダー種類」を「ピクセルイェーダー(/ps)」に
そして「シェーダーモデル」を「Shader Model 5.0(/5.0)」に設定してください。
コンパイルごの出力パスも
$(OutDir)..\..\%(Filename).cso
を設定し「OK」を押してください。