Bagikan melalui


Membuat Timbunan Deskriptor

Untuk membuat dan mengonfigurasi tumpukan deskriptor, Anda harus memilih jenis tumpukan deskriptor, menentukan berapa banyak deskriptor yang dikandungnya, dan mengatur bendera yang menunjukkan apakah CPU terlihat dan/atau shader terlihat.

Jenis Tumpuk Deskriptor

Jenis tumpukan ditentukan oleh satu anggota enum 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;

Properti Timbunan Deskriptor

Properti heap diatur pada struktur D3D12_DESCRIPTOR_HEAP_DESC , yang mereferensikan enum D3D12_DESCRIPTOR_HEAP_TYPE dan D3D12_DESCRIPTOR_HEAP_FLAGS .

Bendera D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE secara opsional dapat diatur pada tumpukan deskriptor untuk menunjukkan bahwa bendera tersebut terikat pada daftar perintah untuk referensi oleh shader. Tumpukan deskriptor yang dibuat tanpa bendera ini memungkinkan aplikasi untuk menahapkan deskriptor dalam memori CPU sebelum menyalinnya ke tumpukan deskriptor yang terlihat shader, sebagai kenyamanan. Tetapi juga baik-baik saja bagi aplikasi untuk langsung membuat deskriptor ke dalam tumpukan deskriptor yang terlihat shader tanpa persyaratan untuk menahapkan apa pun di CPU.

Bendera ini hanya berlaku untuk CBV, SRV, UAV, dan sampler. Ini tidak berlaku untuk jenis tumpukan deskriptor lainnya karena shader tidak secara langsung mereferensikan jenis lain.

Misalnya, jelaskan dan buat timbunan deskriptor sampel.

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

Jelaskan dan buat tampilan buffer konstan (CBV), tampilan sumber daya Shader (SRV), dan tumpukan deskriptor tampilan akses yang tidak diurutkan (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);

Handel Deskriptor

Struktur D3D12_GPU_DESCRIPTOR_HANDLE dan D3D12_CPU_DESCRIPTOR_HANDLE mengidentifikasi deskriptor tertentu dalam tumpuk deskriptor. Handel sedikit seperti penunjuk, tetapi aplikasi tidak boleh mendereferensikannya secara manual; jika tidak, perilaku tidak ditentukan. Penggunaan handel harus melalui API. Handel itu sendiri dapat disalin secara bebas atau diteruskan ke API yang beroperasi pada/menggunakan deskriptor. Tidak ada penghitungan ref, sehingga aplikasi harus memastikan bahwa aplikasi tidak menggunakan handel setelah tumpukan deskriptor yang mendasar telah dihapus.

Aplikasi dapat mengetahui ukuran kenaikan deskriptor untuk jenis tumpukan deskriptor tertentu, sehingga dapat menghasilkan handel ke lokasi mana pun dalam tumpukan deskriptor secara manual mulai dari handel ke pangkalan. Aplikasi tidak boleh pernah deskriptor hardcode menangani ukuran kenaikan, dan harus selalu mengkuerinya untuk instans perangkat tertentu; jika tidak, perilaku tidak ditentukan. Aplikasi juga tidak boleh menggunakan ukuran dan handel kenaikan untuk melakukan pemeriksaan atau manipulasi data tumpukan deskriptor sendiri, karena hasil dari melakukannya tidak terdefinisi. Handel mungkin tidak benar-benar digunakan sebagai pointer, melainkan sebagai proksi untuk pointer sehingga menghindari dereferensi yang tidak disengaja.

Catatan

Ada struktur pembantu, CD3DX12_GPU_DESCRIPTOR_HANDLE, yang ditentukan dalam header d3dx12.h, yang mewarisi struktur D3D12_GPU_DESCRIPTOR_HANDLE dan menyediakan inisialisasi dan operasi berguna lainnya. Demikian pula struktur pembantu CD3DX12_CPU_DESCRIPTOR_HANDLE didefinisikan untuk struktur D3D12_CPU_DESCRIPTOR_HANDLE .

 

Kedua struktur pembantu ini digunakan saat mengisi daftar perintah.

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

Metode Timbunan Deskriptor

Tumpukan deskriptor (ID3D12DescriptorHeap) mewarisi dari ID3D12Pageable. Ini memberlakukan tanggung jawab untuk manajemen residensi tumpukan deskriptor pada aplikasi, sama seperti tumpukan sumber daya. Metode manajemen residensi hanya berlaku untuk tumpukan yang terlihat shader karena tumpukan yang terlihat non shader tidak terlihat oleh GPU secara langsung.

Metode ID3D12Device::GetDescriptorHandleIncrementSize memungkinkan aplikasi untuk secara manual mengimbangi handel ke dalam tumpukan (menghasilkan handel ke mana saja dalam tumpukan deskriptor). Handel lokasi awal heap berasal dari ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart/ID3D12DescriptorHeap::GetGPUDescriptorHandleForHeapStart. Offsetting dilakukan dengan menambahkan ukuran kenaikan * jumlah deskriptor yang akan diimbangi ke awal tumpukan deskriptor . Perhatikan bahwa ukuran kenaikan tidak dapat dianggap sebagai ukuran byte karena aplikasi tidak boleh menangani dereferensi seolah-olah mereka adalah memori - memori yang ditunjukkan untuk memiliki tata letak yang tidak standar dan dapat bervariasi bahkan untuk perangkat tertentu.

GetCPUDescriptorHandleForHeapStart mengembalikan handel CPU untuk tumpukan deskriptor yang terlihat CPU. Ini mengembalikan handel NULL (dan lapisan debug akan melaporkan kesalahan) jika tumpukan deskriptor tidak terlihat CPU.

GetGPUDescriptorHandleForHeapStart mengembalikan handel GPU untuk tumpukan deskriptor yang terlihat shader. Ini mengembalikan handel NULL (dan lapisan debug akan melaporkan kesalahan) jika tumpukan deskriptor tidak terlihat shader.

Misalnya, membuat tampilan target render untuk menampilkan teks D2D menggunakan perangkat 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])));
        }
    
    }

Pembungkus tumpuk deskriptor minimal

Pengembang aplikasi kemungkinan ingin membangun kode pembantu mereka sendiri untuk mengelola handel dan timbunan deskriptor. Contoh dasar ditunjukkan di bawah ini. Pembungkus yang lebih canggih dapat, misalnya, melacak jenis deskriptor apa yang berada di dalam tumpukan dan menyimpan argumen pembuatan deskriptor.

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

Tumpuk Deskriptor