バッファーを使用したテクスチャ データのアップロード

2D または 3D テクスチャ データのアップロードは 1D データのアップロードと似ていますが、アプリケーションでは列ピッチに関連したデータの配置により注意が払われる必要があります。 バッファーは、グラフィックス パイプラインの複数の部分から同時かつ直交的にに使用でき、非常に柔軟です。

バッファーを介したテクスチャ データのアップロード

アプリケーションでは、ID3D12GraphicsCommandList::CopyTextureRegion または ID3D12GraphicsCommandList::CopyBufferRegion を介してデータをアップロードする必要があります。 テクスチャ データは、リソース データに比べてサイズが大きく、繰り返しアクセスされることが多いほか、非線形メモリ レイアウトのキャッシュ整合性が向上したことでより多くのメリットを得られます。 D3D12 でバッファーを使用する場合は、メモリ アラインメントの要件が満たされている限り、リソース データのコピー操作に関連するデータの配置とアラインメントをアプリケーションで完全に制御できます。

このサンプルでは、アプリケーションが 2D データを 1D に平面化してからバッファーに配置する処理を取り上げています。 ミップマップ 2D のシナリオでは、アプリケーションが各サブリソースを個別に平面化して素早く 1D サブ割り当てアルゴリズムを使用するか、または複雑な 2D サブ割り当て手法を使用して、ビデオ メモリの使用率を最小化できます。 最初の手法は、簡単であることから、より多く使用されることが予想されます。 2 つ目の手法は、データをディスク上またはネットワーク経由でパックする場合に役立ちます。 どちらの場合も、アプリケーションはサブリソースごとにコピー API を呼び出す必要があります。

// Prepare a pBitmap in memory, with bitmapWidth, bitmapHeight, and pixel format of DXGI_FORMAT_B8G8R8A8_UNORM. 
//
// Sub-allocate from the buffer for texture data.
//

D3D12_SUBRESOURCE_FOOTPRINT pitchedDesc = { 0 };
pitchedDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
pitchedDesc.Width = bitmapWidth;
pitchedDesc.Height = bitmapHeight;
pitchedDesc.Depth = 1;
pitchedDesc.RowPitch = Align(bitmapWidth * sizeof(DWORD), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);

//
// Note that the helper function UpdateSubresource in D3DX12.h, and ID3D12Device::GetCopyableFootprints 
// can help applications fill out D3D12_SUBRESOURCE_FOOTPRINT and D3D12_PLACED_SUBRESOURCE_FOOTPRINT structures.
//
// Refer to the D3D12 Code example for the previous section "Uploading Different Types of Resources"
// for the code for SuballocateFromBuffer.
//

SuballocateFromBuffer(
    pitchedDesc.Height * pitchedDesc.RowPitch,
    D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT
    );

D3D12_PLACED_SUBRESOURCE_FOOTPRINT placedTexture2D = { 0 };
placedTexture2D.Offset = m_pDataCur – m_pDataBegin;
placedTexture2D.Footprint = pitchedDesc;

//
// Copy texture data from DWORD* pBitmap->pixels to the buffer
//

for (UINT y = 0; y < bitmapHeight; y++)
{
  UINT8 *pScan = m_pDataBegin + placedTexture2D.Offset + y * pitchedDesc.RowPitch;
  memcpy( pScan, &(pBitmap->pixels[y * bitmapWidth]), sizeof(DWORD) * bitmapWidth );
}

//
// Create default texture2D resource.
//

D3D12_RESOURCE_DESC  textureDesc { ... };

CComPtr<ID3D12Resource> texture2D;
d3dDevice->CreateCommittedResource( 
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), 
        D3D12_HEAP_FLAG_NONE, &textureDesc, 
        D3D12_RESOURCE_STATE_COPY_DEST, 
        nullptr, 
        IID_PPV_ARGS(&texture2D) );

//
// Copy heap data to texture2D.
//

