DirectX12 ② 初期化
DirectX12の初期化
言語 : C++
開発環境 : Visual Studio 2020
OS : Windows10
CPU : AMD Ryzen5 3600
RAM : 24.0GB
GPU : NVIDIA GeForce GTX 1050
画面のクリアまで
ヘッダファイル
Dx.h
#pragma once #include <dxgi1_4.h> #include <d3d12.h> #include <wrl/client.h> #include <cstdint> #include <iostream> #include <vector> #pragma comment(lib, "d3d12.lib") #pragma comment(lib, "dxgi.lib") using namespace Microsoft::WRL; constexpr uint32_t FRAME_BUFFER_COUNT = 2; class Dx { public: Dx(); bool Init(uint32_t width, uint32_t height, HWND hwnd); void Terminate(); void BeginRender(); void EndRender(); void ResetCmdList(); void ExcuteCmdList(); ID3D12Device* GetDevice(); ID3D12GraphicsCommandList* GetCmdList(); uint32_t GetWindowWidth(); uint32_t GetWindowHeight(); uint32_t GetCurrentBackBufferIndex(); private: void Present(uint32_t interval); void WaitGPU(); bool CreateFactory(); bool CreateDevice(); bool CreateCommandQueue(); bool CreateSwapChain(); bool CreateCommandAllocator(); bool CreateCommandList(); bool CreateRenderTarget(); bool CreateFence(); bool CreateDepthStencil(); void CreateViewPort(); void CreateScissorRect(); private: HWND _hwnd; uint32_t _width, _height; //ウィンドウの高さと幅 ComPtr<IDXGIFactory4> _pFactory; //ファクトリー ComPtr<ID3D12Device> _pDevice; //デバイス ComPtr<ID3D12CommandQueue> _pCmdQueue; //コマンドキュー ComPtr<IDXGISwapChain3> _pSwapChain; //スワップチェイン ComPtr<ID3D12Resource> _pRenderTargets[FRAME_BUFFER_COUNT]; //レンダーターゲット ComPtr<ID3D12CommandAllocator> _pCmdAllocator[FRAME_BUFFER_COUNT]; //コマンドアロケーター ComPtr<ID3D12GraphicsCommandList> _pCmdList; //コマンドリスト ComPtr<ID3D12DescriptorHeap> _pHeapRTV; //レンダーターゲット用のディスクリプタヒープ ComPtr<ID3D12RootSignature> _pRootSignature; //ルートシグネチャ ComPtr<ID3D12PipelineState> _pPipelineState; //パイプラインステート ComPtr<ID3D12Fence> _pFence; //フェンス ComPtr<ID3D12DescriptorHeap> _pHeapDSV; //デプスステンシルビュー用のディスクリプタヒープ ComPtr<ID3D12Resource> _pDepthStencilBuffer; //デプスステンシルバッファ uint32_t _currentBackBufferIndex; //バックバッファーのインデックス HANDLE _fenceEvent; //フェンスイベント uint64_t _fenceCount[FRAME_BUFFER_COUNT]; //フェンスカウント D3D12_CPU_DESCRIPTOR_HANDLE _handleRTV[FRAME_BUFFER_COUNT]; //レンダーターゲットビューのハンドル D3D12_CPU_DESCRIPTOR_HANDLE _handleDSV; //デプスステンシルビューのハンドル D3D12_VIEWPORT _viewPort; D3D12_RECT _scissorRect; }; extern Dx* dx; //グローバル変数
コンストラクタと初期化関数
Dx.cpp
Dx* dx; Dx::Dx() { _hwnd = nullptr; _currentBackBufferIndex = 0; _fenceEvent = nullptr; _handleDSV = {}; _height = 0; _width = 0; _scissorRect = {}; _viewPort = {}; for (uint32_t i = 0; i < FRAME_BUFFER_COUNT; i++) { _fenceCount[i] = 0; _handleRTV[i] = {}; } } bool Dx::Init(uint32_t width, uint32_t height, HWND hwnd) { HRESULT hResult; #if defined(DEBUG) || defined(_DEBUG) { ComPtr<ID3D12Debug> debug; hResult = D3D12GetDebugInterface(IID_PPV_ARGS(debug.GetAddressOf())); if (SUCCEEDED(hResult)) { debug->EnableDebugLayer(); } } #endif _hwnd = hwnd; _width = width; _height = height; if (!CreateFactory()) { return false; } if (!CreateDevice()) { return false; } if (!CreateCommandQueue()) { return false; } if (!CreateSwapChain()) { return false; } if (!CreateCommandAllocator()) { return false; } if (!CreateCommandList()) { return false; } if (!CreateRenderTarget()) { return false; } if (!CreateDepthStencil()) { return false; } if (!CreateFence()) { return false; } CreateViewPort(); CreateScissorRect(); _pCmdList->Close(); return true; }
デバイスの作成(NVIDIAのグラボを使用する場合)
Dx.cpp
//DXGIファクトリーの作成 bool Dx::CreateFactory() { HRESULT hResult; hResult = CreateDXGIFactory1(IID_PPV_ARGS(_pFactory.GetAddressOf())); if (FAILED(hResult)) { std::cout << "Failed to create DXGIFactory" << std::endl; return false; } return true; } //デバイスの作成 bool Dx::CreateDevice() { HRESULT hResult; //アダプター列挙 std::vector <IDXGIAdapter*> adapters; IDXGIAdapter* tmpAdapter = nullptr; for (uint32_t i = 0; _pFactory->EnumAdapters(i, &tmpAdapter) != DXGI_ERROR_NOT_FOUND; ++i) { adapters.push_back(tmpAdapter); } //アダプターの選択 for (auto adpt : adapters) { DXGI_ADAPTER_DESC adesc = {}; adpt->GetDesc(&adesc); std::wstring strDesc = adesc.Description; //NVIDIAのグラボを探す if (strDesc.find(L"NVIDIA") != std::string::npos) { tmpAdapter = adpt; break; } } //フィーチャレベル列挙 D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, }; //デバイスの作成 //フィーチャーレベルを探す for (D3D_FEATURE_LEVEL l : levels) { hResult = D3D12CreateDevice(tmpAdapter, l, IID_PPV_ARGS(&_pDevice)); if (hResult == S_OK) { break; } } if (hResult != S_OK) { std::cout << "Failed to create Device" << std::endl; return false; } tmpAdapter->Release(); //使い終わったらリリース return true; }
デバイスの作成(グラボを指定しない場合)
Dx.cpp
//DXGIファクトリーの作成 bool Dx::CreateFactory() { HRESULT hResult; hResult = CreateDXGIFactory1(IID_PPV_ARGS(_pFactory.GetAddressOf())); if (FAILED(hResult)) { std::cout << "Failed to create DXGIFactory" << std::endl; return false; } return true; } //デバイスの作成 bool Dx::CreateDevice() { HRESULT hResult; //フィーチャレベル列挙 D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, }; //デバイスの作成 //フィーチャーレベルを探す for (D3D_FEATURE_LEVEL l : levels) { hResult = D3D12CreateDevice(nullptr, l, IID_PPV_ARGS(&_pDevice)); if (hResult == S_OK) { break; } } if (hResult != S_OK) { std::cout << "Failed to create Device" << std::endl; return false; } return true; }
コマンドキューの作成
Dx.cpp
//コマンドキューの作成 bool Dx::CreateCommandQueue() { D3D12_COMMAND_QUEUE_DESC cmdQueueDesc = {}; cmdQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; cmdQueueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL; cmdQueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; cmdQueueDesc.NodeMask = 0; HRESULT hResult = _pDevice->CreateCommandQueue(&cmdQueueDesc, IID_PPV_ARGS(_pCmdQueue.GetAddressOf())); if (FAILED(hResult)) { std::cout << "Failed to create CommnadQueue" << std::endl; return false; } return true; }
スワップチェインの作成
Dx.cpp
//スワップチェインの作成 bool Dx::CreateSwapChain() { DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; swapChainDesc.BufferDesc.Width = _width; swapChainDesc.BufferDesc.Height = _height; swapChainDesc.BufferDesc.RefreshRate.Numerator = 60; swapChainDesc.BufferDesc.RefreshRate.Denominator = 1; swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.BufferCount = FRAME_BUFFER_COUNT; swapChainDesc.OutputWindow = _hwnd; swapChainDesc.Windowed = TRUE; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; ComPtr<IDXGISwapChain> swapChain; HRESULT hResult = _pFactory->CreateSwapChain(_pCmdQueue.Get(), &swapChainDesc, swapChain.GetAddressOf()); if (FAILED(hResult)) { std::cout << "Failed to create IDXGISwapChain" << std::endl; return false; } //IDXGISwapChain3を取得 hResult = swapChain.As(&_pSwapChain); if (FAILED(hResult)) { std::cout << "Failed to get IDXGISwapChain3" << std::endl; return false; } //バックバッファ番号を取得 _currentBackBufferIndex = _pSwapChain->GetCurrentBackBufferIndex(); swapChain.Reset(); //解放 return true; }
コマンドアロケータの作成
Dx.cpp
//コマンドアロケータの作成 bool Dx::CreateCommandAllocator() { HRESULT hResult; for (uint32_t i = 0; i < FRAME_BUFFER_COUNT; i++) { hResult = _pDevice->CreateCommandAllocator( D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(_pCmdAllocator[i].GetAddressOf())); if (FAILED(hResult)) { std::cout << "Failed to create CommandAllocator" << std::endl; return false; } } return true; }
コマンドリストの作成
Dx.cpp
//コマンドリストの作成 bool Dx::CreateCommandList() { HRESULT hResult = _pDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, _pCmdAllocator[_currentBackBufferIndex].Get(), nullptr, IID_PPV_ARGS(_pCmdList.GetAddressOf())); if (FAILED(hResult)) { std::cout << "Failed to create CommandList" << std::endl; return false; } return true; }
レンダーターゲットビューの作成
Dx.cpp
//レンダーターゲットビューの作成 bool Dx::CreateRenderTarget() { //ディスクリプタヒープの作成 D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {}; heapDesc.NumDescriptors = FRAME_BUFFER_COUNT; heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; heapDesc.NodeMask = 0; HRESULT hResult = _pDevice->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(_pHeapRTV.GetAddressOf())); if (FAILED(hResult)) { std::cout << "Failed to create DescriptorHeap" << std::endl; return false; } D3D12_CPU_DESCRIPTOR_HANDLE handle = _pHeapRTV->GetCPUDescriptorHandleForHeapStart(); uint32_t incrementSize = _pDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); for (uint32_t i = 0; i < FRAME_BUFFER_COUNT; i++) { hResult = _pSwapChain->GetBuffer(i, IID_PPV_ARGS(_pRenderTargets[i].GetAddressOf())); if (FAILED(hResult)) { std::cout << "Failed to get RenderTargets" << std::endl; return false; } D3D12_RENDER_TARGET_VIEW_DESC viewDesc = {}; viewDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; viewDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; viewDesc.Texture2D.MipSlice = 0; viewDesc.Texture2D.PlaneSlice = 0; _pDevice->CreateRenderTargetView(_pRenderTargets[i].Get(), &viewDesc, handle); _handleRTV[i] = handle; handle.ptr += incrementSize; } return true; }
フェンスの作成
Dx.cpp
//フェンスの作成 bool Dx::CreateFence() { for (uint32_t i = 0; i < FRAME_BUFFER_COUNT; i++) { _fenceCount[i] = 0; } HRESULT hResult = _pDevice->CreateFence(_fenceCount[_currentBackBufferIndex], D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(_pFence.GetAddressOf())); if (FAILED(hResult)) { std::cout << "Failed to create Fence" << std::endl; return false; } _fenceCount[_currentBackBufferIndex]++; //イベントの作成 _fenceEvent = CreateEvent(nullptr, false, false, nullptr); if (_fenceEvent == nullptr) { return false; } return true; }
デプスステンシルビューの作成
Dx.cpp
//デプスステンシルビューの作成 bool Dx::CreateDepthStencil() { D3D12_HEAP_PROPERTIES heapProp = {}; heapProp.Type = D3D12_HEAP_TYPE_DEFAULT; heapProp.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; heapProp.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; heapProp.CreationNodeMask = 1; heapProp.VisibleNodeMask = 1; D3D12_RESOURCE_DESC desc = {}; desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; desc.Alignment = 0; desc.Width = _width; desc.Height = _height; desc.DepthOrArraySize = 1; desc.MipLevels = 1; desc.Format = DXGI_FORMAT_D32_FLOAT; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; D3D12_CLEAR_VALUE clearValue; clearValue.Format = DXGI_FORMAT_D32_FLOAT; clearValue.DepthStencil.Depth = 1.0f; clearValue.DepthStencil.Stencil = 0; HRESULT hResult = _pDevice->CreateCommittedResource(&heapProp, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &clearValue, IID_PPV_ARGS(_pDepthStencilBuffer.GetAddressOf())); if (FAILED(hResult)) { std::cout << "Failed to create DepthStencilBuffer" << std::endl; return false; } D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {}; heapDesc.NumDescriptors = 1; heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV; heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; heapDesc.NodeMask = 0; hResult = _pDevice->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&_pHeapDSV)); if (FAILED(hResult)) { std::cout << "Failed to create DescriptorHeap for DepthStencilView " << std::endl; return false; } D3D12_CPU_DESCRIPTOR_HANDLE handle = _pHeapDSV->GetCPUDescriptorHandleForHeapStart(); uint32_t incrementSize = _pDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV); D3D12_DEPTH_STENCIL_VIEW_DESC viewDesc = {}; viewDesc.Format = DXGI_FORMAT_D32_FLOAT; viewDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; viewDesc.Texture2D.MipSlice = 0; viewDesc.Flags = D3D12_DSV_FLAG_NONE; _pDevice->CreateDepthStencilView(_pDepthStencilBuffer.Get(), &viewDesc, handle); _handleDSV = handle; return true; }
ビューポート、シザー矩形の作成
Dx.cpp
//ビューポートの作成 void Dx::CreateViewPort() { _viewPort.TopLeftX = 0; _viewPort.TopLeftY = 0; _viewPort.Width = static_cast<float>(_width); _viewPort.Height = static_cast<float>(_height); _viewPort.MinDepth = 0.0f; _viewPort.MaxDepth = 1.0f; } //シザー矩形の作成 void Dx::CreateScissorRect() { _scissorRect.left = 0; _scissorRect.right = _width; _scissorRect.top = 0; _scissorRect.bottom = _height; }
終了処理
Dx.cpp
//終了処理 void Dx::Terminate() { WaitGPU(); if (_fenceEvent != nullptr) { CloseHandle(_fenceEvent); _fenceEvent = nullptr; } _pHeapRTV.Reset(); } //GPUの処理待機 void Dx::WaitGPU() { //シグナル処理 _pCmdQueue->Signal(_pFence.Get(), _fenceCount[_currentBackBufferIndex]); //イベントを設定 _pFence->SetEventOnCompletion(_fenceCount[_currentBackBufferIndex], _fenceEvent); //待機処理 WaitForSingleObjectEx(_fenceEvent, INFINITE, false); _fenceCount[_currentBackBufferIndex]++; }
レンダリング
Dx.cpp
//描画開始 void Dx::BeginRender() { //コマンドの記録開始 _pCmdAllocator[_currentBackBufferIndex]->Reset(); _pCmdList->Reset(_pCmdAllocator[_currentBackBufferIndex].Get(), nullptr); //リソースバリア D3D12_RESOURCE_BARRIER barrier = {}; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; barrier.Transition.pResource = _pRenderTargets[_currentBackBufferIndex].Get(); barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; _pCmdList->ResourceBarrier(1, &barrier); //レンダーターゲットの設定 _pCmdList->OMSetRenderTargets(1, &_handleRTV[_currentBackBufferIndex], false, &_handleDSV); //クリアカラー(赤) float clearColor[] = { 1.0f, 0.0f, 0.0f, 1.0f }; //レンダーターゲットビューをクリア _pCmdList->ClearRenderTargetView(_handleRTV[_currentBackBufferIndex], clearColor, 0, nullptr); //デプスステンシルバッファクリア _pCmdList->ClearDepthStencilView(_handleDSV, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); //ビューポートとシザー矩形をセット _pCmdList->RSSetViewports(1, &_viewPort); _pCmdList->RSSetScissorRects(1, &_scissorRect); } //描画終了 void Dx::EndRender() { //リソースバリア D3D12_RESOURCE_BARRIER barrier = {}; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; barrier.Transition.pResource = _pRenderTargets[_currentBackBufferIndex].Get(); barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; _pCmdList->ResourceBarrier(1, &barrier); //コマンドリストを閉じる _pCmdList->Close(); //コマンドを実行 ID3D12CommandList* ppCmdLists[] = { _pCmdList.Get() }; _pCmdQueue->ExecuteCommandLists(1, ppCmdLists); //画面に表示 Present(1); } //コマンドリストのリセット void Dx::ResetCmdList() { //_pCmdAllocator->Reset(); _pCmdList->Reset(_pCmdAllocator->Get(), nullptr); } //コマンドリストの実行 void Dx::ExcuteCmdList() { ID3D12CommandList* ppCmdLists[] = { _pCmdList.Get() }; _pCmdQueue->ExecuteCommandLists(1, ppCmdLists); //シグナル処理 uint64_t currentValue = _fenceCount[_currentBackBufferIndex]; _pCmdQueue->Signal(_pFence.Get(), currentValue); if (_pFence->GetCompletedValue() != currentValue) { auto event = CreateEvent(nullptr, false, false, nullptr); _pFence->SetEventOnCompletion(currentValue, event); WaitForSingleObject(event, INFINITE); CloseHandle(event); } _pCmdAllocator[_currentBackBufferIndex]->Reset(); _pCmdList->Reset(_pCmdAllocator[_currentBackBufferIndex].Get(), nullptr); } //表示 void Dx::Present(uint32_t interval) { //画面に表示 _pSwapChain->Present(interval, 0); //シグナル処理 uint64_t currentValue = _fenceCount[_currentBackBufferIndex]; _pCmdQueue->Signal(_pFence.Get(), currentValue); //バックバッファ番号を更新 _currentBackBufferIndex = _pSwapChain->GetCurrentBackBufferIndex(); //描画待機 if (_pFence->GetCompletedValue() < _fenceCount[_currentBackBufferIndex]) { _pFence->SetEventOnCompletion(_fenceCount[_currentBackBufferIndex], _fenceEvent); WaitForSingleObjectEx(_fenceEvent, INFINITE, false); } _fenceCount[_currentBackBufferIndex] = currentValue + 1; }
getter関数
Dx.cpp
//デバイスの取得 ID3D12Device* Dx::GetDevice() { return _pDevice.Get(); } //コマンドリストの取得 ID3D12GraphicsCommandList* Dx::GetCmdList() { return _pCmdList.Get(); } //ウィンドウの高さの取得 uint32_t Dx::GetWindowWidth() { return _width; } //ウィンドウの幅の取得 uint32_t Dx::GetWindowHeight() { return _height; } //バックバッファのインデックスを取得 uint32_t Dx::GetCurrentBackBufferIndex() { return _currentBackBufferIndex; }
実行結果
クリアカラーで赤を指定しているため、赤い画面が表示されている。