Creazione di heap del descrittore

Per creare e configurare un heap del descrittore, è necessario selezionare un tipo di heap descrittore, determinare il numero di descrittori contenuti e impostare i flag che indicano se è visibile o meno la CPU e/o lo shader visibile.

Tipi heap del descrittore

Il tipo di heap è determinato da un membro dell'enumerazione 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;

Proprietà dell'heap del descrittore

Le proprietà dell'heap vengono impostate sulla struttura D3D12_DESCRIPTOR_HEAP_DESC , che fa riferimento sia alle enumerazioni D3D12_DESCRIPTOR_HEAP_TYPE che alle D3D12_DESCRIPTOR_HEAP_FLAGS.

Il flag D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE può essere impostato facoltativamente in un heap del descrittore per indicare che è associato a un elenco di comandi per riferimento da shader. Gli heap del descrittore creati senza questo flag consentono alle applicazioni di preparare i descrittori nella memoria della CPU prima di copiarli in un heap descrittore visibile dello shader, per praticità. Tuttavia, è anche consigliabile che le applicazioni creino direttamente descrittori in heap del descrittore visibile dello shader senza dover eseguire operazioni di staging sulla CPU.

Questo flag si applica solo ai campionatori CBV, SRV, UAV e sampler. Non si applica ad altri tipi di heap descrittore poiché gli shader non fanno riferimento direttamente agli altri tipi.

Ad esempio, descrivere e creare un heap del descrittore di campionatore.

// 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)));

Descrivere e creare una visualizzazione buffer costante (CBV), una visualizzazione risorse shader (SRV) e un heap del descrittore UAV (Unordered Access View).

// 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);

Handle del descrittore

Le strutture D3D12_GPU_DESCRIPTOR_HANDLE e D3D12_CPU_DESCRIPTOR_HANDLE identificano descrittori specifici in un heap del descrittore. Un handle è un po' come un puntatore, ma l'applicazione non deve dereferenziarla manualmente; in caso contrario, il comportamento non è definito. L'uso degli handle deve passare attraverso l'API. Un handle stesso può essere copiato liberamente o passato nelle API che operano sui descrittori/usare. Non esiste alcun conteggio dei riferimenti, pertanto l'applicazione deve assicurarsi che non usi un handle dopo l'eliminazione dell'heap del descrittore sottostante.

Le applicazioni possono individuare le dimensioni di incremento dei descrittori per un determinato tipo di heap descrittore, in modo che possano generare handle in qualsiasi posizione in un heap descrittore a partire manualmente dall'handle alla base. Le applicazioni non devono mai gestire dimensioni di incremento del descrittore hardcode e devono sempre eseguire query per una determinata istanza del dispositivo; in caso contrario, il comportamento non è definito. Le applicazioni non devono inoltre utilizzare le dimensioni di incremento e gli handle per eseguire il proprio esame o manipolazione dei dati dell'heap descrittore, in quanto i risultati di questa operazione non sono definiti. Gli handle non possono essere effettivamente usati come puntatori, ma piuttosto come proxy per i puntatori in modo da evitare la dereferenziazione accidentale.

Nota

Esiste una struttura helper, CD3DX12_GPU_DESCRIPTOR_HANDLE, definita nell'intestazione d3dx12.h, che eredita la struttura D3D12_GPU_DESCRIPTOR_HANDLE e fornisce l'inizializzazione e altre operazioni utili. Analogamente, la struttura helper CD3DX12_CPU_DESCRIPTOR_HANDLE è definita per la struttura D3D12_CPU_DESCRIPTOR_HANDLE .

 

Entrambe queste strutture helper vengono usate durante il popolamento degli elenchi di comandi.

// 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());
}

Metodi heap del descrittore

Gli heap descrittori (ID3D12DescriptorHeap) ereditano da ID3D12Pageable. Ciò impone la responsabilità della gestione della residenza degli heap del descrittore nelle applicazioni, proprio come gli heap delle risorse. I metodi di gestione della residenza si applicano solo agli heap visibili dello shader perché gli heap non visibili dello shader non sono visibili direttamente alla GPU.

Il metodo ID3D12Device::GetDescriptorHandleIncrementSize consente alle applicazioni di sfalsare manualmente gli handle in un heap (producendo handle in qualsiasi punto di un heap del descrittore). L'handle del percorso di avvio dell'heap proviene da ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart/ID3D12DescriptorHeap::GetGPUDescriptorHandleForHeapStart. L'offset viene eseguito aggiungendo le dimensioni di incremento * il numero di descrittori da sfalsare all'heap del descrittore iniziare . Si noti che le dimensioni dell'incremento non possono essere considerate come dimensioni di byte perché le applicazioni non devono dereferenziare gli handle come se fossero memoria. La memoria a cui punta ha un layout non standardizzato e può variare anche per un determinato dispositivo.

GetCPUDescriptorHandleForHeapStart restituisce un handle cpu per gli heap del descrittore visibile della CPU. Restituisce un handle NULL e il livello di debug segnala un errore se l'heap del descrittore non è visibile alla CPU.

GetGPUDescriptorHandleForHeapStart restituisce un handle GPU per gli heap del descrittore visibile dello shader. Restituisce un handle NULL e il livello di debug segnala un errore se l'heap del descrittore non è visibile.

Ad esempio, la creazione di visualizzazioni di destinazione di rendering per visualizzare testo D2D usando un dispositivo 11on12.

    // 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])));
        }
    
    }

Wrapper heap del descrittore minimo

È probabile che gli sviluppatori di applicazioni vogliano creare il proprio codice helper per la gestione degli handle e degli heap del descrittore. Di seguito è riportato un esempio di base. I wrapper più sofisticati potrebbero, ad esempio, tenere traccia dei tipi di descrittori in cui si trovano in un heap e archiviare gli argomenti di creazione del descrittore.

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;
};

Heap descrittore