DirectX11--深入理解和使用緩衝區資源
前言
在Direct3D 11中,緩衝區屬於其中一種資源型別,它在記憶體上的佈局是一維線性的。根據HLSL支援的型別以及C++的使用情況,緩衝區可以分為下面這些型別:
- 頂點緩衝區(Vertex Buffer)
- 索引緩衝區(Index Buffer)
- 常量緩衝區(Constant Buffer)
- 有型別的緩衝區(Typed Buffer)
- 結構化緩衝區(Structured Buffer)
- 追加/消耗緩衝區(Append/Consume Buffer)
- 位元組地址緩衝區(Byte Address Buffer)(未完工)
- 間接引數緩衝區(Indirect Argument Buffer)(未完工)
因此這一章主要講述上面這些資源的建立和使用方法
DirectX11 With Windows SDK完整目錄
歡迎加入QQ群: 727623616 可以一起探討DX11,以及有什麼問題也可以在這裡彙報。
頂點緩衝區(Vertex Buffer)
顧名思義,頂點緩衝區存放的是一連串的頂點資料,儘管緩衝區的資料實際上還是一堆二進位制流,但在傳遞給輸入裝配階段的時候,就會根據頂點輸入佈局將其裝配成HLSL的頂點結構體資料。頂點緩衝區的資料可以用自定義的頂點結構體陣列來初始化。頂點可以包含的成員有:頂點座標(必須有),頂點顏色,頂點法向量,紋理座標,頂點切線向量等等。每個頂點的成員必須匹配合適的DXGI資料格式。
當然,純粹的頂點陣列只是針對單個物體而言的。如果需要繪製大量相同的物體,需要同時用到多個頂點緩衝區。這允許你將頂點資料分開成多個頂點緩衝區來存放。
這裡還提供了頂點緩衝區的另一種形式: 例項緩衝區 。我們可以提供一到多個的頂點緩衝區,然後再提供一個例項緩衝區。其中例項緩衝區存放的可以是物體的世界矩陣、世界矩陣的逆轉置、材質等。這樣做可以減少大量重複資料的產生,以及減少大量的CPU繪製呼叫。
CreateVertexBuffer函式--建立頂點緩衝區
頂點緩衝區的建立需要區分下面兩種情況:
- 頂點資料是否需要動態更新
- 是否需要繫結到流輸出
如果頂點緩衝區在建立的時候提供了 D3D11_SUBRESOURCE_DATA
來完成初始化,並且之後都不需要更新,則可以使用 D3D11_USAGE_IMMUTABLE
。
如果頂點緩衝區需要頻繁更新,則可以使用 D3D11_USAGE_DYNAMIC
,並允許CPU寫入( D3D11_CPU_ACCESS_WRITE
)。
如果頂點緩衝區需要繫結到流輸出,則說明頂點緩衝區需要允許GPU寫入,可以使用 D3D11_USAGE_DEFAULT
,並且需要提供繫結標籤 D3D11_BIND_STREAM_OUTPUT
。
下圖說明了頂點緩衝區可以繫結的位置:
頂點緩衝區不需要建立資源檢視,它可以直接繫結到輸入裝配階段或流輸出階段。
建立頂點緩衝區和一般的建立緩衝區函式如下:
// ------------------------------ // CreateBuffer函式 // ------------------------------ // 建立緩衝區 // [In]d3dDeviceD3D裝置 // [In]data初始化結構化資料 // [In]byteWidth緩衝區位元組數 // [Out]structuredBuffer輸出的結構化緩衝區 // [In]usage資源用途 // [In]bindFlags資源繫結標籤 // [In]cpuAccessFlags資源CPU訪問許可權標籤 // [In]structuredByteStride 每個結構體的位元組數 // [In]miscFlags資源雜項標籤 HRESULT CreateBuffer( ID3D11Device * d3dDevice, void * data, UINT byteWidth, ID3D11Buffer ** buffer, D3D11_USAGE usage, UINT bindFlags, UINT cpuAccessFlags, UINT structureByteStride, UINT miscFlags) { D3D11_BUFFER_DESC bufferDesc; bufferDesc.Usage = usage; bufferDesc.ByteWidth = byteWidth; bufferDesc.BindFlags = bindFlags; bufferDesc.CPUAccessFlags = cpuAccessFlags; bufferDesc.StructureByteStride = structureByteStride; bufferDesc.MiscFlags = miscFlags; D3D11_SUBRESOURCE_DATA initData; ZeroMemory(&initData, sizeof(initData)); initData.pSysMem = data; return d3dDevice->CreateBuffer(&bufferDesc, &initData, buffer); } // ------------------------------ // CreateVertexBuffer函式 // ------------------------------ // [In]d3dDeviceD3D裝置 // [In]data初始化資料 // [In]byteWidth緩衝區位元組數 // [Out]vertexBuffer輸出的頂點緩衝區 // [InOpt]dynamic是否需要CPU經常更新 // [InOpt]streamOutput是否還用於流輸出階段(不能與dynamic同時設為true) HRESULT CreateVertexBuffer( ID3D11Device * d3dDevice, void * data, UINT byteWidth, ID3D11Buffer ** vertexBuffer, bool dynamic, bool streamOutput) { UINT bindFlags = D3D11_BIND_VERTEX_BUFFER; D3D11_USAGE usage; UINT cpuAccessFlags = 0; if (dynamic && streamOutput) { return E_INVALIDARG; } else if (!dynamic && !streamOutput) { usage = D3D11_USAGE_IMMUTABLE; } else if (dynamic) { usage = D3D11_USAGE_DYNAMIC; cpuAccessFlags |= D3D11_CPU_ACCESS_WRITE; } else { bindFlags |= D3D11_BIND_STREAM_OUTPUT; usage = D3D11_USAGE_DEFAULT; } return CreateBuffer(d3dDevice, data, byteWidth, vertexBuffer, usage, bindFlags, cpuAccessFlags, 0, 0); }
索引緩衝區(Index Buffer)
索引緩衝區通常需要與頂點緩衝區結合使用,它的作用就是以索引的形式來引用頂點緩衝區中的某一頂點,並按索引緩衝區的順序和圖元型別來組裝圖元。它可以有效地減少頂點緩衝區中重複的頂點資料,從而減小網格模型佔用的資料大小。使用相同的索引值就可以多次引用同一個頂點。
索引緩衝區的使用不需要建立資源檢視,它僅用於輸入裝配階段,並且在裝配的時候你需要指定每個索引所佔的位元組數:
DXGI_FORMAT | 位元組數 | 索引範圍 |
---|---|---|
DXGI_FORMAT_R8_UINT | 1 | 0-255 |
DXGI_FORMAT_R16_UINT | 2 | 0-65535 |
DXGI_FORMAT_R32_UINT | 4 | 0-2147483647 |
將索引緩衝區繫結到輸入裝配階段後,你就可以用帶Indexed的Draw方法,指定起始索引偏移值和索引數目來進行繪製。
CreateIndexBuffer函式--建立索引緩衝區
索引緩衝區的建立只考慮資料是否需要動態更新。
如果索引緩衝區在建立的時候提供了 D3D11_SUBRESOURCE_DATA
來完成初始化,並且之後都不需要更新,則可以使用 D3D11_USAGE_IMMUTABLE
如果索引緩衝區需要頻繁更新,則可以使用 D3D11_USAGE_DYNAMIC
,並允許CPU寫入( D3D11_CPU_ACCESS_WRITE
)。
// ------------------------------ // CreateIndexBuffer函式 // ------------------------------ // [In]d3dDeviceD3D裝置 // [In]data初始化資料 // [In]byteWidth緩衝區位元組數 // [Out]indexBuffer輸出的索引緩衝區 // [InOpt]dynamic是否需要CPU經常更新 HRESULT CreateIndexBuffer( ID3D11Device * d3dDevice, void * data, UINT byteWidth, ID3D11Buffer ** indexBuffer, bool dynamic) { D3D11_USAGE usage; UINT cpuAccessFlags = 0; if (dynamic) { usage = D3D11_USAGE_DYNAMIC; cpuAccessFlags |= D3D11_CPU_ACCESS_WRITE; } else { usage = D3D11_USAGE_IMMUTABLE; } return CreateBuffer(d3dDevice, data, byteWidth, indexBuffer, usage, D3D11_BIND_INDEX_BUFFER, cpuAccessFlags, 0, 0); }
常量緩衝區(Constant Buffer)
常量緩衝區是我們接觸到的第一個可以給所有可程式設計著色器程式使用的緩衝區。由於著色器函式的形參沒法從C++端傳入,我們只能通過類似全域性變數的方式來讓著色器函式訪問,這些引數被打包在一個常量緩衝區中。而C++可以通過建立對應的常量緩衝區來繫結到HLSL對應的 cbuffer
,以實現從C++到HLSL的資料的傳遞。C++的常量緩衝區是以位元組流來對待;而HLSL的 cbuffer
內部可以像結構體那樣包含各種型別的引數,而且還需要注意它的打包規則。
關於常量緩衝區,有太多值得需要注意的細節了:
- 每個著色器階段最多允許15個常量緩衝區,並且每個緩衝區最多可以容納4096個標量。HLSL的
cbuffer
需要指定register(b#)
,#
的範圍為0到14 - 在C++建立常量緩衝區時大小必須為16位元組的倍數,因為HLSL的常量緩衝區本身以及對它的讀寫操作需要嚴格按16位元組對齊
- 對常量緩衝區的成員使用
packoffset
修飾符可以指定起始向量和分量位置 - 在更新常量緩衝區時由於資料是提交完整的位元組流資料到GPU,會導致HLSL中
cbuffer
的所有成員都被更新。為了減少不必要的更新,可以根據這些引數的更新頻率劃分出多個常量緩衝區以節省頻寬資源 - 一個著色器在使用了多個常量緩衝區的情況下,這些常量緩衝區不能出現同名引數
- 單個常量緩衝區可以同時繫結到不同的可程式設計著色器階段,因為這些緩衝區都是隻讀的,不會導致記憶體訪問衝突。一個包含常量緩衝區的
*.hlsli
檔案同時被多個著色器檔案引用,只是說明這些著色器使用相同的常量緩衝區佈局,如果該緩衝區需要在多個著色器階段使用,你還需要在C++同時將相同的常量緩衝區繫結到各個著色器階段上
下面是一個HLSL常量緩衝區的例子(註釋部分可省略,效果等價):
cbuffer CBChangesRarely : register(b2) { matrix gView /* : packoffset(c0) */; float3 gSphereCenter /* : packoffset(c4.x) */; float gSphereRadius /* : packoffset(c4.w) */; float3 gEyePosW /* : packoffset(c5.x) */; float gPad /* : packoffset(c5.w) */; }
CreateConstantBuffer函式--建立常量緩衝區
常量緩衝區的建立需要區分下面兩種情況:
- 是否需要CPU經常更新
- 是否需要GPU更新
如果常量緩衝區在建立的時候提供了 D3D11_SUBRESOURCE_DATA
來完成初始化,並且之後都不需要更新,則可以使用 D3D11_USAGE_IMMUTABLE
。
如果常量緩衝區需要頻繁更新,則可以使用 D3D11_USAGE_DYNAMIC
,並允許CPU寫入( D3D11_CPU_ACCESS_WRITE
)。
如果常量緩衝區在較長的一段時間才需要更新一次,則可以考慮使用 D3D11_USAGE_DEFAULT
。
下圖說明了常量緩衝區可以繫結的位置:
常量緩衝區的使用同樣不需要建立資源檢視。
// ------------------------------ // CreateConstantBuffer函式 // ------------------------------ // [In]d3dDeviceD3D裝置 // [In]data初始化資料 // [In]byteWidth緩衝區位元組數,必須是16的倍數 // [Out]indexBuffer輸出的索引緩衝區 // [InOpt]cpuUpdates是否允許CPU更新 // [InOpt]gpuUpdates是否允許GPU更新 HRESULT CreateConstantBuffer( ID3D11Device * d3dDevice, void * data, UINT byteWidth, ID3D11Buffer ** constantBuffer, bool cpuUpdates, bool gpuUpdates) { D3D11_USAGE usage; UINT cpuAccessFlags = 0; if (cpuUpdates && gpuUpdates) { return E_INVALIDARG; } else if (!cpuUpdates && !gpuUpdates) { usage = D3D11_USAGE_IMMUTABLE; } else if (cpuUpdates) { usage = D3D11_USAGE_DYNAMIC; cpuAccessFlags |= D3D11_CPU_ACCESS_WRITE; } else { usage = D3D11_USAGE_DEFAULT; } return CreateBuffer(d3dDevice, data, byteWidth, constantBuffer, usage, D3D11_BIND_CONSTANT_BUFFER, cpuAccessFlags, 0, 0); }
有型別的緩衝區(Typed Buffer)
這是一種建立和使用起來最簡單的緩衝區,但實際使用頻率遠不如上面所講的三種緩衝區。它的資料可以在HLSL被解釋成基本HLSL型別的陣列形式。
在HLSL中,如果是隻讀的緩衝區型別,則宣告方式如下:
Buffer<float4> gBuffer : register(t0);
需要留意的是,當前緩衝區和紋理需要共用紋理暫存器,即t#,因此要注意和紋理避開使用同一個暫存器槽。
如果是可讀寫的緩衝區型別,則宣告方式如下:
RWBuffer<float4> gRWBuffer : register(u0);
有型別的緩衝區具有下面的方法:
方法 | 作用 |
---|---|
void GetDimensions(out uint) | 獲取資源各個維度下的大小 |
T Load(in int) | 按一維索引讀取緩衝區資料 |
T Operator | Buffer 僅允許讀取, RWBuffer 允許讀寫 |
有型別的緩衝區需要建立著色器資源檢視以繫結到對應的著色器階段。由於HLSL的語法知識定義了有限的型別和元素數目,但在 DXGI_FORMAT
中,有許多種成員都能夠用於匹配一種HLSL型別。比如,HLSL的 float4
你可以使用 DXGI_FORMAT_R32G32B32A32_FLOAT
, DXGI_FORMAT_R16G16B16A16_FLOAT
或 DXGI_FORMAT_R8G8B8A8_UNORM
。而HLSL的 int2
你可以使用 DXGI_FORMAT_R32G32_SINT
, DXGI_FORMAT_R16G16_SINT
或 DXGI_FORMAT_R8G8_SINT
。
CreateTypedBuffer函式--建立有型別的緩衝區
有型別的緩衝區通常需要繫結到著色器上作為資源使用,因此需要將 bindFlags
設為 D3D11_BIND_SHADER_RESOURCE
。
此外,有型別的緩衝區的建立需要區分下面兩種情況:
- 是否允許CPU寫入/讀取
- 是否允許GPU寫入
如果緩衝區在建立的時候提供了 D3D11_SUBRESOURCE_DATA
來完成初始化,並且之後都不需要更新,則可以使用 D3D11_USAGE_IMMUTABLE
。
如果緩衝區需要頻繁更新,則可以使用 D3D11_USAGE_DYNAMIC
,並允許CPU寫入( D3D11_CPU_ACCESS_WRITE
)。
如果緩衝區需要允許GPU寫入,說明後面可能需要建立UAV繫結到 RWBuffer<T>
,為此還需要給 bindFlags
新增 D3D11_BIND_UNORDERED_ACCESS
。
如果緩衝區的資料需要讀出到記憶體,則可以使用 D3D11_USAGE_STAGING
,並允許CPU讀取( D3D11_CPU_ACCESS_READ
)。
下圖說明了有型別的(與結構化)緩衝區可以繫結的位置:
// ------------------------------ // CreateTypedBuffer函式 // ------------------------------ // [In]d3dDeviceD3D裝置 // [In]data初始化資料 // [In]byteWidth緩衝區位元組數 // [Out]typedBuffer輸出的有型別的緩衝區 // [InOpt]cpuUpdates是否允許CPU更新 // [InOpt]gpuUpdates是否允許使用RWBuffer HRESULT CreateTypedBuffer( ID3D11Device * d3dDevice, void * data, UINT byteWidth, ID3D11Buffer ** typedBuffer, bool cpuUpdates, bool gpuUpdates) { UINT bindFlags = D3D11_BIND_SHADER_RESOURCE; D3D11_USAGE usage; UINT cpuAccessFlags = 0; if (cpuUpdates && gpuUpdates) { bindFlags = 0; usage = D3D11_USAGE_STAGING; cpuAccessFlags |= D3D11_CPU_ACCESS_READ; } else if (!cpuUpdates && !gpuUpdates) { usage = D3D11_USAGE_IMMUTABLE; } else if (cpuUpdates) { usage = D3D11_USAGE_DYNAMIC; cpuAccessFlags |= D3D11_CPU_ACCESS_WRITE; } else { usage = D3D11_USAGE_DEFAULT; bindFlags |= D3D11_BIND_UNORDERED_ACCESS; } return CreateBuffer(d3dDevice, data, byteWidth, typedBuffer, usage, bindFlags, cpuAccessFlags, 0, 0); }
關於追加/消耗緩衝區,我們後面再討論。
如果我們希望它作為 Buffer<float4>
使用,則需要建立著色器資源檢視:
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; srvDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; srvDesc.Buffer.FirstElement = 0;// 起始元素的索引 srvDesc.Buffer.NumElements = numElements;// 元素數目 HR(md3dDevice->CreateShaderResourceView(mBuffer.Get(), &srvDesc, mBufferSRV.GetAddressOf()));
而如果我們希望它作為 RWBuffer<float4>
使用,則需要建立無序訪問檢視:
D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc; uavDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; uavDesc.Buffer.FirstElement = 0;// 起始元素的索引 uavDesc.Buffer.Flags = 0; uavDesc.Buffer.NumElements = numElements;// 元素數目 md3dDevice->CreateUnorderedAccessView(mBuffer.Get(), &uavDesc, mBufferUAV.GetAddressOf());
結構化緩衝區(Structured Buffer)
結構化緩衝區可以說是緩衝區的複合形式,它允許模板型別 T
是使用者自定義的型別,即緩衝區存放的內容可以被解釋為結構體陣列。
現在HLSL有如下結構體:
struct Data { float3 v1; float2 v2; };
如果是隻讀的結構化緩衝區,則宣告方式如下:
StructuredBuffer<Data> gStructuredBuffer : register(t0);
如果是可讀寫的結構化緩衝區型別,則宣告方式如下:
RWStructuredBuffer<Data> gRWStructuredBuffer : register(u0);
結構化緩衝區也具有下面的方法:
方法 | 作用 |
---|---|
void GetDimensions(out uint) | 獲取資源各個維度下的大小 |
T Load(in int) | 按一維索引讀取結構化緩衝區資料 |
T Operator | StructuredBuffer 僅允許讀取, RWStructuredBuffer 允許讀寫 |
CreateStructuredBuffer函式--建立結構化緩衝區
結構化緩衝區的建立和有型別的緩衝區建立比較相似,區別在於:
- 需要在
MiscFlags
指定D3D11_RESOURCE_MISC_BUFFER_STRUCTURED
- 需要額外提供
structureByteStride
說明結構體的大小
// ------------------------------ // CreateStructuredBuffer函式 // ------------------------------ // 如果需要建立Append/Consume Buffer,需指定cpuUpdates為false, gpuUpdates為true // [In]d3dDeviceD3D裝置 // [In]data初始化資料 // [In]byteWidth緩衝區位元組數 // [In]structuredByteStride 每個結構體的位元組數 // [Out]structuredBuffer輸出的結構化緩衝區 // [InOpt]cpuUpdates是否允許CPU更新 // [InOpt]gpuUpdates是否允許使用RWStructuredBuffer HRESULT CreateStructuredBuffer( ID3D11Device * d3dDevice, void * data, UINT byteWidth, UINT structuredByteStride, ID3D11Buffer ** structuredBuffer, bool cpuUpdates, bool gpuUpdates) { UINT bindFlags = D3D11_BIND_SHADER_RESOURCE; D3D11_USAGE usage; UINT cpuAccessFlags = 0; if (cpuUpdates && gpuUpdates) { bindFlags = 0; usage = D3D11_USAGE_STAGING; cpuAccessFlags |= D3D11_CPU_ACCESS_READ; } else if (!cpuUpdates && !gpuUpdates) { usage = D3D11_USAGE_IMMUTABLE; } else if (cpuUpdates) { usage = D3D11_USAGE_DYNAMIC; cpuAccessFlags |= D3D11_CPU_ACCESS_WRITE; } else { usage = D3D11_USAGE_DEFAULT; bindFlags |= D3D11_BIND_UNORDERED_ACCESS; } return CreateBuffer(d3dDevice, data, byteWidth, structuredBuffer, usage, bindFlags, cpuAccessFlags, structuredByteStride, D3D11_RESOURCE_MISC_BUFFER_STRUCTURED); }
無論是SRV還是UAV,在指定 Format
時只能指定 DXGI_FORMAT_UNKNOWN
。
如果我們希望它作為 StructuredBuffer<Data>
使用,則需要建立著色器資源檢視:
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; srvDesc.Format = DXGI_FORMAT_UNKNOWN; srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; srvDesc.Buffer.FirstElement = 0;// 起始元素的索引 srvDesc.Buffer.NumElements = numElements;// 元素數目 HR(md3dDevice->CreateShaderResourceView(mBuffer.Get(), &srvDesc, mBufferSRV.GetAddressOf()));
而如果我們希望它作為 RWStructuredBuffer<float4>
使用,則需要建立無序訪問檢視:
D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc; uavDesc.Format = DXGI_FORMAT_UNKNOWN; uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; uavDesc.Buffer.FirstElement = 0;// 起始元素的索引 uavDesc.Buffer.Flags = 0; uavDesc.Buffer.NumElements = numElements;// 元素數目 md3dDevice->CreateUnorderedAccessView(mBuffer.Get(), &uavDesc, mBufferUAV.GetAddressOf());
追加/消耗緩衝區(Append/Consume Buffer)
追加緩衝區和消耗緩衝區型別實際上是結構化緩衝區的特殊變體資源。因為涉及到修改操作,它們都只能以無序訪問檢視的方式來使用。如果你只是希望這些結構體資料經過著色器變換並且不需要考慮最終的輸出順序要一致,那麼使用這兩個緩衝區是一種不錯的選擇。
ConsumeStructuredBuffer<float3> gVertexIn : register(u0); AppendStructuredBuffer<float3> gVertexOut : register(u1);
在HLSL中, AppendStructuredBuffer
僅提供了 Append
方法用於尾端追加成員;而 ConsumeStructuredBuffer
則僅提供了 Consume
方法用於消耗尾端成員。這兩種操作實際上可以看做是對棧的操作。此外,你也可以使用 GetDimensions
方法來獲取當前緩衝區還剩下多少元素。
一旦某個執行緒消耗了一個數據元素,就不能再被另一個執行緒給消耗掉,並且一個執行緒將只消耗一個數據。需要注意的是,因為執行緒之間的執行順序是不確定的,因此無法根據執行緒ID來確定當前消耗的是哪個索引的資源。
此外,追加/消耗緩衝區實際上並不能動態增長,你必須在建立緩衝區的時候就要分配好足夠大的空間。
追加/消耗緩衝區的建立
追加/消耗緩衝區可以經由 CreateStructuredBuffer
函式來建立,需要指定 cpuUpdates
為 false
, gpuUpdates
為 true
.
比較關鍵的是UAV的建立,需要像結構化緩衝區一樣指定 Format
為 DXGI_FORMAT_UNKNOWN
。並且無論是追加緩衝區,還是消耗緩衝區,都需要在 Buffer.Flags
中指定 D3D11_BUFFER_UAV_FLAG_APPEND
:
D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc; uavDesc.Format = DXGI_FORMAT_UNKNOWN; uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; uavDesc.Buffer.FirstElement = 0;// 起始元素的索引 uavDesc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_APPEND; uavDesc.Buffer.NumElements = numElements;// 元素數目 HR(md3dDevice->CreateUnorderedAccessView(mVertexInput.Get(), &uavDesc, mVertexInputUAV.GetAddressOf()));
然後在將UAV繫結到著色器時,如果是追加緩衝區,通常需要指定初始元素數目為0,然後提供給 ID3D11DeviceContext::*SSetUnorderedAccessViews
方法的最後一個引數:
UINT initCounts[1] = { 0 }; md3dImmediateContext->CSSetUnorderedAccessViews(0, 1, mVertexInputUAV.GetAddressOf(), initCounts);
而如果是消耗緩衝區,則需要指定初始元素數目:
UINT initCounts[1] = { numElements }; md3dImmediateContext->CSSetUnorderedAccessViews(1, 1, mVertexInputUAV.GetAddressOf(), initCounts);
(未完待續)
DirectX11 With Windows SDK完整目錄