方法: 動的リソースを使用する
重要な API
アプリでこれらのリソース内のデータを変更する必要がある場合は、動的リソースを作成して使用します。 動的な使用のためにテクスチャとバッファーを作成できます。
知っておくべきこと
テクノロジ
前提条件
C++ に習熟していることを前提としています。 また、グラフィックス プログラミングの概念に対する基礎的な知識も必要となります。
Instructions
手順 1: 動的使用法を指定する
アプリでリソースを変更できるようにする場合は、リソースを作成時に動的で書き込み可能として指定する必要があります。
動的使用法を指定するには
- リソース使用量を動的として指定します。 たとえば、頂点バッファーまたは定数バッファーの D3D11_BUFFER_DESC の Usage メンバーにD3D11_USAGE_DYNAMIC値を指定し、2D テクスチャの D3D11_TEXTURE2D_DESC の Usage メンバーにD3D11_USAGE_DYNAMIC値を指定します。
- リソースを書き込み可能として指定します。 たとえば、 D3D11_BUFFER_DESC または D3D11_TEXTURE2D_DESC の CPUAccessFlags メンバーに D3D11_CPU_ACCESS_WRITE 値 を指定します。
- リソースの説明を作成関数に渡します。 たとえば、 D3D11_BUFFER_DESC のアドレスを ID3D11Device::CreateBuffer に渡し、 D3D11_TEXTURE2D_DESC のアドレスを ID3D11Device::CreateTexture2D に渡します。
動的頂点バッファーを作成する例を次に示します。
// Create a vertex buffer for a triangle.
float2 triangleVertices[] =
{
float2(-0.5f, -0.5f),
float2(0.0f, 0.5f),
float2(0.5f, -0.5f),
};
D3D11_BUFFER_DESC vertexBufferDesc = { 0 };
vertexBufferDesc.ByteWidth = sizeof(float2)* ARRAYSIZE(triangleVertices);
vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
vertexBufferDesc.MiscFlags = 0;
vertexBufferDesc.StructureByteStride = 0;
D3D11_SUBRESOURCE_DATA vertexBufferData;
vertexBufferData.pSysMem = triangleVertices;
vertexBufferData.SysMemPitch = 0;
vertexBufferData.SysMemSlicePitch = 0;
DX::ThrowIfFailed(
m_d3dDevice->CreateBuffer(
&vertexBufferDesc,
&vertexBufferData,
&vertexBuffer2
)
);
手順 2: 動的リソースを変更する
リソースを作成時に動的で書き込み可能として指定した場合は、後でそのリソースに変更を加えることができます。
動的リソースのデータを変更するには
- リソースの新しいデータを設定します。 D3D11_MAPPED_SUBRESOURCE型変数 を 宣言し、0 に初期化します。 この変数を使用して、リソースを変更します。
- 変更するデータへのグラフィックス処理装置 (GPU) アクセスを無効にし、データを含むメモリへのポインターを取得します。 このポインターを取得するには、ID3D11DeviceContext::Map を呼び出すときに、D3D11_MAP_WRITE_DISCARDを MapType パラメーターに渡します。 このポインターを、前の手順の D3D11_MAPPED_SUBRESOURCE 変数のアドレスに設定します。
- 新しいデータをメモリに書き込みます。
- 新しいデータの書き込みが完了したら、 ID3D11DeviceContext::Unmap を呼び出して、データへの GPU アクセスを再び可能にします。
動的頂点バッファーを変更する例を次に示します。
// This might get called as the result of a click event to double the size of the triangle.
void TriangleRenderer::MapDoubleVertices()
{
D3D11_MAPPED_SUBRESOURCE mappedResource;
ZeroMemory(&mappedResource, sizeof(D3D11_MAPPED_SUBRESOURCE));
float2 vertices[] =
{
float2(-1.0f, -1.0f),
float2(0.0f, 1.0f),
float2(1.0f, -1.0f),
};
// Disable GPU access to the vertex buffer data.
auto m_d3dContext = m_deviceResources->GetD3DDeviceContext();
m_d3dContext->Map(vertexBuffer2.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
// Update the vertex buffer here.
memcpy(mappedResource.pData, vertices, sizeof(vertices));
// Reenable GPU access to the vertex buffer data.
m_d3dContext->Unmap(vertexBuffer2.Get(), 0);
}
注釈
動的テクスチャの使用
動的テクスチャは、形式ごとに 1 つだけ作成し、サイズごとに作成することをお勧めします。 動的ミップマップ、キューブ、ボリュームを作成することは、すべてのレベルのマッピングに追加のオーバーヘッドがあるため、お勧めしません。 ミップマップの場合は、最上位レベルでのみ D3D11_MAP_WRITE_DISCARD を指定できます。 すべてのレベルは、最上位レベルのみをマッピングすることによって破棄されます。 この動作は、ボリュームとキューブでも同じです。 キューブの場合、最上位レベルと面 0 がマップされます。
動的バッファーの使用
GPU がバッファーを使用している間に静的頂点バッファーで Map を呼び出すと、パフォーマンスが大幅に低下します。 この状況では、 Map が呼び出し元アプリに戻る前に、GPU がバッファーから頂点またはインデックス データの読み取りを完了するまで、 Map は待機する必要があります。これにより、大幅な遅延が発生します。 Map を呼び出し、フレームごとに静的バッファーから複数回レンダリングすると、マップ ポインターを返す前にコマンドを終了する必要があるため、GPU によるレンダリング コマンドのバッファリングもできなくなります。 バッファー化されたコマンドがない場合、GPU は、アプリが頂点バッファーまたはインデックス バッファーの入力を完了し、レンダリング コマンドを発行するまでアイドル状態のままです。
頂点データやインデックス データは決して変更されないことが理想的ですが、これは常に可能であるとは限りません。 アプリでは、フレームごとに頂点データまたはインデックス データを変更する必要がある多くの状況があります。フレームごとに複数回も変更する必要があります。 このような場合は、 D3D11_USAGE_DYNAMICを使用して頂点バッファーまたはインデックス バッファーを作成することをお勧めします。 この使用フラグにより、ランタイムは頻繁にマップ操作用に最適化されます。 D3D11_USAGE_DYNAMIC は、バッファーが頻繁にマップされる場合にのみ役立ちます。データを一定に保つ場合は、そのデータを静的な頂点またはインデックス バッファーに配置します。
動的頂点バッファーを使用するときにパフォーマンスが向上するには、アプリで適切なD3D11_MAP値を使用して Map を呼び出す必要があります。 D3D11_MAP_WRITE_DISCARD は、アプリが古い頂点またはインデックス データをバッファーに保持する必要がないことを示します。 D3D11_MAP_WRITE_DISCARDを使用して Map を呼び出すときに GPU がまだバッファーを使用している場合、ランタイムは古いバッファー データではなく新しいメモリ領域へのポインターを返します。 このため、アプリで新しいバッファーにデータを配置しながら、GPU で古いデータを使用し続けることができます。 アプリ内で追加のメモリ管理を行う必要はなく、古いバッファーは GPU での使用が終われば自動的に再利用または破棄されます。
注意
D3D11_MAP_WRITE_DISCARDを使用してバッファーをマップすると、ランタイムは常にバッファー全体を破棄します。 0 以外のオフセット フィールドまたは制限付きサイズ フィールドを指定して、バッファーのマップされていない領域に情報を保持することはできません。
スプライトをレンダリングするために 4 つの頂点を追加するなど、アプリがマップごとに格納する必要があるデータの量が少ない場合があります。 D3D11_MAP_WRITE_NO_OVERWRITE は、アプリが動的バッファーで既に使用されているデータを上書きしないことを示します。 Map 呼び出しは古いデータへのポインターを返します。これにより、アプリは頂点またはインデックス バッファーの未使用の領域に新しいデータを追加できます。 GPU で引き続き使用されている可能性があるため、描画操作で使用される頂点またはインデックスをアプリで変更することはできません。 次に、動的バッファーがいっぱいになった後 にD3D11_MAP_WRITE_DISCARD を使用して新しいメモリ領域を受け取り、GPU の完了後に古い頂点またはインデックス データを破棄することをお勧めします。
非同期クエリ メカニズムは、頂点が GPU でまだ使用されているかどうかを判断するのに役立ちます。 頂点を使用する最後の描画呼び出しの後に D3D11_QUERY_EVENT 型のクエリを発行します。 ID3D11DeviceContext::GetData がS_OKを返すとき、頂点は使用されなくなりました。 D3D11_MAP_WRITE_DISCARDまたはマップ値のないバッファーをマップする場合、頂点が GPU と正しく同期されていることを常に保証します。 ただし、マップ値なしでマップすると、前に説明したパフォーマンスの低下が発生します。
注意
direct3D 11.1 ランタイムは、Windows 8 以降で使用でき、動的定数バッファーと、D3D11_MAP_WRITE_NO_OVERWRITEを使用した動的バッファーのシェーダー リソース ビュー (SRV) のマッピングを可能にします。 Direct3D 11 以前のランタイムでは、頂点バッファーまたはインデックス バッファーへの上書きなしの部分更新マッピングが制限されました。 Direct3D デバイスでこれらの機能がサポートされているかどうかを確認するには、 ID3D11Device::CheckFeatureSupport を D3D11_FEATURE_D3D11_OPTIONSで呼び出します。 CheckFeatureSupport は、 D3D11_FEATURE_DATA_D3D11_OPTIONS 構造体のメンバーにデバイスの機能を入力します。 ここでの関連するメンバーは 、MapNoOverwriteOnDynamicConstantBuffer と MapNoOverwriteOnDynamicBufferSRV です。
関連トピック