ID3D12Resource
COM公式ドキュメント
CPU と GPU が物理メモリ (ヒープ) を読み書きする一般化された機能をカプセル化します。単純なデータ配列を扱うための抽象化に加え、シェーダーのサンプリング向けに最適化された多次元データを扱うための抽象化も含みます。
メソッド 7
vtbl = vtable インデックス(0始まり)。HSP等からCOMメソッドをインデックス指定で呼ぶ際に使用します。0〜2 は IUnknown。
リソース内の指定したサブリソースへの CPU ポインターを取得します。ただし、そのポインター値がアプリケーションに開示されない場合があります。Map は必要に応じて CPU キャッシュを無効化し、このアドレスへの CPU からの読み取りに GPU による変更が反映されるようにします。
| Subresource | DWORD | in | サブリソースのインデックス番号を指定します。 |
| pReadRange | D3D12_RANGE* | inoptional | アクセスするメモリの範囲を記述する D3D12_RANGE 構造体へのポインターです。 これは CPU が読み取る可能性のある領域を示し、座標はサブリソース相対です。null ポインターは、サブリソース全体が CPU によって読み取られる可能性があることを示します。End が Begin 以下となる範囲を渡すことで、CPU がデータを一切読み取らないことを指定できます。 |
| ppData | void** | outoptional | リソースデータへのポインターを受け取るメモリブロックへのポインターです。 null ポインターも有効であり、WriteToSubresource のようなメソッドのために CPU 仮想アドレス範囲をキャッシュする用途に便利です。ppData が NULL でない場合、返されるポインターが pReadRange の値によってオフセットされることはありません。 |
戻り値
型: HRESULT
このメソッドは Direct3D 12 の戻り値コード のいずれかを返します。
解説(Remarks)
Map と Unmap は複数のスレッドから安全に呼び出せます。Map の入れ子呼び出しはサポートされており、参照カウント方式で管理されます。最初の Map 呼び出しでリソース用の CPU 仮想アドレス範囲が割り当てられ、最後の Unmap 呼び出しでその CPU 仮想アドレス範囲が解放されます。通常、CPU 仮想アドレスはアプリケーションに返されますが、レイアウトが不明なテクスチャの内容を操作する場合は CPU 仮想アドレスを開示できません。詳細は WriteToSubresource を参照してください。Map を永続的に入れ子にしている場合を除き、アプリケーションはアドレスが一定であることを前提にできません。
Map が返すポインターは、通常のポインターと同等のすべての性質を備えているとは限りませんが、通常の使い方でその違いに気付くことはほとんどありません。たとえば、WRITE_COMBINE 動作のポインターは WRITE_BACK 動作よりも CPU メモリ順序付けの保証が弱くなります。CPU と GPU の双方からアクセスできるメモリは、PCIe の制約により、CPU が持つのと同じアトミックメモリ保証を共有するとは限りません。同期にはフェンスを使用してください。
Map の利用モデルには、単純 (simple) と高度 (advanced) の 2 つのカテゴリがあります。単純な利用モデルはツールの性能を最大化するため、高度なモデルが必要だと確認できるまでは単純なモデルにとどめることを推奨します。
単純な利用モデル
あらゆるアダプターアーキテクチャを十分にサポートするため、アプリケーションは UPLOAD、DEFAULT、READBACK というヒープ型の抽象化にとどめるべきです。アプリケーションは、UPLOAD ヒープ上のリソースへのポインターからの CPU 読み取りを、意図せずであっても避けるべきです。CPU 読み取りは動作はしますが、多くの一般的な GPU アーキテクチャでは極端に低速です。次の点を考慮してください。
- D3D12_HEAP_TYPE_UPLOAD のヒープ、または D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE を持つヒープに関連付けられたリソースから、CPU で読み取らないでください。
- pData が指すメモリ領域は PAGE_WRITECOMBINE で割り当てられている可能性があるため、アプリケーションはそのようなメモリに伴うすべての制約を遵守する必要があります。
-
次のような C++ コードでさえ、以下の x86 アセンブリコードに展開され得るため、メモリからの読み取りが発生して性能上のペナルティを招くことがあります。
C++ コード:
*((int*)MappedResource.pData) = 0;x86 アセンブリコード:
AND DWORD PTR [EAX],0 - この性能上のペナルティを避けるには、適切な最適化設定と言語構文を使用してください。たとえば、volatile ポインターを使うか、コードサイズではなく実行速度を優先して最適化することで、xor 最適化を回避できます。
高度な利用モデル
CPU からアクセス可能なヒープ上のリソースは永続的にマップできます。つまり、リソース作成直後に Map を一度だけ呼び出せば済みます。Unmap を呼び出す必要はありませんが、Map が返したアドレスは、リソースへの最後の参照が解放された後は使用してはなりません。永続的なマップを使用する場合、アプリケーションは、そのメモリを読み書きするコマンドリストを GPU が実行する前に、CPU によるメモリへの書き込みが完了していることを保証する必要があります。一般的なシナリオでは、ExecuteCommandLists を呼び出す前にメモリへ書き込むだけで十分ですが、フェンスを使ってコマンドリストの実行を遅延させる方法も同様に有効です。CPU からアクセス可能なすべてのメモリ型は、リソースをマップしたまま一度もアンマップしないという永続的マップの利用をサポートします。ただし、リソースが破棄された後にそのポインターへアクセスしないことが前提です。
例
D3D12Bundles サンプルでは、ID3D12Resource::Map を次のように使用しています。
三角形のデータを頂点バッファーへコピーします。
// Copy the triangle data to the vertex buffer.
UINT8* pVertexDataBegin;
CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU.
ThrowIfFailed(m_vertexBuffer->Map(0, &readRange, reinterpret_cast<void**>(&pVertexDataBegin)));
memcpy(pVertexDataBegin, triangleVertices, sizeof(triangleVertices));
m_vertexBuffer->Unmap(0, nullptr);
定数バッファー用のアップロードヒープを作成します。
// Create an upload heap for the constant buffers.
ThrowIfFailed(pDevice->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(sizeof(ConstantBuffer) * m_cityRowCount * m_cityColumnCount),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&m_cbvUploadHeap)));
// Map the constant buffers. Note that unlike D3D11, the resource
// does not need to be unmapped for use by the GPU. In this sample,
// the resource stays 'permanently' mapped to avoid overhead with
// mapping/unmapping each frame.
CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU.
ThrowIfFailed(m_cbvUploadHeap->Map(0, &readRange, reinterpret_cast<void**>(&m_pConstantBuffers)));
D3D12 リファレンスのサンプルコード を参照してください。
リソース内の指定したサブリソースへの CPU ポインターを無効化します。
| Subresource | DWORD | in | サブリソースのインデックスを指定します。 |
| pWrittenRange | D3D12_RANGE* | inoptional | アンマップするメモリの範囲を記述する D3D12_RANGE 構造体へのポインターです。 これは CPU が変更した可能性のある領域を示し、座標はサブリソース相対です。null ポインターは、サブリソース全体が CPU によって変更された可能性があることを示します。End が Begin 以下となる範囲を渡すことで、CPU がデータを一切書き込まなかったことを指定できます。 このパラメーターはツールによってのみ使用され、実際のアンマップ操作の正しさには影響しません。 |
解説(Remarks)
Map メソッドの詳細な「解説」および「例」を参照してください。
リソースの記述を取得します。
戻り値
Direct3D 12 のリソース記述構造体です。
このメソッドは、バッファーリソースの GPU 仮想アドレスを返します。
戻り値
型: D3D12_GPU_VIRTUAL_ADDRESS
このメソッドは GPU 仮想アドレスを返します。 D3D12_GPU_VIRTUAL_ADDRESS は UINT64 の typedef による別名です。
解説(Remarks)
このメソッドはバッファーリソースに対してのみ有用であり、すべてのテクスチャリソースに対しては 0 を返します。
GPU 仮想アドレスの利用方法の詳細は、Indirect Drawing を参照してください。
例
D3D1211on12 サンプルでは、ID3D12Resource::GetGPUVirtualAddress を次のように使用しています。
// Initialize the vertex buffer view.
m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress();
m_vertexBufferView.StrideInBytes = sizeof(Vertex);
m_vertexBufferView.SizeInBytes = vertexBufferSize;
D3D12 リファレンスのサンプルコード を参照してください。
CPU を使用してサブリソースにデータをコピーします。これにより、レイアウトが未定義のほとんどのテクスチャの内容を CPU から変更できます。
| DstSubresource | DWORD | in | サブリソースのインデックスを指定します。 |
| pDstBox | D3D12_BOX* | inoptional | リソースデータのコピー先となるコピー先サブリソースの部分領域を定義するボックスへのポインターです。 NULL の場合、データはオフセットなしでコピー先サブリソースに書き込まれます。 コピー元の寸法はコピー先に収まる必要があります (D3D12_BOX を参照)。 空のボックスを指定した場合、この呼び出しは何も行いません。 top の値が bottom 以上の場合、left の値が right 以上の場合、 または front の値が back 以上の場合、ボックスは空とみなされます。 ボックスが空の場合、このメソッドは何の操作も実行しません。 |
| pSrcData | void* | in | メモリ上のコピー元データへのポインターです。 |
| SrcRowPitch | DWORD | in | コピー元データのある行から次の行までの距離です。 |
| SrcDepthPitch | DWORD | in | コピー元データのある深度スライスから次の深度スライスまでの距離です。 |
戻り値
型: HRESULT
このメソッドは Direct3D 12 の戻り値コード のいずれかを返します。
解説(Remarks)
リソースは事前に Map でマップしておく必要があります。WriteToSubresource および ReadFromSubresource による CPU アクセスが有効となるためには、テクスチャは D3D12_RESOURCE_STATE_COMMON 状態でなければなりません。ただし、バッファーにはこの制約はありません。
効率のため、ボックス内の範囲の境界とアライメントは水平方向に ( 64 / [1 ピクセルあたりのバイト数] ) ピクセル単位にそろえてください。 垂直方向の境界とアライメントは 2 行単位とするべきですが、1 ピクセルあたり 1 バイトの形式を使う場合は 4 行を推奨します。 1 回の呼び出しにつき単一の深度スライスであれば効率よく処理されます。 ポインターとストライドを 128 バイト境界にそろえることは必須ではありませんが推奨されます。
下位のミップマップレベルに書き込む場合は、上記より大きい幅と高さを使用することを推奨します。 これは、小さいミップマップレベルが実際にはより大きなメモリブロック内に格納され、不透明な量のオフセットが加わることでキャッシュラインへのアライメントを妨げる可能性があるためです。
WriteToSubresource と ReadFromSubresource は、UMA アダプターではほぼゼロコピーの最適化を可能にしますが、ディスクリート/NUMA アダプターでは、テクスチャデータをローカルビデオメモリに配置できないため効率を著しく損なう恐れがあります。一般的なアプリケーションは、アダプターアーキテクチャが UMA であると認識できる場合を除き、ディスクリート向けのアップロード手法にとどめるべきです。アップロードの詳細については CopyTextureRegion を、UMA の詳細については D3D12_FEATURE_DATA_ARCHITECTURE を参照してください。
UMA システムでは、ループタイリング と呼ばれるループ最適化により、このルーチンを使ってメモリコピーのコストを最小化できます。アップロードを CPU キャッシュに無理なく収まるチャンクに分割することで、CPU とメインメモリ間の実効帯域幅が理論上の最大値により近づきます。
CPU を使用してサブリソースからデータをコピーします。これにより、レイアウトが未定義のほとんどのテクスチャの内容を CPU から読み取れます。
| pDstData | void* | out | メモリ上のコピー先データへのポインターです。 |
| DstRowPitch | DWORD | in | コピー先データのある行から次の行までの距離です。 |
| DstDepthPitch | DWORD | in | コピー先データのある深度スライスから次の深度スライスまでの距離です。 |
| SrcSubresource | DWORD | in | 読み取り元となるサブリソースのインデックスを指定します。 |
| pSrcBox | D3D12_BOX* | inoptional | リソースデータの読み取り元となるコピー先サブリソースの部分領域を定義するボックスへのポインターです。 NULL の場合、データはオフセットなしでコピー先サブリソースから読み取られます。 コピー先の寸法はコピー先に収まる必要があります (D3D12_BOX を参照)。 空のボックスを指定した場合、この呼び出しは何も行いません。 top の値が bottom 以上の場合、left の値が right 以上の場合、または front の値が back 以上の場合、ボックスは空とみなされます。 ボックスが空の場合、このメソッドは何の操作も実行しません。 |
戻り値
型: HRESULT
このメソッドは Direct3D 12 の戻り値コード のいずれかを返します。
解説(Remarks)
WriteToSubresource の「解説」セクションを参照してください。
配置リソース (placed resource) およびコミット済みリソース (committed resource) について、リソースヒープのプロパティを取得します。
| pHeapProperties | D3D12_HEAP_PROPERTIES* | outoptional | D3D12_HEAP_PROPERTIES 構造体へのポインターです。メソッドが正常に完了すると、リソースヒープのプロパティが格納されます。 |
| pHeapFlags | D3D12_HEAP_FLAGS* | outoptional | D3D12_HEAP_FLAGS 型の変数を指定します。メソッドが正常に完了すると、その他のヒープフラグが格納されます。 |
戻り値
型: HRESULT
このメソッドは Direct3D 12 の戻り値コード のいずれかを返します。 リソースが予約リソース (reserved resource) として作成されていた場合は、E_INVALIDARG が返されます。
解説(Remarks)
このメソッドは配置リソースおよびコミット済みリソースに対してのみ機能し、予約リソースに対しては機能しません。 リソースが予約リソースとして作成されていた場合は、E_INVALIDARG が返されます。 ページは、どのヒープにもマップされていないことも、1 つまたは複数のヒープにマップされていることもあります。
詳細については、Direct3D 12 のメモリ管理 を参照してください。
Microsoft 公式リファレンス: 英語 (en-us) · 日本語 (ja-jp) · 原文ソース (GitHub)
HSP用 COM定義
#usecom / #comfunc によるHSPのCOM呼び出し定義。数字は vtbl インデックス(0始まり)。クラスIDが無い場合 #usecom の末尾は "{}"、ある場合は "{CLSID}"。
#define global IID_ID3D12Resource "{696442BE-A72E-4059-BC79-5B5C98040FAD}" #usecom global ID3D12Resource IID_ID3D12Resource "{}" #comfunc global ID3D12Resource_Map 8 int,var,sptr #comfunc global ID3D12Resource_Unmap 9 int,var #comfunc global ID3D12Resource_GetDesc 10 #comfunc global ID3D12Resource_GetGPUVirtualAddress 11 #comfunc global ID3D12Resource_WriteToSubresource 12 int,var,sptr,int,int #comfunc global ID3D12Resource_ReadFromSubresource 13 sptr,int,int,int,var #comfunc global ID3D12Resource_GetHeapProperties 14 var,var ; ※数字は vtbl インデックス(0始まり)。0/1/2 は IUnknown(QueryInterface/AddRef/Release)。 ; ※このインターフェースは直接 CoCreateInstance するクラスIDが無いため "{}"(他メソッド/アクティベーションで取得)。 ; ※出力/バッファ引数は var(変数直渡し)。varptr 方式にも切替可。 ; ※ハンドル/void*等の不透明ポインタは IronHSP では intptr 指定が可能。#define global IID_ID3D12Resource "{696442BE-A72E-4059-BC79-5B5C98040FAD}" #usecom global ID3D12Resource IID_ID3D12Resource "{}" #comfunc global ID3D12Resource_Map 8 int,sptr,sptr #comfunc global ID3D12Resource_Unmap 9 int,sptr #comfunc global ID3D12Resource_GetDesc 10 #comfunc global ID3D12Resource_GetGPUVirtualAddress 11 #comfunc global ID3D12Resource_WriteToSubresource 12 int,sptr,sptr,int,int #comfunc global ID3D12Resource_ReadFromSubresource 13 sptr,int,int,int,sptr #comfunc global ID3D12Resource_GetHeapProperties 14 sptr,sptr ; ※数字は vtbl インデックス(0始まり)。0/1/2 は IUnknown(QueryInterface/AddRef/Release)。 ; ※このインターフェースは直接 CoCreateInstance するクラスIDが無いため "{}"(他メソッド/アクティベーションで取得)。 ; ※出力/バッファ引数はポインタ方式(token=sptr / 呼び出しは varptr(変数))。 ; ※ハンドル/void*等の不透明ポインタは IronHSP では intptr 指定が可能。