Condividi tramite


Creazione di un componente Direct3D 12 di base

Nota

Vedi il repository DirectX-Graphics-Samples per esempi di grafica DirectX 12 che illustrano come creare applicazioni a elevato utilizzo di grafica per Windows 10.

Questo argomento descrive il flusso di chiamata per creare un componente Direct3D 12 di base.

Flusso di codice per una semplice app

Il ciclo più esterno di un programma D3D 12 segue un processo grafico molto standard:

Suggerimento

Le funzionalità nuove di Direct3D 12 sono seguite da una nota.

Inizializzare

L'inizializzazione prevede innanzitutto la configurazione delle variabili e delle classi globali e una funzione di inizializzazione deve preparare la pipeline e gli asset.

  • Inizializzare la pipeline.
    • Abilitare il livello di debug.

    • Creare il dispositivo.

    • Crea la coda dei comandi.

    • Creare la catena di scambio.

    • Creare un heap di descrittori per destinazione di rendering (RTV).

      Nota

      Un heap di descrittori può essere visto come una matrice di descrittori . Dove ogni descrittore descrive completamente un oggetto per la GPU.

    • Creare risorse per il frame (una vista di destinazione render per ogni fotogramma).

    • Creare un allocatore di comandi.

      Nota

      Un allocatore di comandi gestisce la memoria sottostante per elenchi di comandi e pacchetti.  

  • Inizializzare le risorse.
    • Creare una firma radice vuota.

      Nota

      Una grafica firma radice definisce le risorse associate alla pipeline grafica.

    • Compilare gli shader.

    • Creare il layout di input dei vertici.

    • Creare un oggetto stato pipeline descrizione, quindi creare l'oggetto .

      Nota

      Un oggetto di stato della pipeline mantiene lo stato di tutti gli shader attualmente impostati, nonché alcuni oggetti di stato di funzione fissa, come l'assembler di input , il tesselator , il rasterizer e la fusione di output .

    • Creare l'elenco dei comandi.

    • Chiudere l'elenco dei comandi.

    • Creare e caricare i vertex buffer.

    • Creare le viste del buffer dei vertici.

    • Creare una recinzione.

      Nota

      Viene utilizzata una barriera per sincronizzare la CPU con la GPU (vedere sincronizzazione multi-motore).

    • Creare un gestore di eventi.

    • Attendere il completamento della GPU.

      Nota

      Controlla la recinzione!

Vedere classe D3D12HelloTriangle, OnInit, LoadPipeline e LoadAssets.

Aggiornare

Aggiornare tutto ciò che deve cambiare dopo l'ultimo fotogramma.

  • Modificare la costante, il vertice, i buffer di indice e tutto il resto, in base alle esigenze.

Fare riferimento a OnUpdate.

Fornire

Disegnare il nuovo mondo.

  • Popolare l'elenco dei comandi.
    • Reimpostare l'allocatore dell'elenco di comandi.

      Nota

      Riutilizzare la memoria associata all'allocatore di comandi.

       

    • Reimpostare l'elenco dei comandi.

    • Impostare la firma radice della grafica.

      Nota

      Imposta la firma principale grafica da utilizzare nella lista dei comandi attuale.

       

    • Impostare il riquadro di visualizzazione e i rettangoli scissor.

    • Impostare una barriera di risorse, che indica che il back buffer deve essere usato come destinazione di rendering.

      Nota

      Le barriere delle risorse vengono usate per gestire le transizioni di risorse.

       

    • Registrare i comandi nell'elenco dei comandi.

    • Indicare che il buffer di retro verrà utilizzato per la presentazione dopo che l'elenco di comandi è stato eseguito.

      Nota

      Un'altra chiamata per impostare una barriera di risorse.

       

    • Chiudere l'elenco dei comandi per ulteriori registrazioni.

  • Eseguire l'elenco dei comandi.
  • Presentare la cornice.
  • Attendere il completamento della GPU.

    Nota

    Continuare ad aggiornare e controllare la recinzione.

Fare riferimento a OnRender.

Distruggere

Chiudere correttamente l'app.

  • Attendere il completamento della GPU.

    Nota

    Controllo finale sulla recinzione.

  • Chiudere l'handle dell'evento.

Fare riferimento a OnDestroy.

Esempio di codice per una semplice app

Di seguito viene espanso il flusso di codice precedente per includere il codice necessario per un esempio di base.

classe D3D12HelloTriangle

Definisci prima la classe in un file di intestazione, inclusi un viewport, un rettangolo di ritaglio e un buffer dei vertici usando le strutture:

