ここぷろ!

私がプログラミングで学んだこと、行き詰った事などを書いていきます。たまに個人的なことも載せるかも

【DirectX12】Direct3Dデバイスの作成【初期化】


 こんにちは、ここあです。

 昨日記事を上げる事が出来なかったので、今日二つ上げようと思っていたのですが、思った以上に帰ってくるのが遅くなってしまったため、明日二つ上げることにしました。

 今日はDirect3Dバイスの作成について解説していきます。

Library & Includes

#include <d3dx12.h>
#pragma comment(lib, "d3d12.lib")
#include <dxgi1_4.h>
#pragma comment(lib, "dxgi.lib")
#include <D3Dcompiler.h>
#pragma comment(lib, "d3dcompiler.lib")
#include <DirectXMath.h>

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

Direct3Dバイスの作成

 初期化の一番初めにやらなければいけない事が、Direct3Dバイスの作成です(以下D3Dデバイス)。
 Windowsには通常、複数のD3Dデバイスが存在し、GPUを複数PCに積んでいる場合には、それぞれがD3Dデバイスとなります。なので、最初にその中からどのデバイスが目的に合ったものなのかの選択を最初に行います。

DXGIファクトリの生成

 IDXGIFactory4を使用します。IDXGIFactory4は生成されたファクトリを操作するためのインタフェースクラスです。

 CreateDXGIFactory1関数DXGIファクトリを生成します。

 IID_PPV_ARGSマクロを使うことで、引数から自動的に2つのパラメータを作成してもらう事で、もしプログラムで使いたいインタフェースが変わった場合の、変更箇所を1ヶ所にすることで間違いを起こしにくくします
CreateDXGIFactory1

ComPtr<IDXGIFactory4> dxgiFactory;

// DXGIファクトリの生成
if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)))) {
    return false;
}


 バイスを列挙する為に、IDXGIFactory1::EnumAdapters1関数を使います。この関数はインターフェイスが取得できなかった場合にDXGI_ERROR_NOT_FOUNDを返すので、それが返されるまでループを回して目的のデバイスを探していきます。
 IDXGIFactory1::EnumAdapters1


 IDXGIAdapter1::GetDesc1関数バイスの情報を取得できます。ここでは、この情報を、ハードウェアではないデバイスを除外するために使用しています。
 IDXGIAdapter1::GetDesc1 method | Microsoft Docs

バイスを作成できるか確認

 ハードウェアデバイスである確認が取れたものは、D3D12CreateDevice関数実際に作成できるかどうかを調べます。この関数はインタフェースポインタを格納する変数のアドレスとして、nullptrを渡した場合、作成できるかどうかを調べることが出来ます。今回はnullptrを渡さなければならないのでIID_PPV_ARGSマクロは使えません。
 D3D12CreateDevice機能|マイクロソフトドキュメント

  • D3D12CreateDevice関数
    • 第1引数:IDXGIAdapter1インタフェースのポインタ
    • 第2引数:作成するデバイスに要求される最小限の「機能レベル」。
    • 第3引数:COMオブジェクト各インタフェース固有のGUID
    • 第4引数:デバイスへのポインタを受け取るメモリブロックへのポインタ

 機能レベルとは、GPUがどの世代のDirectXに対応しているかを示すためのものです。IDXGIAdapter1の情報がこの条件を満たさない場合、作成に失敗します。

ComPtr<IDXGIFactory1> dxgiAdapter;    //デバイス取得用
int adapterIndex  = 0;    //列挙するデバイスのインデックス
bool adapterFound = false;    //目的のデバイスを見つけたか

// 目的のデバイスを探索
while(dxgiFactory->EnumAdapters1( adapterIndex, &dxgiAdapter) != DXGI_ERROR_NOT_FOUND) {
    DXGI_ADAPTER_DESC1 desc;
    dxgiAdapter->GetDesc1(&desc);  // デバイスの情報を取得

    // ハードウェアのみ選ぶ
    if (!(desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)) {
        if (SUCCEEDED(D3D12CreateDevice(dxgiAdapter.Get(), D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device),  nullptr))) {
            adapterFound = true;
            break;
        }
    }
    ++adapterIndex;
}

これで、デバイスを見つけることが出来ていれば、デバイス作成の為の準備と確認は終わりです。

WARPバイスの作成

 ループが終わっても、作成可能デバイスが見つからなかった場合、WARPバイスの作成を試みます。
 IDXGIFactory4::EnumWarpAdapter関数を使い、使用可能なWARPバイスがあるかを調べます。
 見つからない場合、Direct3Dは使えないので、falseを返します。

 最後にD3D12CreateDevice関数の呼び出しで、実際にデバイスを作成します。

ComPtr<ID3D12Device> g_device;    //global変数
// デバイス情報を取得できているか
if (!adapterFound) {
    if (FAILED(dxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(&dxgiAdapter)))) return false;
    warp = true;
}

// 
if (FAILED(D3D12CreateDevice(dxgiAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&g_device)))) {
    return false;
}

終わりに

 これでデバイスを作ることが出来ました。
 明日はDirectX12になって追加されたコマンドキューの作成とスワップチェーンの作成について解説したいと思います。
 今回のデバイス作成を実装したコードはGitHubに上げています。
 それでは、今回はこれで。

 もし解説が間違っていたり、より良い方法などがあった場合は教えて頂けるとありがたいです。
 
github.com