기본 Direct3D 12 구성 요소 만들기

참고

Windows 10 그래픽 집약적인 애플리케이션을 빌드하는 방법을 보여 주는 DirectX 12 그래픽 샘플의 DirectX-Graphics-Samples 리포지토리를 참조하세요.

이 항목에서는 기본 Direct3D 12 구성 요소를 만들기 위한 호출 흐름을 설명합니다.

간단한 앱 코드 흐름

D3D 12 프로그램의 가장 바깥쪽 루프는 매우 표준적인 그래픽 프로세스를 따릅니다.

Direct3D 12의 새로운 기능 뒤에는 설명이 나와 있습니다.

Initialize

초기화는 먼저 글로벌 변수와 클래스를 설정해야 하고, 초기화 함수에서 파이프라인과 자산을 준비해야 합니다.

  • 파이프라인을 초기화합니다.
    • 디버그 계층을 사용하도록 설정합니다.

    • 디바이스를 만듭니다.

    • 명령 큐를 만듭니다.

    • 스왑 체인을 만듭니다.

    • RTV(렌더링 대상 보기) 설명자 힙을 만듭니다.

      참고

      설명자 힙설명자의 배열로 간주할 수 있습니다. 각 설명자는 GPU에 대한 개체를 완전히 설명합니다.

    • 프레임 리소스(각 프레임에 대한 렌더링 대상 보기)를 만듭니다.

    • 명령 할당자를 만듭니다.

      참고

      명령 할당자는 명령 목록 및 번들에 대한 기본 스토리지를 관리합니다.  

  • 자산을 초기화합니다.
    • 빈 루트 서명을 만듭니다.

      참고

      그래픽 루트 서명은 그래픽 파이프라인에 바인딩되는 리소스를 정의합니다.

    • 셰이더를 컴파일합니다.

    • 꼭짓점 입력 레이아웃을 만듭니다.

    • 파이프라인 상태 개체 설명을 만든 다음, 개체를 만듭니다.

      참고

      파이프라인 상태 개체는 현재 설정된 모든 셰이더 및 특정 고정 함수 상태 개체(예: 입력 어셈블러, 테설레이터, 래스터라이저출력 병합기)의 상태를 유지합니다.

    • 명령 목록을 만듭니다.

    • 명령 목록을 닫습니다.

    • 꼭짓점 버퍼를 만들고 로드합니다.

    • 꼭짓점 버퍼 보기를 만듭니다.

    • 펜스를 만듭니다.

      참고

      펜스는 CPU를 GPU와 동기화하는 데 사용됩니다( 다중 엔진 동기화 참조).

    • 이벤트 핸들을 만듭니다.

    • GPU가 완료될 때까지 기다립니다.

      참고

      펜스를 확인하세요!

D3D12HelloTriangle 클래스, OnInit, LoadPipelineLoadAssets를 참조하세요.

업데이트

마지막 프레임 이후 변경해야 하는 모든 항목을 업데이트합니다.

  • 필요에 따라 상수, 꼭짓점, 인덱스 버퍼 및 다른 모든 항목을 수정합니다.

OnUpdate를 참조하세요.

렌더링

새로운 월드를 그립니다.

  • 명령 목록을 채웁니다.
    • 명령 목록 할당자를 다시 설정합니다.

      참고

      명령 할당자와 연결된 메모리를 다시 사용하세요.

       

    • 명령 목록을 다시 설정합니다.

    • 그래픽 루트 서명을 설정합니다.

      참고

      현재 명령 목록에 사용할 그래픽 루트 서명을 설정합니다.

       

    • 뷰포트 및 가위 사각형을 설정합니다.

    • 렌더링 대상으로 사용할 백 버퍼를 나타내는 리소스 장벽을 설정합니다.

      참고

      리소스 장벽은 리소스 전환을 관리하는 데 사용됩니다.

       

    • 명령을 명령 목록에 기록합니다.

    • 명령 목록이 실행된 후 백 버퍼가 표시하는 데 사용됨을 나타냅니다.

      참고

      리소스 장벽을 설정하는 또 다른 호출입니다.

       

    • 추가 기록을 위해 명령 목록을 닫습니다.

  • 명령 목록을 실행합니다.
  • 프레임을 제공합니다.
  • GPU가 완료될 때까지 기다립니다.

    참고

    펜스를 계속 업데이트하고 확인하세요.

OnRender를 참조하세요.

삭제

앱을 완전히 닫습니다.

  • GPU가 완료될 때까지 기다립니다.

    참고

    펜스를 최종적으로 확인하세요.

  • 이벤트 핸들을 닫습니다.

OnDestroy를 참조하세요.

간단한 앱 코드 예제

다음은 기본 샘플에 필요한 코드를 포함하도록 위의 코드 흐름을 확장합니다.

D3D12HelloTriangle 클래스

먼저 헤더 파일에서 다음 구조체를 사용하여 뷰포트, 가위 사각형 및 꼭짓점 버퍼를 포함한 클래스를 정의합니다.

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

프로젝트 기본 원본 파일에서 개체 초기화를 시작합니다.

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

LoadPipeline()

다음 코드는 그래픽 파이프라인에 대한 기본 사항을 만듭니다. 디바이스 및 스왑 체인을 만드는 프로세스는 Direct3D 11과 매우 비슷합니다.

  • 다음을 호출하여 디버그 계층을 사용하도록 설정합니다.

D3D12GetDebugInterface
ID3D12Debug::EnableDebugLayer

  • 디바이스를 만듭니다.

CreateDXGIFactory1
D3D12CreateDevice

  • 명령 큐 설명을 작성한 다음, 명령 큐를 만듭니다.

D3D12_COMMAND_QUEUE_DESC
ID3D12Device::CreateCommandQueue

  • 스왑 체인 설명을 작성한 다음, 스왑 체인을 만듭니다.