#include "DXSample.h"

using namespace DirectX;
using namespace Microsoft::WRL;

class D3D12HelloTriangle : public DXSample
{
public:
    D3D12HelloTriangle(UINT width, UINT height, std::wstring name);

    virtual void OnInit();
    virtual void OnUpdate();
    virtual void OnRender();
    virtual void OnDestroy();

private:
    static const UINT FrameCount = 2;

    struct Vertex
    {
        XMFLOAT3 position;
        XMFLOAT4 color;
    };

    // Pipeline objects.
    D3D12_VIEWPORT m_viewport;
    D3D12_RECT m_scissorRect;
    ComPtr<IDXGISwapChain3> m_swapChain;
    ComPtr<ID3D12Device> m_device;
    ComPtr<ID3D12Resource> m_renderTargets[FrameCount];
    ComPtr<ID3D12CommandAllocator> m_commandAllocator;
    ComPtr<ID3D12CommandQueue> m_commandQueue;
    ComPtr<ID3D12RootSignature> m_rootSignature;
    ComPtr<ID3D12DescriptorHeap> m_rtvHeap;
    ComPtr<ID3D12PipelineState> m_pipelineState;
    ComPtr<ID3D12GraphicsCommandList> m_commandList;
    UINT m_rtvDescriptorSize;

    // App resources.
    ComPtr<ID3D12Resource> m_vertexBuffer;
    D3D12_VERTEX_BUFFER_VIEW m_vertexBufferView;

    // Synchronization objects.
    UINT m_frameIndex;
    HANDLE m_fenceEvent;
    ComPtr<ID3D12Fence> m_fence;
    UINT64 m_fenceValue;

    void LoadPipeline();
    void LoadAssets();
    void PopulateCommandList();
    void WaitForPreviousFrame();
};

OnInit()

Nel file di origine principale dei progetti avviare l'inizializzazione degli oggetti:

void D3D12HelloTriangle::OnInit()
{
    LoadPipeline();
    LoadAssets();
}

LoadPipeline()

Il codice seguente crea le nozioni di base per una pipeline grafica. Il processo di creazione di dispositivi e catene di scambio è molto simile a Direct3D 11.

  • Abilitare il livello di debug, con chiamate a:

D3D12GetDebugInterface
ID3D12Debug::EnableDebugLayer

  • Creare il dispositivo:

CreateDXGIFactory1
D3D12CreateDevice

  • Compilare una descrizione della coda dei comandi, quindi creare la coda dei comandi:

D3D12_COMMAND_QUEUE_DESC
ID3D12Device::CreateCommandQueue

  • Compilare una descrizione della porta di scambio, quindi creare la catena di scambio:

DXGI_SWAP_CHAIN_DESC
IDXGIFactory::CreateSwapChain
IDXGISwapChain3::GetCurrentBackBufferIndex

  • Compilare una descrizione dell'heap. creare quindi un heap del descrittore:

D3D12_DESCRIPTOR_HEAP_DESC
ID3D12Device::CreateDescriptorHeap
ID3D12Device::GetDescriptorHandleIncrementSize

  • Crea la vista di destinazione del rendering

CD3DX12_CPU_DESCRIPTOR_HANDLE
GetCPUDescriptorHandleForHeapStart
IDXGISwapChain::GetBuffer
ID3D12Device::CreateRenderTargetView

Nei passaggi successivi, gli elenchi di comandi vengono ottenuti dall'allocatore di comandi e inviati nella coda dei comandi.

Caricare le dipendenze della pipeline di rendering (si noti che la creazione di un dispositivo WARP software è completamente facoltativa).

