建立描述符堆
若要建立和設定描述元堆積,您必須選取描述元堆積類型、判斷它包含多少描述元,以及設定旗標,指出其是否為 CPU 可見和/或著色器可見。
描述符堆類型
堆的類型是由 D3D12_DESCRIPTOR_HEAP_TYPE 列舉中的一個成員決定的:
typedef enum D3D12_DESCRIPTOR_HEAP_TYPE
{
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, // Constant buffer/Shader resource/Unordered access views
D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, // Samplers
D3D12_DESCRIPTOR_HEAP_TYPE_RTV, // Render target view
D3D12_DESCRIPTOR_HEAP_TYPE_DSV, // Depth stencil view
D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES // Simply the number of descriptor heap types
} D3D12_DESCRIPTOR_HEAP_TYPE;
描述項堆積屬性
堆積屬性是在 D3D12_DESCRIPTOR_HEAP_DESC 結構上設定,其會參考 D3D12_DESCRIPTOR_HEAP_TYPE 和 D3D12_DESCRIPTOR_HEAP_FLAGS 列舉。
旗標 D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE 可選擇設定在描述符堆上,以指示它綁定在命令列表中以供著色器參考。 在沒有 旗標的情況下建立的 描述堆積,允許應用程式選擇先將描述項暫存於 CPU 記憶體中,然後再方便地將其複製到著色器可見的描述堆積中。 但應用程式也可以直接在著色器可見的描述符堆中建立描述符,而不需要在 CPU 上暫存任何資料。
此旗標僅適用於 CBV、SRV、UAV 和取樣器。 它不適用於其他描述元堆積類型,因為著色器不會直接參考其他類型。
例如,描述並建立取樣器描述元堆積。
// Describe and create a sampler descriptor heap.
D3D12_DESCRIPTOR_HEAP_DESC samplerHeapDesc = {};
samplerHeapDesc.NumDescriptors = 1;
samplerHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
samplerHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
ThrowIfFailed(m_device->CreateDescriptorHeap(&samplerHeapDesc, IID_PPV_ARGS(&m_samplerHeap)));
描述並建立常數緩衝區檢視 (CBV)、著色器資源檢視 (SRV),以及未排序的存取檢視 (UAV) 描述元堆積。
// Describe and create a shader resource view (SRV) and unordered
// access view (UAV) descriptor heap.
D3D12_DESCRIPTOR_HEAP_DESC srvUavHeapDesc = {};
srvUavHeapDesc.NumDescriptors = DescriptorCount;
srvUavHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
srvUavHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
ThrowIfFailed(m_device->CreateDescriptorHeap(&srvUavHeapDesc, IID_PPV_ARGS(&m_srvUavHeap)));
m_rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
m_srvUavDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
描述符句柄
D3D12_GPU_DESCRIPTOR_HANDLE 和 D3D12_CPU_DESCRIPTOR_HANDLE 結構會識別描述元堆積中的特定描述項。 句柄有點像指標,但應用程式不得手動取值;否則,行為是未定義的。 使用操作介面必須經過 API。 句柄本身可以自由複製或傳遞至在/使用描述元上運作的 API。 沒有 ref 計數,因此應用程式必須確保它不會在刪除基礎描述元堆積之後使用句柄。
應用程式可以找出特定描述符堆積類型的描述符遞增大小,以便從基礎描述符的句柄手動生成堆積中任何位置的句柄。 應用程式絕對不能硬式編碼描述元處理遞增大小,而且應該一律查詢指定的裝置實例;否則,行為是未定義的。 應用程式也不得使用遞增大小和句柄來執行自己的檢查或作描述元堆積數據,因為這樣做的結果未定義。 句柄可能實際上並不是用來當作指標,而是作為指標的代理,以避免意外存取。
注意
標頭檔 d3dx12.h 中定義了輔助結構 CD3DX12_GPU_DESCRIPTOR_HANDLE,它繼承了 D3D12_GPU_DESCRIPTOR_HANDLE 結構,並提供初始化和其他實用操作。 同樣地,CD3DX12_CPU_DESCRIPTOR_HANDLE協助程序結構是針對 D3D12_CPU_DESCRIPTOR_HANDLE 結構所定義。
這兩個協助程序結構會在填入命令清單時使用。
// Fill the command list with all the render commands and dependent state.
void D3D12nBodyGravity::PopulateCommandList()
{
// Command list allocators can only be reset when the associated
// command lists have finished execution on the GPU; apps should use
// fences to determine GPU execution progress.
ThrowIfFailed(m_commandAllocators[m_frameIndex]->Reset());
// However, when ExecuteCommandList() is called on a particular command
// list, that command list can then be reset at any time and must be before
// re-recording.
ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_frameIndex].Get(), m_pipelineState.Get()));
// Set necessary state.
m_commandList->SetPipelineState(m_pipelineState.Get());
m_commandList->SetGraphicsRootSignature(m_rootSignature.Get());
m_commandList->SetGraphicsRootConstantBufferView(RootParameterCB, m_constantBufferGS->GetGPUVirtualAddress() + m_frameIndex * sizeof(ConstantBufferGS));
ID3D12DescriptorHeap* ppHeaps[] = { m_srvUavHeap.Get() };
m_commandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps);
m_commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView);
m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_POINTLIST);
m_commandList->RSSetScissorRects(1, &m_scissorRect);
// Indicate that the back buffer will be used as a render target.
m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart(), m_frameIndex, m_rtvDescriptorSize);
m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
// Record commands.
const float clearColor[] = { 0.0f, 0.0f, 0.1f, 0.0f };
m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
// Render the particles.
float viewportHeight = static_cast<float>(static_cast<UINT>(m_viewport.Height) / m_heightInstances);
float viewportWidth = static_cast<float>(static_cast<UINT>(m_viewport.Width) / m_widthInstances);
for (UINT n = 0; n < ThreadCount; n++)
{
const UINT srvIndex = n + (m_srvIndex[n] == 0 ? SrvParticlePosVelo0 : SrvParticlePosVelo1);
D3D12_VIEWPORT viewport;
viewport.TopLeftX = (n % m_widthInstances) * viewportWidth;
viewport.TopLeftY = (n / m_widthInstances) * viewportHeight;
viewport.Width = viewportWidth;
viewport.Height = viewportHeight;
viewport.MinDepth = D3D12_MIN_DEPTH;
viewport.MaxDepth = D3D12_MAX_DEPTH;
m_commandList->RSSetViewports(1, &viewport);
CD3DX12_GPU_DESCRIPTOR_HANDLE srvHandle(m_srvUavHeap->GetGPUDescriptorHandleForHeapStart(), srvIndex, m_srvUavDescriptorSize);
m_commandList->SetGraphicsRootDescriptorTable(RootParameterSRV, srvHandle);
m_commandList->DrawInstanced(ParticleCount, 1, 0, 0);
}
m_commandList->RSSetViewports(1, &m_viewport);
// Indicate that the back buffer will now be used to present.
m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));
ThrowIfFailed(m_commandList->Close());
}
描述符堆方法
描述符堆積(ID3D12DescriptorHeap)繼承自 ID3D12Pageable。 這會將描述符堆的管理責任強加於應用程式上,就像資源堆一樣。 駐留管理方法僅適用於著色器可見記憶體堆疊,因為非著色器可見記憶體堆疊對 GPU 是不可見的。
ID3D12Device::GetDescriptorHandleIncrementSize 方法可讓應用程式手動將句柄位移到堆積中(產生句柄到描述元堆積中的任何位置)。 堆積啟動位置的句柄來自 ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart/ID3D12DescriptorHeap::GetGPUDescriptorHandleForHeapStart。 透過新增(遞增大小 * 要位移的描述項數目)到描述項堆積的起始位置來完成位移。 請注意,遞增大小無法視為位元組大小,因為應用程式不能像記憶體一樣取值句柄 ,而指向的記憶體具有非標準化的配置,而且即使在指定的裝置上也會有所不同。
GetCPUDescriptorHandleForHeapStart 會傳回 CPU 可見描述元堆的 CPU 控制碼。 如果描述符堆不可由 CPU 可見,它會返回空句柄(且偵錯層會報告錯誤)。
GetGPUDescriptorHandleForHeapStart 返回著色器可見描述堆的 GPU 控制代碼。 如果描述項堆積對著色器不可見,它會傳回NULL控制代碼,而且調試層會回報錯誤。
例如,建立繪圖目標視圖,以使用 11on12 裝置顯示 D2D 文字。
// Create descriptor heaps.
{
// Describe and create a render target view (RTV) descriptor heap.
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
rtvHeapDesc.NumDescriptors = FrameCount;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
ThrowIfFailed(m_d3d12Device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&m_rtvHeap)));
m_rtvDescriptorSize = m_d3d12Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
}
// Create frame resources.
{
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart());
// Create a RTV, D2D render target, and a command allocator for each frame.
for (UINT n = 0; n < FrameCount; n++)
{
ThrowIfFailed(m_swapChain->GetBuffer(n, IID_PPV_ARGS(&m_renderTargets[n])));
m_d3d12Device->CreateRenderTargetView(m_renderTargets[n].Get(), nullptr, rtvHandle);
// Create a wrapped 11On12 resource of this back buffer. Since we are
// rendering all D3D12 content first and then all D2D content, we specify
// the In resource state as RENDER_TARGET - because D3D12 will have last
// used it in this state - and the Out resource state as PRESENT. When
// ReleaseWrappedResources() is called on the 11On12 device, the resource
// will be transitioned to the PRESENT state.
D3D11_RESOURCE_FLAGS d3d11Flags = { D3D11_BIND_RENDER_TARGET };
ThrowIfFailed(m_d3d11On12Device->CreateWrappedResource(
m_renderTargets[n].Get(),
&d3d11Flags,
D3D12_RESOURCE_STATE_RENDER_TARGET,
D3D12_RESOURCE_STATE_PRESENT,
IID_PPV_ARGS(&m_wrappedBackBuffers[n])
));
// Create a render target for D2D to draw directly to this back buffer.
ComPtr<IDXGISurface> surface;
ThrowIfFailed(m_wrappedBackBuffers[n].As(&surface));
ThrowIfFailed(m_d2dDeviceContext->CreateBitmapFromDxgiSurface(
surface.Get(),
&bitmapProperties,
&m_d2dRenderTargets[n]
));
rtvHandle.Offset(1, m_rtvDescriptorSize);
ThrowIfFailed(m_d3d12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocators[n])));
}
}
最小描述元堆積包裝函式
應用程式開發人員可能會想要建置自己的協助程式程式代碼,以管理描述項句柄和堆積。 基本范例如下所示。 例如,更複雜的包裝函式可以追蹤堆積中描述元的類型,並儲存描述元建立自變數。
class CDescriptorHeapWrapper
{
public:
CDescriptorHeapWrapper() { memset(this, 0, sizeof(*this)); }
HRESULT Create(
ID3D12Device* pDevice,
D3D12_DESCRIPTOR_HEAP_TYPE Type,
UINT NumDescriptors,
bool bShaderVisible = false)
{
D3D12_DESCRIPTOR_HEAP_DESC Desc;
Desc.Type = Type;
Desc.NumDescriptors = NumDescriptors;
Desc.Flags = (bShaderVisible ? D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE : D3D12_DESCRIPTOR_HEAP_FLAG_NONE);
HRESULT hr = pDevice->CreateDescriptorHeap(&Desc,
__uuidof(ID3D12DescriptorHeap),
(void**)&pDH);
if (FAILED(hr)) return hr;
hCPUHeapStart = pDH->GetCPUDescriptorHandleForHeapStart();
hGPUHeapStart = pDH->GetGPUDescriptorHandleForHeapStart();
HandleIncrementSize = pDevice->GetDescriptorHandleIncrementSize(Desc.Type);
return hr;
}
operator ID3D12DescriptorHeap*() { return pDH; }
D3D12_CPU_DESCRIPTOR_HANDLE hCPU(UINT index)
{
return hCPUHeapStart.MakeOffsetted(index,HandleIncrementSize);
}
D3D12_GPU_DESCRIPTOR_HANDLE hGPU(UINT index)
{
assert(Desc.Flags&D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE);
return hGPUHeapStart.MakeOffsetted(index,HandleIncrementSize);
}
D3D12_DESCRIPTOR_HEAP_DESC Desc;
CComPtr<ID3D12DescriptorHeap> pDH;
D3D12_CPU_DESCRIPTOR_HANDLE hCPUHeapStart;
D3D12_GPU_DESCRIPTOR_HANDLE hGPUHeapStart;
UINT HandleIncrementSize;
};
相關主題