commandList->CopyTextureRegion( 
        &CD3DX12_TEXTURE_COPY_LOCATION( texture2D, 0 ), 
        0, 0, 0, 
        &CD3DX12_TEXTURE_COPY_LOCATION( m_spUploadHeap, placedTexture2D ), 
        nullptr );

ヘルパー構造体の CD3DX12_HEAP_PROPERTIESCD3DX12_TEXTURE_COPY_LOCATION、および CreateCommittedResource メソッドと CopyTextureRegion メソッドの使用に注意してください。

コピー

アプリケーションでは、D3D12 のメソッドを使用して、D3D11 の UpdateSubresourceCopySubresourceRegion およびリソースの初期データを置き換えることができます。 行優先のテクスチャ データに相当する単一の 3D サブリソースは、バッファー リソースに配置できます。 CopyTextureRegion では、そのテクスチャ データを、バッファーからテクスチャ レイアウトが不明なテクスチャ リソースに、またはその逆にコピーすることができます。 アプリケーションでは、頻繁にアクセスされる GPU リソースをこの手法で作成することが推奨されるため、アップロード ヒープに大きなバッファーを作成し、頻繁にアクセスされる GPU リソースを CPU からアクセスされない既定のヒープに作成します。 そのような手法では、一般的に UMA アーキテクチャを損なうことなく、個々の GPU とそれらの大量の CPU アクセス不能メモリが効率的にサポートされます。

次の 2 つの定数に注目してください。

const UINT D3D12_TEXTURE_DATA_PITCH_ALIGNMENT = 256;
const UINT D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT = 512;

マップとマップ解除

MapUnmap は、複数のスレッドから安全に呼び出すことができます。 Map を最初に呼び出したときに、リソースの CPU 仮想アドレス範囲が割り当てられます。 この CPU 仮想アドレス範囲は、Unmap を最後に呼び出したときに割り当て解除されます。 CPU 仮想アドレスは通常はアプリケーションに返されます。

CPU と GPU の間でリードバック ヒープ内のリソースを介してデータが渡されるたびに、MapUnmap を使用して D3D12 に対応するすべてのシステムをサポートする必要があります。 範囲をできるだけ狭くすると、範囲を必要とするシステムの効率が最大化されます ( D3D12_RANGEを参照)。

デバッグ ツールのパフォーマンスは、 マップ / のマップ解除 のすべての呼び出しで範囲を正確に使用できるだけでなく、CPU の変更が行われなくなったときにリソースのマッピングを解除するアプリケーションからもメリットがあります。

D3D11 では Map (DISCARD パラメーターを設定) を使用してリソースの名前を変更できましたが、D3D12 ではこれがサポートされません。 アプリケーション自体がリソースの名前変更を実装する必要があります。 すべての Map 呼び出しは暗黙的にNO_OVERWRITEされ、マルチスレッドになります。 アプリケーションは、CPU によるデータ アクセスの前に、コマンド リスト内の関連する GPU 作業が終了していることを確認する必要があります。 D3D12 で Map を呼び出した場合、コマンド バッファーは暗黙的にフラッシュされず、GPU が処理を完了するまで待機するのがブロックされることもありません。 このため、一部のシナリオでは MapUnmap が最適化によって削除されることもあります。

バッファー アラインメント

バッファー アラインメントには次の制限があります。

  • 線形サブリソースのコピーは、512 バイトに揃える必要があります (行ピッチは D3D12_TEXTURE_DATA_PITCH_ALIGNMENT バイトに揃えられます)。
  • 定数データの読み取りは、ヒープの先頭から 256 バイトの倍数で行う必要があります (つまり 256 バイトにアラインされたアドレスからのみ)。
  • インデックス データの読み取りは、インデックス データ タイプ サイズの倍数で行う必要があります (つまりデータに対して自然にアラインされたアドレスからのみ)。
  • ID3D12GraphicsCommandList::ExecuteIndirect データは、4 の倍数であるオフセットからのデータである必要があります (つまり、DWORD がアラインされたアドレスからのみ)。