如何:使用動態資源
重要 API
當您的應用程式需要變更這些資源中的資料時,您可以建立和使用動態資源。 您可以建立動態使用的紋理和緩衝區。
您所需了解的事情
技術
必要條件
我們假設您熟悉 C++。 您還需要圖形程式設計概念的基本經驗。
指示
步驟 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 類型變數,並將其初始化為零。 您將使用此變數來變更資源。
- 停用圖形處理單位 (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);
}
備註
使用動態紋理
我們建議您為每個格式只建立一個動態紋理,而且可能每個大小。 我們不建議建立動態 Mipmap、Cube 和磁片區,因為對應每個層級的額外負荷。 針對 mipmap,您只能在最上層指定 D3D11_MAP_WRITE_DISCARD 。 所有層級只要對應最上層,即可捨棄所有層級。 磁片區和 Cube 的行為相同。 針對 Cube,最上層和臉部 0 會對應。
使用動態緩衝區
當您在 GPU 使用緩衝區時,在靜態頂點緩衝區上呼叫 Map 時,您會獲得顯著的效能負面影響。 在此情況下, Map 必須等到 GPU 完成從緩衝區讀取頂點或索引資料之後, Map 才能返回呼叫的應用程式,這會導致大幅延遲。 呼叫 Map ,然後針對每個畫面從靜態緩衝區轉譯數次,也會防止 GPU 緩衝轉譯命令,因為它必須在傳回地圖指標之前完成命令。 如果沒有緩衝的命令,GPU 會保持閒置狀態,直到應用程式完成填滿頂點緩衝區或索引緩衝區,併發出轉譯命令為止。
在理想情況下,頂點或索引資料永遠不會變更,但這不一定可行。 有許多情況是應用程式需要變更頂點或為每個畫面編制資料索引,甚至每個畫面多次。 在這些情況下,建議您使用 D3D11_USAGE_DYNAMIC建立頂點或索引緩衝區。 此使用旗標會導致執行時間針對頻繁的地圖作業進行優化。 只有在 緩衝區經常對應時,D3D11_USAGE_DYNAMIC才有用;如果資料是保持常數,請將該資料放在靜態頂點或索引緩衝區中。
若要在使用動態頂點緩衝區時收到效能改進,您的應用程式必須以適當的D3D11_MAP值呼叫Map。 D3D11_MAP_WRITE_DISCARD 表示應用程式不需要在緩衝區中保留舊的頂點或索引資料。 如果在呼叫 Map 與 D3D11_MAP_WRITE_DISCARD時,GPU 仍在使用緩衝區,執行時間會傳回記憶體新區域的指標,而不是舊的緩衝區資料。 這可讓 GPU 繼續使用舊資料,而應用程式會將資料放在新的緩衝區中。 應用程式中不需要額外的記憶體管理;當 GPU 完成時,舊的緩衝區會自動重複使用或終結。
注意
當您使用 D3D11_MAP_WRITE_DISCARD對應緩衝區時,執行時間一律會捨棄整個緩衝區。 您無法藉由指定非零位移或有限的大小欄位,在未對應的緩衝區區域中保留資訊。
在某些情況下,應用程式每個地圖需要儲存的資料量很小,例如新增四個頂點來轉譯 Sprite。 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 (動態緩衝區) 動態緩衝區來對應動態常數緩衝區和著色器資源檢視。 Direct3D 11 和更早的執行時間限制不會覆寫頂點或索引緩衝區的部分更新對應。 若要判斷 Direct3D 裝置是否支援這些功能,請呼叫 ID3D11Device::CheckFeatureSupport 與 D3D11_FEATURE_D3D11_OPTIONS。 CheckFeatureSupport 會以裝置的功能填入 D3D11_FEATURE_DATA_D3D11_OPTIONS 結構的成員。 這裡的相關成員包括 MapNoOverwriteOnDynamicConstantBuffer 和 MapNoOverwriteOnDynamicBufferSRV。
相關主題