ここぷろ!

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

【DirectX12】レンダーターゲット用デスクプタの作成【初期化】


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

 少し更新が開いてしまいましたが、今日はレンダーターゲット用デスクプタの作成をやっていきたいと思います。


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;

レンダーターゲット用デスクリプタの作成

デスクリプタとは

 デスクリプタヒープというのは、GPU上に作られるデスクリプタを保存するための配列です。
 前回、説明したので、詳しい説明は省略します。

デスクリプタヒープの先頭アドレスを保持するハンドルの取得

 まず初めに、CPU向けにデスクリプタヒープの先頭アドレスを保持するハンドルを取得します。
 これは、GPUメモリ上のデスクリプタヒープに、CPUから値を設定したい為に行います。

CPU向けのアドレス取得はなぜ?

 前回、デスクリプタヒープはGPU上に作成されるという説明をしました。
 それなのに、CPU向けのアドレスを取得するのはなぜか
 それはGPUメモリには、GPU向けのアドレスと、CPU向けのアドレスが存在するからです。

 GPUメモリにはGPUだけではなく、CPUからも読み込みと書き込みができます。
 ですが、CPUとGPU異なるメモリ管理装置を経由してメモリを見ている場合があり、その場合にはGPUメモリの見え方は異なるのです。

メモリを参照する番地管理の違い

 メインメモリとGPUメモリを持ったコンピューターを、単純な例として考えましょう。

 GPUGPUメモリだけ見えていれば十分です。なのでGPUGPUメモリをアドレス0番から数えます。
 ですが、CPUにとってはGPUメモリより、メインメモリが重要です。なのでCPUは、メインメモリをアドレス0番から。GPUメモリをメインメモリの最後の番地から逆順に数えます。

 この時点でCPUとGPUの間で、メモリの数え方が異なるものになってしまいました。


 実際は、いろいろな理由はありますが、最も端的な理由として、このような現象が起きる為、GPUメモリにはGPU向けのアドレスと、CPU向け、2つのアドレスが存在します。


レンダーターゲットはフレームバッファと1:1となるように作成

 アドレスを取得したので、作成の為にフレームバッファの数だけforループを回します。

 IDXGISwapChain1::GetBuffer関数を使って、i番目のレンダーターゲットのID3D12Resouceインターフェイスを取得し、ID3D12Device::CreateRenderTargetView関数によって、デスクリプタヒープ上にRTV用のデスクリプタを作成しています。

 最後に、次のデスクリプタのアドレスを計算して、レンダーターゲット用デスクプタの作成は終わりです。

ID3D12Resouce

 GPUメモリにある、様々なデータを管理するための汎用クラスです。
 配列、テクスチャ、モデルといったデータは、全てID3D12Resouceインターフェイスを通して管理されます。
f:id:holmes8901:20181121102440p:plain

CreateRenderTargetView関数

 この関数を使用することで、デスクリプタヒープ上にRTV用のデスクリプタを作成できます。

ID3D12Device::CreateRenderTargetView method | Microsoft Docs

  • CreateSwapChainForHwnd関数
    • 第1引数:ID3D12Resouceインターフェイスへのポインタ
    • 第2引数:D3D12_RENDER_TARGET_VIEW_DESC構造体へのポインタ
    • 第3引数:D3D12_CPU_DESCRIPTOR_HANDLE型のアドレス
  • 補足説明
    • 第1引数:デスクリプタに割り当てるレンダーターゲット
    • 第2引数:nullptrを渡すことで、デフォルト値で初期化した構造体のポインタが渡されたかのように動作
    • 第3引数:デスクリプタを作成するアドレス

D3D12_RENDER_TARGET_VIEW_DESC構造体

 CreateRenderTargetView関数の第2引数として渡す、D3D12_RENDER_TARGET_VIEW_DESC構造体には、二つのメンバがあります。

D3D12_RENDER_TARGET_VIEW_DESC | Microsoft Docs

  • D3D12_RENDER_TARGET_VIEW_DESC
    • Format:表示形式を指定
    • ViewDimension:レンダーターゲットのリソースにアクセスする方法を指定
事前定義
const UINT FRAME_COUNT = 2;

UINT g_rtvDescriptorSize;

ConPtr<ID3D12Device>  g_device
ConPtr<ID3D12SwapChain3> g_swapChain;
ConPtr<ID3D12Resource> g_renderTargfets[FRAME_COUNT];
実装
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = {};
rtvHandle.ptr = rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();

// フレームバッファの数だけループ
for (int i = 0; i < FRAME_COUNT; ++i) {
    
    // バッファを取得
    if (FAILED(g_swapChain->GetBuffer( i, IID_PPV_ARGS(&g_renderTargfets[i])))) {
        return false;
    }

    // レンダーターゲットビュー
    g_device->CreateRenderTargetView( g_renderTargfets[i].Get(), nullptr, rtvHandle);
    rtvHandle.ptr += g_rtvDescriptorSize;
}

終わりに

 お疲れ様でした。今回で初期化処理のほとんどが終わりました。
 
 今回の実装内容はGitHubに上げてあります。
 解説が間違っていたり、より良い方法などがあった場合は教えて頂けるとありがたいです。

 あと残すのは、コマンドアロケータ、コマンドリストの作成と、フェンス、フェンスイベントの作成です。
 次回はコマンドアロケータの作成について解説していきます。


 それでは、今回はこの辺りで。

github.com