DirextX11を使用してましたがDX12もいいかなと思い、覚書
■Step1:環境構築
VisualStudio2017を使用しています。
■Step2:プロジェクト作成
VisualStudio2017を起動して「ファイル(F)」「新規作成(N)」「プロジェクト(p)」を選択します
今回はC++で構築するので「VisualC++」の「その他」から「空のプロジェクト」を選択します。
名前は適当に(今回はtest01としています)
「OK」を押しますと以下のようになります、ここで今後の為「X64」に変更しておきましょう
■Step3:test01.cppを作成
test01.cppを追加するために「ソースファイル」で右クリックするとサブメニューがでるので、そこで「追加(D)」「新しい項目(W)」を選択してください。
名前を「test01.cpp」にし「C++ファイル(.cpp)」が選択されていることを確認してから「追加(A)」を押します。
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:仮実行
■Step5:DirextX12を制御
DirectX12を制御するクラス「DX12Renderere.cpp」「DX12Renderere.h」を追加します。
#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;
}
#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を呼び出すよう修正します。
#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)」を選択してください。
「リンカー」「入力」の「追加の依存ファイル」にライブラリを追加します。
(このときX64になっていることを確認してください)
「d3d12.lib」「dxgi.lib」「dxguid.lib」を追加し「OK」を押します。・