Direct3D 12 の基本的なコンポーネントの作成
Note
Windows 10用のグラフィックス集中型アプリケーションを構築する方法を示す DirectX 12 グラフィックス サンプルの DirectX-Graphics-Samples リポジトリを参照してください。
このトピックでは、Direct3D 12 の基本的なコンポーネントを作成するための呼び出しフローについて説明します。
シンプルなアプリのコード フロー
D3D 12 プログラムの最も外側のループでは、次のようなごく標準的なグラフィックス プロセスに従います。
ヒント
Direct3D 12 の新機能については、その後に注記を付けています。
Initialize
初期化では、初めにグローバル変数とクラスを設定し、初期化関数でパイプラインと資産を準備する必要があります。
- パイプラインを初期化します。
デバッグ レイヤーを有効にします。
デバイスを作成します。
コマンド キューを作成します。
スワップ チェーンを作成します。
レンダー ターゲット ビュー (RTV) の記述子ヒープを作成します。
フレーム リソース (各フレームのレンダー ターゲット ビュー) を作成します。
コマンド アロケーターを作成します。
Note
コマンド アロケーターは、コマンド リストとバンドルのためのストレージを管理します。
- 資産を初期化します。
空のルート署名を作成します。
Note
グラフィックス ルート署名では、どのリソースをグラフィックス パイプラインにバインドするかを定義します。
シェーダーをコンパイルします。
頂点入力レイアウトを作成します。
パイプライン状態オブジェクトの記述を作成してから、そのオブジェクトを作成します。
Note
パイプライン状態オブジェクトは、その時点のシェーダーすべてに加えて、特定の固定関数状態オブジェクト (たとえば入力アセンブラー、テッセレーター、ラスタライザー、出力マージャー) の状態を保持しています。
コマンド リストを作成します。
コマンド リストを閉じます。
頂点バッファーを作成して読み込みます。
頂点バッファー ビューを作成します。
フェンスを作成します。
Note
フェンスは、CPU を GPU と同期するために使用されます ( 「マルチエンジン同期」を参照)。
イベント ハンドルを作成します。
GPU が完了するまで待ちます。
Note
フェンスを調べます。
class D3D12HelloTriangle、OnInit、LoadPipeline および 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
- コマンド アロケーター ID3D12Device::CreateCommandAllocator を作成します。
後のステップで、コマンド リストがコマンド アロケーターから取得されてコマンド キューに提出されます。
レンダリング パイプラインの依存関係を読み込みます (ソフトウェア 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
- 頂点バッファー ID3D12Device::CreateCommittedResource を作成します。
- 頂点データを頂点バッファーにコピーします。
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 を使用して、すべてのセットアップ コマンドを記録して実行しました。 そのメンバーをメインのレンダー ループで再利用できます。
レンダリングするには、コマンド リストの内容を用意してからそのコマンド リストを実行し、スワップ チェーンの中の次のバッファーを提示します。
- コマンド リストの内容を用意します。
- コマンド リスト ID3D12CommandQueue::ExecuteCommandLists を実行します。
- IDXGISwapChain1::Present1 でフレームを提示します。
- GPU が完了するまで待ちます。
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
- 現在、バック バッファーを使用して表示されることを示します: ID3D12GraphicsCommandList::ResourceBarrier。
- コマンド リスト ID3D12GraphicsCommandList::Close を閉じます。
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 とプロセスをこの順に呼び出します。
- ID3D12CommandQueue::Signal
- ID3D12Fence::GetCompletedValue
- ID3D12Fence::SetEventOnCompletion
- イベントを待ちます。
- フレーム インデックス IDXGISwapChain3::GetCurrentBackBufferIndex を更新します。
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);
}
関連トピック