【第1部】シンプルバージョン編
1103.シンプルな三角形の描画(3)
では、前項に続きまして
GameDevice::LoadPipeline()関数の中身を解説したいと思います。
factory
数々の
Dx12オブジェクトの中で、
factoryはちょっと変わったオブジェクトです。
factory問う名前の通り、何かを作成するために存在します。その何か、というのは
Dx12デバイスと
スワップチェーンです。この2つは
factoryを使って構築します。
factoryの構築は以下の様な記述になっています。
//ファクトリ
ComPtr<IDXGIFactory4> factory = Dx12Factory::CreateDirect();
Dx12Factoryは
ネームスペースとなっており、追いかけていくと以下の記述になります。
namespace Dx12Factory {
static inline ComPtr<IDXGIFactory4> CreateDirect() {
UINT dxgiFactoryFlags = 0;
#if defined(_DEBUG)
// Enable the debug layer (requires the Graphics Tools "optional feature").
// NOTE: Enabling the debug layer after device creation will invalidate the active device.
{
ComPtr<ID3D12Debug> debugController;
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
{
debugController->EnableDebugLayer();
// Enable additional debug layers.
dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
}
}
#endif
ComPtr<IDXGIFactory4> factory;
ThrowIfFailed(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)));
return factory;
}
}
このように、
DirectX-Graphics-Samplesの中のサンプルと同様になっています。
また、赤くなっている
ThrowIfFailed()関数について説明します。
実体は以下のようになっています。これも
DirectX-Graphics-Samplesを習ったものです。
inline void ThrowIfFailed(HRESULT hr)
{
if (FAILED(hr))
{
throw HrException(hr);
}
}
このようにシンプルなものです。
Dx12は全体的に
COMという技術を利用します。
CreateDXGIFactory2()関数などは、
COMインターフェイスを作成します。これらの関数は
HRESULTという型を返しますが、これが失敗した時は
FAILED(hr)でチェックできる形になります。つまり、関数呼び出しに失敗したら、例外をスローしなさい、という意味になります。
BaseDx12ではこれをカスタマイズした
日本語のエラー文字を送出できるバージョンもあります。以下が実体です。
inline void ThrowIfFailed(HRESULT hr,
const std::wstring& wstr1,
const std::wstring& wstr2 = std::wstring(),
const std::wstring& wstr3 = std::wstring()
)
{
if (FAILED(hr))
{
throw HrException(hr, Util::WStoRetMB(wstr1) + Util::WStoRetMB(wstr2) + Util::WStoRetMB(wstr3));
}
}
inline void ThrowIfFailed(HRESULT hr,
const std::string& str1,
const std::string& str2 = std::string(),
const std::string& str3 = std::string()
)
{
if (FAILED(hr))
{
throw HrException(hr, str1 + str2 + str3);
}
}
このように
wstring版と
string版があります。
デバイス
これまでの
DirectXのバージョンもそうでしたが、
Dx12も中心となる重要なオブジェクトは
デバイスです。
多くの命令がこの
デバイスを通じて行われます。
factoryの作成が終わりましたら、続いて作成します。以下がそのブロックです。
//デバイス
m_device = D3D12Device::CreateDefault(factory, m_useWarpDevice);
ここで、先ほど作成した
factoryを渡します。
m_useWarpDeviceというのは
ラップデバイスを使うかどうかです。
ハードウェアデバイスのみに対応するのでfaiseとなっています。
m_deviceというのは、親クラスの
Dx12Deviceクラスにある
protected変数です。
protectedなので、継承先である
GameDeviceクラスからも、直接扱えます。
その宣言は以下のようになっています。
ComPtr<ID3D12Device> m_device;
このように
ID3D12Deviceインターフェイスが
Dx12のデバイスです。
デバイスの作成は、以下の様なコードが呼ばれます。
static inline ComPtr<ID3D12Device> CreateDefault(ComPtr<IDXGIFactory4> factory, bool useWarpDevice) {
ComPtr<ID3D12Device> device;
if (useWarpDevice)
{
ComPtr<IDXGIAdapter> warpAdapter;
ThrowIfFailed(factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter)));
ThrowIfFailed(D3D12CreateDevice(
warpAdapter.Get(),
D3D_FEATURE_LEVEL_11_0,
IID_PPV_ARGS(&device)
));
}
else
{
ComPtr<IDXGIAdapter1> hardwareAdapter;
GetHardwareAdapter(factory.Get(), &hardwareAdapter);
ThrowIfFailed(D3D12CreateDevice(
hardwareAdapter.Get(),
D3D_FEATURE_LEVEL_11_0,
IID_PPV_ARGS(&device)
));
}
return device;
}
ラップモードではありませんので、赤い部分が実行されます。
ここで呼ばれている
GetHardwareAdapter()関数はサブ関数で、以下の様な内容です。
_Use_decl_annotations_
void GetHardwareAdapter(IDXGIFactory2* pFactory, IDXGIAdapter1** ppAdapter)
{
ComPtr<IDXGIAdapter1> adapter;
*ppAdapter = nullptr;
for (UINT adapterIndex = 0; DXGI_ERROR_NOT_FOUND != pFactory->EnumAdapters1(adapterIndex, &adapter); ++adapterIndex)
{
DXGI_ADAPTER_DESC1 desc;
adapter->GetDesc1(&desc);
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
{
// Don't select the Basic Render Driver adapter.
// If you want a software adapter, pass in "/warp" on the command line.
continue;
}
// Check to see if the adapter supports Direct3D 12, but don't create the
// actual device yet.
if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr)))
{
break;
}
}
*ppAdapter = adapter.Detach();
}
ここの記述は
DirectX-Graphics-Samplesのものとまるで同じですが、この部分をオリジナル化するメリットもありません。
着実にお決まりの処理をこなしてくれればいいと思います。
このようにして、作成された
ID3D12Deviceインターフェイスは
D3D12Device::CreateDefault()関数によって作成され、
GameDevice::LoadPipeline()関数に戻されます。
それでは次項では
コマンドキューの作成から続けましょう。