DXGI_SWAP_CHAIN_DESC
IDXGIFactory::CreateSwapChain
IDXGISwapChain3::GetCurrentBackBufferIndex

  • 힙 설명을 작성합니다. 그런 다음, 설명자 힙을 만듭니다.

D3D12_DESCRIPTOR_HEAP_DESC
ID3D12Device::CreateDescriptorHeap
ID3D12Device::GetDescriptorHandleIncrementSize

  • 렌더링 대상 보기를 만듭니다.

CD3DX12_CPU_DESCRIPTOR_HANDLE
GetCPUDescriptorHandleForHeapStart
IDXGISwapChain::GetBuffer
ID3D12Device::CreateRenderTargetView

이후 단계에서는 명령 할당자에서 명령 목록을 가져오고, 이를 명령 큐에 제출합니다.

렌더링 파이프라인 종속성을 로드합니다(소프트웨어 WARP 디바이스 만들기는 전적으로 선택 사항임).

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

자산을 로드하고 준비하는 작업은 시간이 오래 걸리는 프로세스입니다. 이러한 단계의 대부분은 D3D 11과 비슷하지만 일부는 D3D 12에 새로 추가되었습니다.

Direct3D 12에서 필요한 파이프라인 상태는 PSO(파이프라인 상태 개체)를 통해 명령 목록에 연결됩니다. 다음 예제에서는 PSO를 만드는 방법을 보여 줍니다. PSO는 멤버 변수로 저장하여 필요한 횟수만큼 다시 사용할 수 있습니다.

설명자 힙은 보기 및 리소스(예: 렌더링 대상 보기)에 액세스하는 방법을 정의합니다.

명령 목록 할당자 및 PSO를 사용하면 나중에 실행할 실제 명령 목록을 만들 수 있습니다.

다음 API 및 프로세스를 연속적으로 호출합니다.

  • 사용 가능한 도우미 구조체를 사용하여 빈 루트 서명을 만듭니다.

CD3DX12_ROOT_SIGNATURE_DESC
D3D12SerializeRootSignature
ID3D12Device::CreateRootSignature

  • 셰이더 D3DCompileFromFile을 로드하고 컴파일합니다.
  • 꼭짓점 입력 레이아웃을 만듭니다. D3D12_INPUT_ELEMENT_DESC.
  • 사용 가능한 도우미 구조체를 사용하여 파이프라인 상태 설명을 작성한 다음, 그래픽 파이프라인 상태를 만듭니다.

D3D12_GRAPHICS_PIPELINE_STATE_DESC
CD3DX12_RASTERIZER_DESC
CD3DX12_BLEND_DESC
ID3D12Device::CreateGraphicsPipelineState

  • 명령 목록을 만들고 닫습니다.

ID3D12Device::CreateCommandList
ID3D12GraphicsCommandList::Close

ID3D12Resource::Map
ID3D12Resource::Unmap

  • 꼭짓점 버퍼 뷰를 초기화합니다. GetGPUVirtualAddress.
  • 펜스를 만들고 초기화합니다. ID3D12Device::CreateFence.
  • 프레임 동기화에 사용할 이벤트 핸들을 만듭니다.
  • 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()

간단한 예제에서는 아무 것도 업데이트되지 않습니다.

void D3D12HelloTriangle::OnUpdate()

{
}

OnRender()

설정하는 동안 m_commandList 멤버 변수를 사용하여 모든 설정 명령을 기록하고 실행했습니다. 이제 기본 렌더링 루프에서 해당 멤버를 다시 사용할 수 있습니다.

렌더링에는 명령 목록을 채우기 위한 호출이 포함됩니다. 그러면 명령 목록을 실행하고 스왑 체인의 다음 버퍼를 표시할 수 있습니다.

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

명령 목록 할당자 및 명령 목록 자체는 먼저 다시 설정한 후에 다시 사용할 수 있습니다. 향상된 고급 시나리오에서는 여러 프레임마다 할당자를 다시 설정하는 것이 좋습니다. 메모리는 명령 목록을 실행한 직후 해제할 수 없는 할당자와 연결됩니다. 다음 예제에서는 각 프레임마다 할당자를 다시 설정하는 방법을 보여 줍니다.

이제 현재 프레임에 대한 명령 목록을 다시 사용합니다. 뷰포트를 명령 목록에 다시 연결합니다(명령 목록을 다시 설정할 때마다 그리고 명령 목록을 실행하기 전에 수행해 함). 리소스가 렌더링 대상으로 사용됨을 나타내고, 명령을 기록한 다음, 명령 목록이 실행될 때 렌더링 대상이 표시하는 데 사용됨을 나타냅니다.

명령 목록이 채워지면 다음 메서드 및 프로세스를 차례로 호출합니다.

  • 명령 할당자 및 명령 목록을 다시 설정합니다.

ID3D12CommandAllocator::Reset
ID3D12GraphicsCommandList::Reset

  • 루트 서명, 뷰포트 및 가위 사각형을 설정합니다.

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

  • 백 버퍼가 렌더링 대상으로 사용됨을 나타냅니다.

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

  • 명령을 기록합니다.

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

다음 코드는 펜스를 매우 간단하게 사용하는 방법을 보여 줍니다.

참고

프레임에서 완료될 때까지 기다리는 것은 대부분의 앱에서 너무 비효율적입니다.

 

다음 API 및 프로세스를 순서대로 호출합니다.

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

앱을 완전히 닫습니다.

  • GPU가 완료될 때까지 기다립니다.
  • 이벤트를 닫습니다.
void D3D12HelloTriangle::OnDestroy()
{

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

    CloseHandle(m_fenceEvent);
}

Direct3D 12 이해

작업 샘플