void D3D12HelloTriangle::LoadPipeline()
{
#if defined(_DEBUG)
    // Enable the D3D12 debug layer.
    {
        
        ComPtr<ID3D12Debug> debugController;
        if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
        {
            debugController->EnableDebugLayer();
        }
    }
#endif

    ComPtr<IDXGIFactory4> factory;
    ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&factory)));

    if (m_useWarpDevice)
    {
        ComPtr<IDXGIAdapter> warpAdapter;
        ThrowIfFailed(factory->EnumWarpAdapter(IID_PPV_ARGS(&warpAdapter)));

        ThrowIfFailed(D3D12CreateDevice(
            warpAdapter.Get(),
            D3D_FEATURE_LEVEL_11_0,
            IID_PPV_ARGS(&m_device)
            ));
    }
    else
    {
        ComPtr<IDXGIAdapter1> hardwareAdapter;
        GetHardwareAdapter(factory.Get(), &hardwareAdapter);

        ThrowIfFailed(D3D12CreateDevice(
            hardwareAdapter.Get(),
            D3D_FEATURE_LEVEL_11_0,
            IID_PPV_ARGS(&m_device)
            ));
    }

    // Describe and create the command queue.
    D3D12_COMMAND_QUEUE_DESC queueDesc = {};
    queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
    queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;

    ThrowIfFailed(m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue)));

    // Describe and create the swap chain.
    DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
    swapChainDesc.BufferCount = FrameCount;
    swapChainDesc.BufferDesc.Width = m_width;
    swapChainDesc.BufferDesc.Height = m_height;
    swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
    swapChainDesc.OutputWindow = Win32Application::GetHwnd();
    swapChainDesc.SampleDesc.Count = 1;
    swapChainDesc.Windowed = TRUE;

    ComPtr<IDXGISwapChain> swapChain;
    ThrowIfFailed(factory->CreateSwapChain(
        m_commandQueue.Get(),        // Swap chain needs the queue so that it can force a flush on it.
        &swapChainDesc,
        &swapChain
        ));

    ThrowIfFailed(swapChain.As(&m_swapChain));

    // This sample does not support fullscreen transitions.
    ThrowIfFailed(factory->MakeWindowAssociation(Win32Application::GetHwnd(), DXGI_MWA_NO_ALT_ENTER));

    m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();

    // 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_device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&m_rtvHeap)));

        m_rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
    }

    // Create frame resources.
    {
        CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart());

        // Create a RTV for each frame.
        for (UINT n = 0; n < FrameCount; n++)
        {
            ThrowIfFailed(m_swapChain->GetBuffer(n, IID_PPV_ARGS(&m_renderTargets[n])));
            m_device->CreateRenderTargetView(m_renderTargets[n].Get(), nullptr, rtvHandle);
            rtvHandle.Offset(1, m_rtvDescriptorSize);
        }
    }

    ThrowIfFailed(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocator)));
}

LoadAssets()

Il caricamento e la preparazione degli asset è un processo lungo. Molte di queste fasi sono simili a D3D 11, anche se alcune sono nuove per D3D 12.

In Direct3D 12, lo stato richiesto della pipeline viene collegato a un elenco di comandi tramite un oggetto stato della pipeline (PSO). Questo esempio illustra come creare un PSO. È possibile archiviare l'oggetto PSO come variabile membro e riutilizzarlo quante volte necessario.

Un heap di descrittori definisce le viste e come accedere alle risorse, ad esempio una vista di destinazione di rendering.

Con l'allocatore dell'elenco dei comandi e PSO, è possibile creare l'elenco dei comandi effettivo, che verrà eseguito in un secondo momento.

Le API e i processi seguenti vengono chiamati in successione.

  • Creare una root signature vuota utilizzando la struttura helper disponibile.

CD3DX12_ROOT_SIGNATURE_DESC
D3D12SerializeRootSignature
ID3D12Device::CreateRootSignature

  • Caricare e compilare gli shader: D3DCompileFromFile.
  • Creare il layout di input dei vertici: D3D12_INPUT_ELEMENT_DESC.
  • Compilare una descrizione dello stato della pipeline usando le strutture helper disponibili, quindi creare lo stato della pipeline grafica:

D3D12_GRAPHICS_PIPELINE_STATE_DESC
CD3DX12_RASTERIZER_DESC
CD3DX12_BLEND_DESC
ID3D12Device::CreateGraphicsPipelineState

  • Creare, quindi chiudere, un elenco di comandi:

ID3D12Device::CreateCommandList
ID3D12GraphicsCommandList::Close

ID3D12Resource::Map
ID3D12Resource::Unmap

  • Inizializzare la vista vertex buffer: GetGPUVirtualAddress.
  • Creare e inizializzare il recinto: ID3D12Device::CreateFence.
  • Creare un handle di evento da usare con la sincronizzazione dei fotogrammi.
  • Attendere il completamento della GPU.
