Membuat komponen Direct3D 12 dasar

Catatan

Lihat repositori DirectX-Graphics-Samples untuk sampel Grafik DirectX 12 yang menunjukkan cara membangun aplikasi intensif grafis untuk Windows 10.

Topik ini menjelaskan alur panggilan untuk membuat komponen Direct3D 12 dasar.

Alur kode untuk aplikasi sederhana

Perulangan terluar dari program D3D 12 mengikuti proses grafis yang sangat standar:

Tip

Fitur baru Direct3D 12 diikuti dengan catatan.

Initialize

Inisialisasi melibatkan terlebih dahulu menyiapkan variabel dan kelas global, dan fungsi inisialisasi harus menyiapkan alur dan aset.

  • Menginisialisasi alur.
    • Aktifkan lapisan debug.

    • Buat perangkat.

    • Buat antrean perintah.

    • Buat rantai pertukaran.

    • Buat heap deskriptor tampilan target render (RTV).

      Catatan

      Timbunan deskriptor dapat dianggap sebagai array deskriptor. Di mana setiap deskriptor sepenuhnya menjelaskan objek ke GPU.

    • Buat sumber daya bingkai (tampilan target render untuk setiap bingkai).

    • Buat alokator perintah.

      Catatan

      Alokator perintah mengelola penyimpanan yang mendasar untuk daftar perintah dan bundel.  

  • Menginisialisasi aset.
    • Buat tanda tangan akar kosong.

      Catatan

      Tanda tangan akar grafik menentukan sumber daya apa yang terikat ke alur grafis.

    • Kompilasi shader.

    • Buat tata letak input vertex.

    • Buat deskripsi objek status alur , lalu buat objek .

      Catatan

      Objek status alur mempertahankan status semua shader yang saat ini diatur serta objek status fungsi tetap tertentu (seperti perakitan input, tesselator, rasterizer , dan penggabungan output).

    • Buat daftar perintah.

    • Tutup daftar perintah.

    • Buat dan muat buffer vertex.

    • Buat tampilan buffer vertex.

    • Buat pagar.

      Catatan

      Pagar digunakan untuk menyinkronkan CPU dengan GPU (lihat Sinkronisasi multi-mesin).

    • Membuat handel peristiwa.

    • Tunggu hingga GPU selesai.

      Catatan

      Periksa pagarnya!

Lihat kelas D3D12HelloTriangle, OnInit, LoadPipeline , dan LoadAssets.

Pembaruan

Perbarui semua yang harus berubah sejak bingkai terakhir.

  • Ubah konstanta, puncak, buffer indeks, dan yang lainnya, seperlunya.

Lihat OnUpdate.

Render

Menggambar dunia baru.

  • Isi daftar perintah.
    • Reset alokator daftar perintah.

      Catatan

      Gunakan kembali memori yang terkait dengan alokator perintah.

       

    • Reset daftar perintah.

    • Atur tanda tangan akar grafik.

      Catatan

      Menyetel tanda tangan akar grafik yang akan digunakan untuk daftar perintah saat ini.

       

    • Atur viewport dan persegi panjang gunting.

    • Atur penghubung sumber daya, yang menunjukkan buffer belakang akan digunakan sebagai target render.

      Catatan

      Hambatan sumber daya digunakan untuk mengelola transisi sumber daya.

       

    • Rekam perintah ke dalam daftar perintah.

    • Tunjukkan buffer belakang akan digunakan untuk menyajikan setelah daftar perintah dijalankan.

      Catatan

      Panggilan lain untuk mengatur penghubung sumber daya.

       

    • Tutup daftar perintah untuk merekam lebih lanjut.

  • Jalankan daftar perintah.
  • Sajikan bingkai.
  • Tunggu hingga GPU selesai.

    Catatan

    Terus perbarui dan periksa pagarnya.

Lihat OnRender.

Menghancurkan

Tutup aplikasi dengan bersih.

  • Tunggu hingga GPU selesai.

    Catatan

    Pemeriksaan terakhir di pagar.

  • Tutup handel peristiwa.

Lihat OnDestroy.

Contoh kode untuk aplikasi sederhana

Berikut ini memperluas alur kode di atas untuk menyertakan kode yang diperlukan untuk sampel dasar.

kelas D3D12HelloTriangle

Pertama-tama tentukan kelas dalam file header, termasuk viewport, persegi panjang gunting, dan buffer vertex menggunakan struktur:

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

Dalam file sumber utama proyek, mulai inisialisasi objek:

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

LoadPipeline()

Kode berikut membuat dasar-dasar untuk alur grafis. Proses pembuatan perangkat dan rantai pertukaran sangat mirip dengan Direct3D 11.

  • Aktifkan lapisan debug, dengan panggilan ke:

D3D12GetDebugInterface
ID3D12Debug::EnableDebugLayer

  • Buat perangkat:

CreateDXGIFactory1
D3D12CreateDevice

  • Isi deskripsi antrean perintah, lalu buat antrean perintah:

D3D12_COMMAND_QUEUE_DESC
ID3D12Device::CreateCommandQueue

  • Isi deskripsi swapchain, lalu buat rantai pertukaran:

DXGI_SWAP_CHAIN_DESC
IDXGIFactory::CreateSwapChain
IDXGISwapChain3::GetCurrentBackBufferIndex

  • Isi deskripsi timbunan. lalu buat timbunan deskriptor:

D3D12_DESCRIPTOR_HEAP_DESC
ID3D12Device::CreateDescriptorHeap
ID3D12Device::GetDescriptorHandleIncrementSize

  • Buat tampilan target render:

CD3DX12_CPU_DESCRIPTOR_HANDLE
GetCPUDescriptorHandleForHeapStart
IDXGISwapChain::GetBuffer
ID3D12Device::CreateRenderTargetView

Pada langkah selanjutnya, daftar perintah diperoleh dari alokator perintah dan dikirimkan ke antrean perintah.

Muat dependensi alur penyajian (perhatikan bahwa pembuatan perangkat lunak WARP sepenuhnya opsional).

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

Memuat dan menyiapkan aset adalah proses yang panjang. Banyak dari tahapan ini mirip dengan D3D 11, meskipun beberapa baru menggunakan D3D 12.

Di Direct3D 12, status alur yang diperlukan dilampirkan ke daftar perintah melalui objek status alur (PSO). Contoh ini menunjukkan cara membuat PSO. Anda dapat menyimpan PSO sebagai variabel anggota dan menggunakannya kembali sebanyak yang diperlukan.

Timbunan deskriptor menentukan tampilan dan cara mengakses sumber daya (misalnya, tampilan target render).

Dengan alokator daftar perintah dan PSO, Anda dapat membuat daftar perintah aktual, yang akan dijalankan di lain waktu.

API dan proses berikut dipanggil secara berturut-turut.

  • Buat tanda tangan akar kosong, menggunakan struktur pembantu yang tersedia:

CD3DX12_ROOT_SIGNATURE_DESC
D3D12SerializeRootSignature
ID3D12Device::CreateRootSignature

D3D12_GRAPHICS_PIPELINE_STATE_DESC
CD3DX12_RASTERIZER_DESC
CD3DX12_BLEND_DESC
ID3D12Device::CreateGraphicsPipelineState

  • Buat, lalu tutup, daftar perintah:

ID3D12Device::CreateCommandList
ID3D12GraphicsCommandList::Close

ID3D12Resource::Map
ID3D12Resource::Unmap

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

Untuk contoh sederhana, tidak ada yang diperbarui.

void D3D12HelloTriangle::OnUpdate()

{
}

OnRender()

Selama pengaturan, variabel anggota m_commandList digunakan untuk merekam dan menjalankan semua perintah pengaturan. Anda sekarang dapat menggunakan kembali anggota tersebut dalam perulangan render utama.

Penyajian melibatkan panggilan untuk mengisi daftar perintah, maka daftar perintah dapat dijalankan dan buffer berikutnya dalam rantai pertukaran yang disajikan:

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

IsiCommandList()

Anda harus mengatur ulang alokator daftar perintah dan daftar perintah itu sendiri sebelum Anda dapat menggunakannya kembali. Dalam skenario yang lebih canggih, mungkin masuk akal untuk mengatur ulang alokator setiap beberapa bingkai. Memori dikaitkan dengan alokator yang tidak dapat dirilis segera setelah menjalankan daftar perintah. Contoh ini menunjukkan cara mengatur ulang alokator setelah setiap bingkai.

Sekarang, gunakan kembali daftar perintah untuk bingkai saat ini. Memasang kembali viewport ke daftar perintah (yang harus dilakukan setiap kali daftar perintah diatur ulang, dan sebelum daftar perintah dijalankan), tunjukkan bahwa sumber daya akan digunakan sebagai target render, perintah rekaman, lalu tunjukkan bahwa target render akan digunakan untuk menyajikan ketika daftar perintah selesai dijalankan.

Mengisi daftar perintah memanggil metode dan proses berikut secara bergiliran:

  • Reset alokator perintah, dan daftar perintah:

ID3D12CommandAllocator::Reset
ID3D12GraphicsCommandList::Reset

  • Atur persegi panjang tanda tangan akar, viewport, dan gunting:

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

  • Tunjukkan bahwa buffer belakang akan digunakan sebagai target render:

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

  • Rekam perintah:

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

Kode berikut menunjukkan penggunaan pagar yang terlalu disederhanakan.

Catatan

Menunggu bingkai selesai terlalu tidak efisien untuk sebagian besar aplikasi.

 

API dan proses berikut dipanggil secara berurutan:

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

Tutup aplikasi dengan bersih.

  • Tunggu hingga GPU selesai.
  • Tutup acara.
void D3D12HelloTriangle::OnDestroy()
{

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

    CloseHandle(m_fenceEvent);
}

Memahami Direct3D 12

Sampel Yang Berfungsi