Erstellen einer einfachen Direct3D 12-Komponente

Hinweis

Im DirectX-Graphics-Samples-Repository finden Sie DirectX 12-Grafikbeispiele, die veranschaulichen, wie grafikintensive Anwendungen für Windows 10.

In diesem Thema wird der Aufruffluss zum Erstellen einer einfachen Direct3D 12-Komponente beschrieben.

Codefluss für eine einfache App

Die äußerste Schleife eines D3D 12-Programms folgt einem sehr standardmäßigen Grafikprozess:

Tipp

Auf features new to Direct3D 12 (Neue Features von Direct3D 12) folgt ein Hinweis.

Initialisieren

Die Initialisierung umfasst zunächst das Einrichten der globalen Variablen und Klassen, und eine Initialisierungsfunktion muss die Pipeline und die Objekte vorbereiten.

  • Initialisieren Sie die Pipeline.

    • Aktivieren Sie die Debugebene.

    • Erstellen Sie das Gerät.

    • Erstellen Sie die Befehlswarteschlange.

    • Erstellen Sie die Auslagerungskette.

    • Erstellen Eines RTV-Deskriptordeskriptor-Heaps (Renderzielansicht).

      Hinweis

      Ein Deskriptorhap kann als ein Array von Deskriptoren verwendet werden. Wobei jeder Deskriptor ein Objekt für die GPU vollständig beschreibt.

    • Erstellen sie Frameressourcen (eine Renderzielansicht für jeden Frame).

    • Erstellen Sie eine Befehlszuweisung.

      Hinweis

      Eine Befehlszuweisung verwaltet den zugrunde liegenden Speicher für Befehlslisten und Bündel.

  • Initialisieren Sie die Objekte.

    • Erstellen Sie eine leere Stammsignatur.

      Hinweis

      Eine Grafikstammsignatur definiert, welche Ressourcen an die Grafikpipeline gebunden sind.

    • Kompilieren Sie die Shader.

    • Erstellen Sie das Vertexeingabelayout.

    • Erstellen Sie eine Beschreibung des Pipelinezustandsobjekts, und erstellen Sie dann das -Objekt.

      Hinweis

      Ein Pipelinezustandsobjekt verwaltet den Zustand aller derzeit festgelegten Shader sowie bestimmter fester Funktionszustandsobjekte (z. B. des Eingabe-Assemblers, des Tesselators, des Rasterizers und der Ausgabefusion).

    • Erstellen Sie die Befehlsliste.

    • Schließen Sie die Befehlsliste.

    • Erstellen und laden Sie die Scheitelpunktpuffer.

    • Erstellen Sie die Scheitelpunktpufferansichten.

    • Erstellen Sie einen Fence.

      Hinweis

      Ein Fence wird verwendet, um die CPU mit der GPU zu synchronisieren (siehe Multi-Engine-Synchronisierung).

    • Erstellen Sie ein Ereignishand handle.

    • Warten Sie, bis die GPU abgeschlossen ist.

      Hinweis

      Check on the fence!

Weitere Informationen finden Sie in der Klasse D3D12HelloPipengle, OnInit, LoadPipeline und LoadAssets.

Aktualisieren

Aktualisieren Sie alles, was sich seit dem letzten Frame ändern sollte.

  • Ändern Sie die Konstante, den Scheitelpunkt, die Indexpuffer und alles andere nach Bedarf.

Weitere Informationen finden Sie unter OnUpdate.

Rendern

Zeichnen Sie die neue Welt.

  • Füllen Sie die Befehlsliste auf.
    • Setzen Sie die Zuweisung der Befehlsliste zurück.

      Hinweis

      Verwenden Sie den Arbeitsspeicher, der der Befehlszuweisung zugeordnet ist, erneut.

    • Setzen Sie die Befehlsliste zurück.

    • Legen Sie die Grafikstammsignatur fest.

      Hinweis

      Legt die Grafikstammsignatur fest, die für die aktuelle Befehlsliste verwendet werden soll.

    • Legen Sie die Viewport- und Scissor-Rechtecke fest.

    • Legen Sie eine Ressourcenbarriere fest, die angibt, dass der Hintergrundpuffer als Renderziel verwendet werden soll.

      Hinweis

      Ressourcenbarrieren werden verwendet, um Ressourcenübergänge zu verwalten.

    • Zeichnen Sie Befehle in der Befehlsliste auf.

    • Geben Sie an, dass der Hintergrundpuffer verwendet wird, um nach der Ausführung der Befehlsliste anzuzeigen.

      Hinweis

      Ein weiterer Aufruf zum Festlegen einer Ressourcenbarriere.

    • Schließen Sie die Befehlsliste, um weitere Aufzeichnungen zu erstellen.

  • Führen Sie die Befehlsliste aus.
  • Stellen Sie den Frame vor.
  • Warten Sie, bis die GPU abgeschlossen ist.

    Hinweis

    Aktualisieren Und überprüfen Sie den Fence.

