サイトアイコン GAMEWORKS LAB

【DirectX12】DirectX12の初期化

DirectX12の事前準備

事前準備はVisualStudioをインストールするだけです!

DirectX11に引き続きDirectX12もWindowsSDKに含まれています。
なのでVisualStudioさえあれば特にインストールなどを行う必要はありません!

前提条件

今回はComPtrというものを使用します。
詳細は割愛しますが、参照が無くなった時点で自動で解放してくれる機能です。
所謂スマートポインターです!

ComPtrという名前からも分かる通りCOMコンポーネント向けの機能となります。

これを使用するために下記の記述を行っています。

#include <wrl.h>

using Microsoft::WRL::ComPtr;
using namespace DirectX;

この用に記載することで下記のようにCOMオブジェクトのポインタを扱えます。

ComPtr<ID3D12Device> pDevice;

 

DirectX12のデバイスの作成

今回はDirectX12の初期化について記述していきます。
DirectX12は初期化が少し長いので、皆さん頑張ってください…!

先ず最初にDirectX12を初期化するために下記が必要になります。

ライブラリファイル d3d12.lib
dxgi.lib
ヘッダーファイル d3d12.h
dxgi1_6.h

これらでDirectX12のライブラリとDXGIの機能を有効にします。

では早速、DirectxXでおなじみのデバイスの作成を行っていきます。

IDXGIFactory

とその前にIDXGIFactoryの作成を行っていきます。
ハードウェアに依存する低レベルな機能をサポートしています。
例えばモニターやグラフィックボードといったハードのスペックを取得できます。

UINT dxgiFactoryFlags = 0;
#if defined(_DEBUG)
{
	// デバッグレイヤーの有効化
	ComPtr<ID3D12Debug> debugController;
	if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
	{
		debugController->EnableDebugLayer();
		dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
	}
}
#endif

ComPtr<IDXGIFactory6> pFactory;
// DXGIFactory6の生成
if (FAILED(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&pFactory))))
{
	return false;
}

こうやってIDXGIFactoryを作ることで
デバイス作成時に使用するアダプターの取得などが可能になります。

又、後ほど出てくるSwapChainの作成にも必要になります。
今回は合わせてDirectX12のデバッグ機能を有効にしています。

これにより何か問題が起きた際にログを出力してくれます。

ID3D12Deviceの生成
HRESULT hr;
ComPtr<ID3D12Device> pDevice;
// D3D12デバイスの生成
D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_12_1, IID_PPV_ARGS(&pDevice));

デバイスの生成はこれだけです。
これで自動的に使用可能なアダプター(グラフィックデバイス)を指定してくれます。
※第一引数で任意のアダプターを選択することも可能です。

 

描画制御用のコマンドバッファ作成

これまでのDirectXでは殆ど内部に隠蔽されていましたが、
DirectxX12からは自分で直接コマンドを作成して制御する形になっています。

DirectX11の流れ
  1. DeviceContextでリソースのバインドや描画のコマンドを作成
  2. DirectXが特定のタイミングに自動でGPUへコマンドの実行依頼(途中であれば1に戻る)
  3. SwapChainのPresent(Vシンク待ち)やQueryコマンドで描画待ち

※2は自分で手動で行う方法もあります。

DirectX12の流れ
  1. コマンドリストの内容リセット
  2. コマンドリストに対してリソースのバインドや描画コマンドの作成
  3. 自分でGPUに対してコマンドの実行依頼(複数のコマンドリストがある場合は1に戻る)
  4. SwapChainで画面のフリップ
  5. Fenceを使ってGPU待ちを行う

 

こんな感じに変わっています。
DirectX12では流れが増えてしまった感じもあります。
ただ実はコマンドリストは並列化(マルチスレッド)やキャッシュ(事前に作成)が行えます。

その辺りを駆使して最適化を行っていけば、CPUについてはDirectX11を上回る性能を出せるはずです!
※GPUの最適化には色々と工夫が必要のようです