void D3D12HelloTriangle::LoadAssets()
{
    // Create an empty root signature.
    {
        CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
        rootSignatureDesc.Init(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

        ComPtr<ID3DBlob> signature;
        ComPtr<ID3DBlob> error;
        ThrowIfFailed(D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));
        ThrowIfFailed(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature)));
    }

    // Create the pipeline state, which includes compiling and loading shaders.
    {
        ComPtr<ID3DBlob> vertexShader;
        ComPtr<ID3DBlob> pixelShader;

#if defined(_DEBUG)
        // Enable better shader debugging with the graphics debugging tools.
        UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
        UINT compileFlags = 0;
#endif

        ThrowIfFailed(D3DCompileFromFile(GetAssetFullPath(L"shaders.hlsl").c_str(), nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, &vertexShader, nullptr));
        ThrowIfFailed(D3DCompileFromFile(GetAssetFullPath(L"shaders.hlsl").c_str(), nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &pixelShader, nullptr));

        // Define the vertex input layout.
        D3D12_INPUT_ELEMENT_DESC inputElementDescs[] =
        {
            { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
            { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
        };

        // Describe and create the graphics pipeline state object (PSO).
        D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
        psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) };
        psoDesc.pRootSignature = m_rootSignature.Get();
        psoDesc.VS = { reinterpret_cast<UINT8*>(vertexShader->GetBufferPointer()), vertexShader->GetBufferSize() };
        psoDesc.PS = { reinterpret_cast<UINT8*>(pixelShader->GetBufferPointer()), pixelShader->GetBufferSize() };
        psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
        psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
        psoDesc.DepthStencilState.DepthEnable = FALSE;
        psoDesc.DepthStencilState.StencilEnable = FALSE;
        psoDesc.SampleMask = UINT_MAX;
        psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
        psoDesc.NumRenderTargets = 1;
        psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
        psoDesc.SampleDesc.Count = 1;
        ThrowIfFailed(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pipelineState)));
    }

    // Create the command list.
    ThrowIfFailed(m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocator.Get(), m_pipelineState.Get(), IID_PPV_ARGS(&m_commandList)));

    // Command lists are created in the recording state, but there is nothing
    // to record yet. The main loop expects it to be closed, so close it now.
    ThrowIfFailed(m_commandList->Close());

    // Create the vertex buffer.
    {
        // Define the geometry for a triangle.
        Vertex triangleVertices[] =
        {
            { { 0.0f, 0.25f * m_aspectRatio, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f } },
            { { 0.25f, -0.25f * m_aspectRatio, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } },
            { { -0.25f, -0.25f * m_aspectRatio, 0.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } }
        };

        const UINT vertexBufferSize = sizeof(triangleVertices);

        // Note: using upload heaps to transfer static data like vert buffers is not 
        // recommended. Every time the GPU needs it, the upload heap will be marshalled 
        // over. Please read up on Default Heap usage. An upload heap is used here for 
        // code simplicity and because there are very few verts to actually transfer.
        CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_UPLOAD);
        auto desc = CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize);
        ThrowIfFailed(m_device->CreateCommittedResource(
            &heapProps,
            D3D12_HEAP_FLAG_NONE,
            &desc,
            D3D12_RESOURCE_STATE_GENERIC_READ,
            nullptr,
            IID_PPV_ARGS(&m_vertexBuffer)));

        // Copy the triangle data to the vertex buffer.
        UINT8* pVertexDataBegin;
        CD3DX12_RANGE readRange(0, 0);        // We do not intend to read from this resource on the CPU.
        ThrowIfFailed(m_vertexBuffer->Map(0, &readRange, reinterpret_cast<void**>(&pVertexDataBegin)));
        memcpy(pVertexDataBegin, triangleVertices, sizeof(triangleVertices));
        m_vertexBuffer->Unmap(0, nullptr);

        // Initialize the vertex buffer view.
        m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress();
        m_vertexBufferView.StrideInBytes = sizeof(Vertex);
        m_vertexBufferView.SizeInBytes = vertexBufferSize;
    }

    // Create synchronization objects and wait until assets have been uploaded to the GPU.
    {
        ThrowIfFailed(m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence)));
        m_fenceValue = 1;

        // Create an event handle to use for frame synchronization.
        m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
        if (m_fenceEvent == nullptr)
        {
            ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError()));
        }

        // Wait for the command list to execute; we are reusing the same command 
        // list in our main loop but for now, we just want to wait for setup to 
        // complete before continuing.
        WaitForPreviousFrame();
    }
}

OnUpdate()

Per un semplice esempio, non viene aggiornato nulla.

void D3D12HelloTriangle::OnUpdate()

{
}

OnRender()

Durante la configurazione, la variabile membro m_commandList è stata usata per registrare ed eseguire tutti i comandi di configurazione. È ora possibile riutilizzare il membro nel ciclo di rendering principale.

Il rendering implica una chiamata per popolare l'elenco dei comandi, quindi è possibile eseguire l'elenco dei comandi e presentare il buffer successivo nella catena di swap.