Weitere Informationen finden Sie unter OnRender.

Zerstören

Schließen Sie die App sauber.

  • Warten Sie, bis die GPU abgeschlossen ist.

    Hinweis

    Abschließende Überprüfung des Fences.

  • Schließen Sie das Ereignishand handle.

Weitere Informationen finden Sie unter OnDestroy.

Codebeispiel für eine einfache App

Im Folgenden wird der oben beschriebene Codefluss erweitert, um den Code ein include, der für ein einfaches Beispiel erforderlich ist.

D3D12Hello Wiengle-Klasse

Definieren Sie zunächst die -Klasse in einer Headerdatei, einschließlich eines Viewports, eines Scissor-Rechtecks und eines Scheitelpunktpuffers mithilfe der -Strukturen:

#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()

Beginnen Sie in der Hauptquellendatei der Projekte mit der Initialisierung der Objekte:

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

LoadPipeline()

Der folgende Code erstellt die Grundlagen für eine Grafikpipeline. Der Prozess zum Erstellen von Geräten und Austauschketten ähnelt stark Direct3D 11.

  • Aktivieren Sie die Debugebene mit Aufrufen von:

D3D12GetDebugInterface
ID3D12Debug::EnableDebugLayer

  • Erstellen Sie das Gerät:

CreateDXGIFactory1
D3D12CreateDevice

  • Geben Sie eine Beschreibung der Befehlswarteschlange ein, und erstellen Sie dann die Befehlswarteschlange:

_D3D12-BEFEHLSWARTESCHLANGE _ _ DESC
ID3D12Device::CreateCommandQueue

  • Geben Sie eine Beschreibung für die Swapkette ein, und erstellen Sie dann die Swapkette:

DXGI _ SWAP _ CHAIN _ DESC
IDXGIFactory::CreateSwapChain
IDXGISwapChain3::GetCurrentBackBufferIndex

  • Füllen Sie eine Heapbeschreibung aus. erstellen Sie dann einen Deskriptorheap:

D3D12 _ DESCRIPTOR _ HEAP _ DESC
ID3D12Device::CreateDescriptorHeap
ID3D12Device::GetDescriptorHandleIncrementSize

  • Erstellen Sie die Renderzielansicht:

CD3DX12 _ CPU _ DESCRIPTOR _ HANDLE
GetCPUDescriptorHandleForHeapStart
IDXGISwapChain::GetBuffer
ID3D12Device::CreateRenderTargetView

In späteren Schritten werden Befehlslisten von der Befehlszuweisung abgerufen und an die Befehlswarteschlange übermittelt.

Laden Sie die Abhängigkeiten der Renderingpipeline (beachten Sie, dass die Erstellung eines WARP-Softwaregeräts vollständig optional ist).

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()

Das Laden und Vorbereiten von Ressourcen ist ein langwieriger Prozess. Viele dieser Phasen ähneln D3D 11, obwohl einige noch nicht mit D3D 12 vergleichbar sind.

In Direct3D 12 wird der erforderliche Pipelinezustand über ein Pipelinezustandsobjekt (Pipeline State Object, PSO) an eine Befehlsliste angefügt. In diesem Beispiel wird gezeigt, wie Sie einen PSO erstellen. Sie können den PSO als Membervariable speichern und so oft wie nötig wiederverwenden.

Ein Deskriptorheap definiert die Ansichten und den Zugriff auf Ressourcen (z. B. eine Renderzielansicht).

Mit der Befehlslistenzuweisung und pso können Sie die tatsächliche Befehlsliste erstellen, die zu einem späteren Zeitpunkt ausgeführt wird.

Die folgenden APIs und Prozesse werden nacheinander aufgerufen.

  • Erstellen Sie mithilfe der verfügbaren Hilfsstruktur eine leere Stammsignatur:

CD3DX12 _ ROOT _ SIGNATURE _ DESC
D3D12SerializeRootSignature
ID3D12Device::CreateRootSignature

  • Laden und Kompilieren der Shader: D3DCompileFromFile.
  • Erstellen Sie das Vertexeingabelayout: D3D12 _ INPUT _ ELEMENT _ DESC.
  • Geben Sie unter Verwendung der verfügbaren Hilfsstrukturen eine Beschreibung des Pipelinezustands ein, und erstellen Sie dann den Grafikpipelinezustand:

D3D12 _ GRAPHICS _ PIPELINE _ STATE _ DESC
CD3DX12 _ RASTERIZER _ DESC
CD3DX12 _ BLEND _ DESC
ID3D12Device::CreateGraphicsPipelineState

  • Erstellen und schließen Sie dann eine Befehlsliste:

ID3D12Device::CreateCommandList
ID3D12GraphicsCommandList::Close

ID3D12Resource::Map
ID3D12Resource::Unmap

  • Initialisieren sie die Vertexpufferansicht: GetGPUVirtualAddress.
  • Erstellen und initialisieren Sie den Fence: ID3D12Device::CreateFence.
  • Erstellen Sie ein Ereignishandle für die Verwendung mit der Framesynchronisierung.
  • Warten Sie, bis die GPU abgeschlossen ist.
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()

In einem einfachen Beispiel wird nichts aktualisiert.

void D3D12HelloTriangle::OnUpdate()

{
}

OnRender()

Während der Einrichtung wurde die Membervariable m _ commandList verwendet, um alle Einrichtungsbefehle aufzuzeichnen und auszuführen. Sie können diesen Member jetzt in der Hauptrenderingschleife wiederverwenden.

Das Rendering umfasst einen Aufruf zum Auffüllen der Befehlsliste. Anschließend kann die Befehlsliste ausgeführt und der nächste Puffer in der Swapkette angezeigt werden:

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()

Sie müssen die Befehlslistenzuweisung und die Befehlsliste selbst zurücksetzen, bevor Sie sie wiederverwenden können. In komplexeren Szenarien kann es sinnvoll sein, die Zuweisung auf mehrere Frames zurückzusetzen. Arbeitsspeicher ist der Zuweisung zugeordnet, die nicht sofort nach dem Ausführen einer Befehlsliste freigegeben werden kann. In diesem Beispiel wird gezeigt, wie die Zuweisung nach jedem Frame zurückgesetzt wird.

Verwenden Sie nun die Befehlsliste für den aktuellen Frame wieder. Setzen Sie den Viewport erneut an die Befehlsliste an (dies muss erfolgen, wenn eine Befehlsliste zurückgesetzt wird, und bevor die Befehlsliste ausgeführt wird). Geben Sie an, dass die Ressource als Renderziel verwendet wird, zeichnen Sie Befehle auf, und geben Sie dann an, dass das Renderziel verwendet wird, um zu präsentieren, wenn die Befehlsliste ausgeführt wird.

Beim Auffüllen von Befehlslisten werden die folgenden Methoden und Prozesse nacheinander aufruft:

  • Setzen Sie die Befehlszuweisung und die Befehlsliste zurück:

ID3D12CommandAllocator::Reset
ID3D12GraphicsCommandList::Reset

  • Legen Sie die Stammsignatur, den Viewport und die Scissors-Rechtecke fest:

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

  • Geben Sie an, dass der Hintergrundpuffer als Renderziel verwendet werden soll:

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

  • Notiert die Befehle:

ID3D12GraphicsCommandList::ClearRenderTargetView
ID3D12GraphicsCommandList::IASetPrimitiveTopology
ID3D12GraphicsCommandList::IASetVertexBuffers
ID3D12GraphicsCommandList::D rawInstanced

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()

Der folgende Code zeigt eine zu vereinfachte Verwendung von Zäunen.

Hinweis

Das Warten auf den Abschluss eines Frames ist für die meisten Apps zu ineffizient.

Die folgenden APIs und Prozesse werden in der reihenfolge aufgerufen:

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()

Schließen Sie die App sauber.

  • Warten Sie, bis die GPU abgeschlossen ist.
  • Schließen Sie das Ereignis.
void D3D12HelloTriangle::OnDestroy()
{

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

    CloseHandle(m_fenceEvent);
}

Grundlegendes zu Direct3D 12

Funktionierende Beispiele