Erstellen von Deskriptorheaps

Um einen Deskriptorheap zu erstellen und zu konfigurieren, müssen Sie einen Deskriptorheaptyp auswählen, bestimmen, wie viele Deskriptoren darin enthalten sind, und Flags festlegen, die angeben, ob die CPU sichtbar und/oder der Shader sichtbar ist.

Deskriptorheaptypen

Der Heaptyp wird durch ein Element der D3D12_DESCRIPTOR_HEAP_TYPE Enumeration bestimmt:

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;

Deskriptorheapeigenschaften

Heapeigenschaften werden für die D3D12_DESCRIPTOR_HEAP_DESC-Struktur festgelegt, die sowohl auf die D3D12_DESCRIPTOR_HEAP_TYPE als auch auf D3D12_DESCRIPTOR_HEAP_FLAGS Enumerationen verweist.

Das Flag D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE kann optional auf einem Deskriptorheap festgelegt werden, um anzugeben, dass es in einer Befehlsliste zur Referenz durch Shader gebunden ist. Deskriptorheaps, die ohne dieses Flag erstellt werden, ermöglichen Anwendungen die Möglichkeit, Deskriptoren im CPU-Arbeitsspeicher zu inszenieren, bevor sie in einen sichtbaren Deskriptorheap des Shaders kopiert werden, um sie zu vereinfachen. Es ist jedoch auch in Ordnung, dass Anwendungen Deskriptoren direkt in Shader-Heaps für sichtbare Deskriptor-Heaps erstellen, ohne dass etwas auf der CPU bereitgestellt werden muss.

Dieses Flag gilt nur für CBV, SRV, UAV und Sampler. Sie gilt nicht für andere Deskriptorheaptypen, da Shader nicht direkt auf die anderen Typen verweisen.

Beschreiben und erstellen Sie beispielsweise einen Samplerdeskriptorheap.

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

Beschreiben und erstellen Sie eine konstante Pufferansicht (CBV), eine Shader-Ressourcenansicht (SRV) und einen UAV-Deskriptorheap (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);

Deskriptorhandles

Die D3D12_GPU_DESCRIPTOR_HANDLE - und D3D12_CPU_DESCRIPTOR_HANDLE strukturen identifizieren bestimmte Deskriptoren in einem Deskriptorheap. Ein Handle ähnelt einem Zeiger, aber die Anwendung darf es nicht manuell dereferenzieren. Andernfalls ist das Verhalten nicht definiert. Die Verwendung der Handles muss die API durchlaufen. Ein Handle selbst kann frei kopiert oder an APIs übergeben werden, die mit Deskriptoren arbeiten bzw. verwenden. Da keine Ref-Zählung erfolgt, muss die Anwendung sicherstellen, dass sie kein Handle verwendet, nachdem der zugrunde liegende Deskriptorheap gelöscht wurde.

Anwendungen können die Inkrementgröße der Deskriptoren für einen bestimmten Deskriptorheaptyp ermitteln, sodass sie Handles an einer beliebigen Stelle in einem Deskriptorheap manuell vom Handle bis zur Basis generieren können. Anwendungen dürfen inkrementierte Größen niemals hartcodieren und sollten sie immer für ein bestimmtes Gerät instance abfragen. Andernfalls ist das Verhalten nicht definiert. Anwendungen dürfen auch nicht die inkrementierten Größen und Handles verwenden, um ihre eigenen Untersuchungen oder Manipulationen von Deskriptorheapdaten durchzuführen, da die Ergebnisse daraus nicht definiert sind. Die Handles können nicht als Zeiger, sondern als Proxys für Zeiger verwendet werden, um versehentliche Dereferenzierung zu vermeiden.

Hinweis

Im Header d3dx12.h ist eine Hilfsstruktur CD3DX12_GPU_DESCRIPTOR_HANDLE definiert, die die D3D12_GPU_DESCRIPTOR_HANDLE-Struktur erbt und Initialisierung und andere nützliche Vorgänge bereitstellt. Ebenso wird die CD3DX12_CPU_DESCRIPTOR_HANDLE Hilfsstruktur für die D3D12_CPU_DESCRIPTOR_HANDLE-Struktur definiert.

 

Beide Hilfsstrukturen werden beim Auffüllen von Befehlslisten verwendet.

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

Deskriptorheapmethoden

Deskriptorheaps (ID3D12DescriptorHeap) erben von ID3D12Pageable. Dies erzwingt die Verantwortung für die Residency-Verwaltung von Deskriptorheaps für Anwendungen, genau wie Ressourcenheaps. Die Methoden für die Verwaltung von Residenzen gelten nur für sichtbare Shaderheaps, da die sichtbaren Heaps ohne Shader für die GPU nicht direkt sichtbar sind.

Die ID3D12Device::GetDescriptorHandleIncrementSize-Methode ermöglicht Es Anwendungen, Handles manuell in einen Heap zu verschieben (sodass Handles an beliebiger Stelle in einem Deskriptorheap erzeugt werden). Das Handle des Heapstartspeicherorts stammt von ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart/ID3D12DescriptorHeap::GetGPUDescriptorHandleForHeapStart. Der Ausgleich erfolgt durch Hinzufügen der Inkrementgröße * der Anzahl der Deskriptoren, die dem Deskriptorheapstart versetzt werden sollen. Beachten Sie, dass die Inkrementgröße nicht als Bytegröße betrachtet werden kann, da Anwendungen die Dereferenzierung nicht so behandeln dürfen, als ob es sich um Arbeitsspeicher handelt. Der Speicher, auf den verwiesen wird, weist ein nicht standardisiertes Layout auf und kann auch für ein bestimmtes Gerät variieren.

GetCPUDescriptorHandleForHeapStart gibt ein CPU-Handle für sichtbare CPU-Deskriptorheaps zurück. Es gibt ein NULL-Handle zurück (und die Debugebene meldet einen Fehler), wenn der Deskriptorheap nicht für die CPU sichtbar ist.

GetGPUDescriptorHandleForHeapStart gibt ein GPU-Handle für shader visible descriptor heaps zurück. Es gibt ein NULL-Handle zurück (und die Debugebene meldet einen Fehler), wenn der Deskriptorheap nicht sichtbar ist.

Beispiel: Erstellen von Renderzielansichten zum Anzeigen von D2D-Text mithilfe eines 11on12-Geräts.

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

Heap-Wrapper für minimale Deskriptor

Anwendungsentwickler werden wahrscheinlich ihren eigenen Hilfscode zum Verwalten von Deskriptorhandles und Heaps erstellen möchten. Es folgt ein einfaches Beispiel. Komplexere Wrapper könnten beispielsweise nachverfolgen, welche Deskriptoren sich in einem Heap befinden, und die Deskriptorerstellungsargumente speichern.

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

Deskriptorheaps