void D3D12HelloTriangle::OnRender()
{
    // Record all the commands we need to render the scene into the command list.
    PopulateCommandList();

    // Execute the command list.
    ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() };
    m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);

    // Present the frame.
    ThrowIfFailed(m_swapChain->Present(1, 0));

    WaitForPreviousFrame();
}

PopulateCommandList()

È necessario reimpostare l'allocatore dell'elenco di comandi e l'elenco dei comandi stesso prima di poterli riutilizzare. Negli scenari più avanzati potrebbe essere opportuno reimpostare l'allocatore ogni pochi fotogrammi. La memoria è associata all'allocatore che non può essere rilasciata immediatamente dopo l'esecuzione di un elenco di comandi. Questo esempio mostra come reimpostare l'allocatore dopo ogni fotogramma.

Riutilizzare ora l'elenco dei comandi per il frame corrente. Ricollegare il riquadro di visualizzazione all'elenco dei comandi (che deve essere eseguito ogni volta che viene reimpostato un elenco di comandi e prima dell'esecuzione dell'elenco di comandi), indicare che la risorsa verrà usata come destinazione di rendering, registrare i comandi e quindi indicare che la destinazione di rendering verrà utilizzata per la presentazione quando l'elenco di comandi ha terminato l'esecuzione.

Popolamento di elenchi di comandi chiama a sua volta i metodi e i processi seguenti:

  • Reimpostare l'allocatore dei comandi e l'elenco dei comandi:

ID3D12CommandAllocator::Reset
ID3D12GraphicsCommandList::Reset

  • Impostare la firma radice, il riquadro di visualizzazione e i rettangoli delle forbici:

ID3D12GraphicsCommandList::SetGraphicsRootSignature
ID3D12GraphicsCommandList::RSSetViewports
ID3D12GraphicsCommandList::RSSetScissorRects

  • Indicare che il buffer posteriore deve essere usato come destinazione di rendering:

ID3D12GraphicsCommandList::ResourceBarrier
ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart
ID3D12GraphicsCommandList::OMSetRenderTargets

  • Registrare i comandi:

ID3D12GraphicsCommandList::ClearRenderTargetView
ID3D12GraphicsCommandList::IASetPrimitiveTopology
ID3D12GraphicsCommandList::IASetVertexBuffers
ID3D12GraphicsCommandList::DrawInstanced

void D3D12HelloTriangle::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_commandAllocator->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_commandAllocator.Get(), m_pipelineState.Get()));

    // Set necessary state.
    m_commandList->SetGraphicsRootSignature(m_rootSignature.Get());
    m_commandList->RSSetViewports(1, &m_viewport);
    m_commandList->RSSetScissorRects(1, &m_scissorRect);

    // Indicate that the back buffer will be used as a render target.
    auto barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
    m_commandList->ResourceBarrier(1, &barrier);

    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.2f, 0.4f, 1.0f };
    m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
    m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    m_commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView);
    m_commandList->DrawInstanced(3, 1, 0, 0);

    // Indicate that the back buffer will now be used to present.
    barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
    m_commandList->ResourceBarrier(1, &barrier);

    ThrowIfFailed(m_commandList->Close());
}

WaitForPreviousFrame()

Il codice seguente illustra un uso eccessivamente semplificato delle recinzioni.

Nota

L'attesa del completamento di un frame è troppo inefficiente per la maggior parte delle app.

 

Le API e i processi seguenti vengono chiamati in ordine:

void D3D12HelloTriangle::WaitForPreviousFrame()
{
    // WAITING FOR THE FRAME TO COMPLETE BEFORE CONTINUING IS NOT BEST PRACTICE.
    // This is code implemented as such for simplicity. More advanced samples 
    // illustrate how to use fences for efficient resource usage.

    // Signal and increment the fence value.
    const UINT64 fence = m_fenceValue;
    ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), fence));
    m_fenceValue++;

    // Wait until the previous frame is finished.
    if (m_fence->GetCompletedValue() < fence)
    {
        ThrowIfFailed(m_fence->SetEventOnCompletion(fence, m_fenceEvent));
        WaitForSingleObject(m_fenceEvent, INFINITE);
    }

    m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
}

OnDestroy()

Chiudere l'app correttamente.

  • Attendere che la GPU finisca.
  • Chiudere l'evento.
void D3D12HelloTriangle::OnDestroy()
{

    // Wait for the GPU to be done with all resources.
    WaitForPreviousFrame();

    CloseHandle(m_fenceEvent);
}

Comprendere Direct3D 12

Gli esempi di lavoro