では早速、コマンド制御用のオブジェクトを作成していきましょう。

ID3D12CommandQueue
ComPtr<ID3D12CommandQueue> pCommandQueue;
if (FAILED(pDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&pCommandQueue))))
{
	return false;
}

 

ID3D12CommandAllocator
ComPtr<ID3D12CommandAllocator> pCommandAllocator;
if (FAILED(pDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&pCommandAllocator))))
{
	return false;
}

 

ID3D12GraphicsCommandList
ComPtr<ID3D12GraphicsCommandList> pCommandList;
if (FAILED(pDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, pCommandAllocator.Get(), nullptr, IID_PPV_ARGS(&pCommandList))))
{
	return false;
}
// 初期がコマンドの受付状態なので閉じておく
if (FAILED(pCommandList->Close()))
{
	return false;
}

 

ID3D12Fence
ComPtr<ID3D12Fence> pFence;
if (FAILED(pDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&pFence))))

	return false;
}
HANDLE FenceEvent;
// 同期待ちするためのイベント作成
FenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (FenceEvent == nullptr && FAILED(GetLastError()))
{
	return false;
}

これでコマンドバッファを扱うためのオブジェクトが一通り揃います。

ID3D12CommandQueue GPUに対してコマンドバッファの実行依頼が行えます。
ID3D12CommandAllocator コマンドリストのメモリ確保を管理します。
※設定によってはキャッシュに利用できます。
ID3D12GraphicsCommandList コマンドリストの生成と管理を行います。
※設定によってはキャッシュすることが可能です。
ID3D12Fence GPUと同期して実行完了待ちを行うことができます。
今回は待つ際にWindowsのEventを利用しています。

SwapChainの初期化

SwapChainとはレンダリング結果を出力するためのオブジェクトです。
紐づいたビデオアダプタやウィンドウに対してレンダリング結果を出力します。

DXGI_SWAP_CHAIN_DESC1 swapChainDesc{};
swapChainDesc.BufferCount = FrameCount;
swapChainDesc.Width = width;
swapChainDesc.Height = height;
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swapChainDesc.SampleDesc.Count = 1;

ComPtr<IDXGISwapChain1> pSwapChain;
if (FAILED(pFactory->CreateSwapChainForHwnd(
	pCommandQueue.Get(),
	hWnd, // 表示するウィンドウのハンドル
	&swapChainDesc,
	nullptr,
	nullptr,
	&pSwapChain
)))
{
	return false;
}

これでSwapChainが作成できます。
細かな設定は次の通りです。

DXGI_SWAP_CHAIN_DESC1
Width バックバッファの幅
Height バックバッファの高さ
Format バックバッファのフォーマット
Stereo 立体視の有無を指定します。
SampleDesc MSAAを適応するための設定を行います。
ハードウェアに応じて使用できる品質が異なります。
なので使用する場合はデバイスで使用可能な性能を調べてから設定します。
BufferUsage バッファの使用用途を設定します。
今回はシェーダからの出力先として使用するの下記を指定します。
DXGI_USAGE_RENDER_TARGET_OUTPUT
BufferCount バックバッファの数
Scaling バッファのスケーリング方法を設定します。
詳細はDXGI_SCALINGを確認してください。
SwapEffect フロントバッファとバックバッファの入れ替え方法を設定できます。
詳細はDXGI_SWAP_EFFECTを確認してください。
AlphaMode フレームバッファのα地の扱いを指定できます。
詳細はDXGI_ALPHA_MODEを確認してください。
Flags スワップチェインの設定フラグを指定します。
詳細はDXGI_SWAP_CHAIN_FLAGを確認してください。

 

今回の記事に記載した一通りを実装すれば、先ずはDirectX12の実装を進めていく準備ができます。
画面に絵を出すにはまだ少し先が長いので、気長に付き合っていただければと思います。

 

サンプルプログラム

今回のDirectX12の初期化を行ったプロジェクトをアップしています。
必要に応じて参考にしてください。

Download

モバイルバージョンを終了