【ゲーム制作】No.10: 動的頂点バッファ/インデックスバッファの設定について

お久しぶりです。前回の更新が2021年10月13日ですから、約17ヶ月ぶりの更新になってしまいました。
いろいろ生活は代わりましたが、ゲーム開発はそのまま続けています。

環境もDirectX11( Microsoft DirectX SDK (June 2010) ) + Windows11のままです。
今後も定期的に更新をしていきたいと思っています。

今日のテーマは動的な頂点バッファとインデックスバッファの設定方法についてです。

自分は今、動的LOD機能の制作を進めており(これも近いうちに公開したいと思っています)、

その為に頂点バッファとインデックスバッファの動的な読み取りと書込みが必要だったのですが、

これをどう設定するのが良いのかいろいろ苦労した為、それの備忘録になります。

!!注意!!:これは自分の環境での設定です。他の環境での動作は未確認です。


【パターン1】頂点バッファとインデックスバッファで動的な読み取りと書込みを行う場合(イレギュラーな方法)

下記の設定でバッファを作ると、バッファは動的な読み取りと書込みを行うことが出来ます。

D3D11_BUFFER_DESC    buffer_desc = { 0 };
buffer_desc.ByteWidth         = バッファのサイズ;
buffer_desc.Usage                = D3D11_USAGE_DEFAULT;
buffer_desc.BindFlags           = D3D11_BIND_UNORDERED_ACCESS;
buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
buffer_desc.MiscFlags           = 0;
buffer_desc.StructureByteStride = 0;

D3D11_SUBRESOURCE_DATA subresource = { 0 };
subresource.pSysMem          = 開始アドレス;
subresource.SysMemPitch      = 0;
subresource.SysMemSlicePitch = 0;

p_d3ddevice->CreateBuffer(&buffer_desc, &subresource, &m_p_vertex_buffer);


特徴として、

①D3D11_BIND_UNORDERED_ACCESSという正しくない設定を使っています。
 動いたから良いかレベルのイレギュラーな設定です。

②D3D11_BIND_VERTEX_BUFFERやD3D11_BIND_INDEX_BUFFERと合わせて使うことは出来ません。

その為、ID3D11DeviceContext::IASetVertexBuffers()やID3D11DeviceContext::IASetIndexBuffer()を使うと
D3D11_BIND_VERTEX_BUFFERやD3D11_BIND_INDEX_BUFFERが立っていない為に、デバッグウインドウに以下のような警告が出るようになります。

D3D11 ERROR: ID3D11DeviceContext::IASetVertexBuffers: A Buffer trying to be bound to slot 0 did not have the appropriate bind flag set at creation time to allow the Buffer to be bound as a VertexBuffer. [ STATE_SETTING ERROR #238: IASETVERTEXBUFFERS_INVALIDBUFFER]

デバッグビルトでは表示コスト以外に影響はありませんが、リファレンスドライバー(D3D_DRIVER_TYPE_REFERENCE)などで作成していると警告ではなくエラーになります。
この警告を修正する為には次のパターン2の方法に切り替える必要があります。


【パターン2】頂点バッファとインデックスバッファで動的な読み取りと書込みを行う場合(より一般的な方法)

上記設定を踏まえて、より一般的な方法として動的に読み取りと書込みを行う頂点バッファとインデックスバッファを作る為には、
動的な読み取りと動的な書込みに分けて設計する必要があります。

動的な読み取り:D3D10_USAGE_STAGINGを使用してバッファを作成します。読み取り前にGPU→CPUにメモリをコピーし、このバッファのMap()でデータの読み取りを行います。

動的な書き込み:D3D11_USAGE_DYNAMICを使用してバッファを作成します。このバッファのMap()でデータの書き込みを行います。


// 1,D3D11_USAGE_DYNAMICを使用してバッファーを作成

D3D11_BUFFER_DESC    buffer_desc = { 0 };
buffer_desc.ByteWidth           = バッファのサイズ;
buffer_desc.Usage               = D3D11_USAGE_DYNAMIC;
buffer_desc.BindFlags           = D3D11_BIND_INDEX_BUFFER;    // 警告を修正する為
buffer_desc.CPUAccessFlags      = D3D11_CPU_ACCESS_WRITE;    // 書き込みを行う為
buffer_desc.MiscFlags           = 0;
buffer_desc.StructureByteStride = 0;

D3D11_SUBRESOURCE_DATA subresource = { 0 };
subresource.pSysMem          = 開始アドレス;
subresource.SysMemPitch      = 0;
subresource.SysMemSlicePitch = 0;

p_d3ddevice->CreateBuffer(&buffer_desc, &subresource, &p_index_buffer);


// 動的な読み取りを行う場合
//
// 2,D3D11_USAGE_STAGINGを使用してバッファーを作成
ID3D11BufferPtr    p_index_buffer_cpu_staging    = NULL;

D3D11_BUFFER_DESC    buffer_desc = { 0 };
buffer_desc.ByteWidth            = バッファのサイズ;
buffer_desc.Usage                = D3D11_USAGE_STAGING;    // GPU→CPUメモリコピーの為
buffer_desc.BindFlags            = 0;
buffer_desc.CPUAccessFlags        = D3D11_CPU_ACCESS_READ;// 読み取りを行う為
buffer_desc.MiscFlags            = 0;
buffer_desc.StructureByteStride    = 0;

uint8_collection    NULL_buffer( バッファのサイズ, 0);

D3D11_SUBRESOURCE_DATA    subresource = { 0 };
subresource.pSysMem = &NULL_buffer.front();
subresource.SysMemPitch = 0;
subresource.SysMemSlicePitch = 0;

hr = p_d3ddevice->CreateBuffer(&buffer_desc, &subresource, &p_index_buffer_cpu_staging);

// 3,頂点バッファからD3D11_USAGE_STAGINGバッファへ頂点データをコピー

p_device_context->CopyResource( p_index_buffer_cpu_staging, p_index_buffer);

// 4,頂点データをコピーされたD3D11_USAGE_STAGINGバッファをMap()し読み出す

D3D11_MAPPED_SUBRESOURCE    mapped_resource    = { 0 };

hr = p_device_context->Map(
    p_index_buffer_cpu_staging,
    0,
    D3D11_MAP_READ,
    0,
    &mapped_resource);


// 動的な書込みを行う場合
//
// 5,D3D11_USAGE_DYNAMICバッファをMap()し書き込む

D3D11_MAPPED_SUBRESOURCE    mapped_resource    = { 0 };

hr = p_device_context->Map(
    p_index_buffer,
    0,
    D3D11_MAP_WRITE_DISCARD,
    0,
    &mapped_resource);


注意点として、動的な書込みはD3D11_MAP_WRITE_DISCARDかD3D11_MAP_WRITE_NO_OVERWRITEになります。
D3D11_MAP_READ_WRITEは使用できずエラーになります。