Direct3D 12 の基本的なコンポーネントの作成

Note

Windows 10用のグラフィックス集中型アプリケーションを構築する方法を示す DirectX 12 グラフィックス サンプルの DirectX-Graphics-Samples リポジトリを参照してください。

このトピックでは、Direct3D 12 の基本的なコンポーネントを作成するための呼び出しフローについて説明します。

シンプルなアプリのコード フロー

D3D 12 プログラムの最も外側のループでは、次のようなごく標準的なグラフィックス プロセスに従います。

ヒント

Direct3D 12 の新機能については、その後に注記を付けています。

Initialize

初期化では、初めにグローバル変数とクラスを設定し、初期化関数でパイプラインと資産を準備する必要があります。

  • パイプラインを初期化します。
    • デバッグ レイヤーを有効にします。

    • デバイスを作成します。

    • コマンド キューを作成します。

    • スワップ チェーンを作成します。

    • レンダー ターゲット ビュー (RTV) の記述子ヒープを作成します。

      Note

      記述子ヒープは、記述子の配列と考えることができます。 この記述子はそれぞれ 1 つのオブジェクトを GPU に対して完全に記述します。

    • フレーム リソース (各フレームのレンダー ターゲット ビュー) を作成します。

    • コマンド アロケーターを作成します。

      Note

      コマンド アロケーターは、コマンド リストとバンドルのためのストレージを管理します。  

  • 資産を初期化します。
    • 空のルート署名を作成します。

      Note

      グラフィックス ルート署名では、どのリソースをグラフィックス パイプラインにバインドするかを定義します。

    • シェーダーをコンパイルします。

    • 頂点入力レイアウトを作成します。

    • パイプライン状態オブジェクトの記述を作成してから、そのオブジェクトを作成します。

      Note

      パイプライン状態オブジェクトは、その時点のシェーダーすべてに加えて、特定の固定関数状態オブジェクト (たとえば入力アセンブラーテッセレーターラスタライザー出力マージャー) の状態を保持しています。

    • コマンド リストを作成します。

    • コマンド リストを閉じます。

    • 頂点バッファーを作成して読み込みます。

    • 頂点バッファー ビューを作成します。

    • フェンスを作成します。

      Note

      フェンスは、CPU を GPU と同期するために使用されます ( 「マルチエンジン同期」を参照)。

    • イベント ハンドルを作成します。

    • GPU が完了するまで待ちます。

      Note

      フェンスを調べます。

class D3D12HelloTriangleOnInitLoadPipeline および LoadAssets を参照してください。

更新

最後のフレーム以降に変更する必要があるすべてのものを更新します。

  • 定数、頂点、インデックス バッファー、およびその他すべての必要なものに変更を加えます。

OnUpdate を参照してください。

レンダー

新しいワールドを描画します。

  • コマンド リストの内容を用意します。
    • コマンド リストのアロケーターをリセットします。

      Note

      コマンド アロケーターに関連付けられているメモリを再利用します。

       

    • コマンド リストをリセットします。

    • グラフィックス ルート署名を設定します。

      Note

      現在のコマンド リストで使用するグラフィックス ルート署名を設定します。

       

    • ビューポートとシザリング四角形を設定します。

    • リソース バリアを設定します。これによって、バック バッファーがレンダー ターゲットとして使用されることを示します。

      Note

      リソース バリアは、リソースの移行を管理するために使用されます。

       

    • コマンドをコマンド リストに記録します。

    • バック バッファーがコマンド リストの実行完了後の提示に使用されることを示します。

      Note

      リソース バリアを設定するための別の呼び出しです。

       

    • コマンド リストを閉じます。それ以上は記録できなくなります。

  • コマンド リストを実行します。
  • フレームを提示します。
  • GPU が完了するまで待ちます。

    Note

    フェンスの更新と確認を続けます。

OnRender を参照してください。

Destroy

アプリをクリーンに閉じます。

  • GPU が完了するまで待ちます。

    Note

    フェンスの最終確認を行います。

  • イベント ハンドルを閉じます。

OnDestroy を参照してください。

シンプルなアプリのコード例

次に示すのは、基本的なサンプルを作るために上記のコード フローに加えて必要になるコードです。

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

次のコードでは、フェンスの使い方をかなり単純化しています。

Note

1 つのフレームが完了するまで待つことは、ほとんどのアプリにとって非効率的です。

 

次の 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 とは

実用的なサンプル