Creazione di un componente Direct3D 12 di base
Nota
Vedere il repository DirectX-Graphics-Samples per esempi di grafica DirectX 12 che illustrano come creare applicazioni a elevato utilizzo di grafica per Windows 10.
Questo argomento descrive il flusso di chiamata per creare un componente Direct3D 12 di base.
Flusso di codice per una semplice app
Il ciclo più esterno di un programma D3D 12 segue un processo grafico molto standard:
Suggerimento
Le funzionalità nuove di Direct3D 12 sono seguite da una nota.
- Initialize
- Ripetere
- Distruggere
Initialize
L'inizializzazione prevede innanzitutto la configurazione delle variabili e delle classi globali e una funzione di inizializzazione deve preparare la pipeline e gli asset.
- Inizializzare la pipeline.
Abilitare il livello di debug.
Creare il dispositivo.
Creare la coda dei comandi.
Creare la catena di scambio.
Creare un heap del descrittore di destinazione di rendering (RTV).
Nota
Un heap descrittore può essere considerato come una matrice di descrittori. Dove ogni descrittore descrive completamente un oggetto per la GPU.
Creare risorse frame (una visualizzazione di destinazione di rendering per ogni frame).
Creare un allocatore di comandi.
Nota
Un allocatore di comandi gestisce l'archiviazione sottostante per gli elenchi di comandi e i bundle.
- Inizializzare gli asset.
Creare una firma radice vuota.
Nota
Una firma radice grafica definisce le risorse associate alla pipeline grafica.
Compilare gli shader.
Creare il layout di input dei vertici.
Creare una descrizione dell'oggetto stato della pipeline , quindi creare l'oggetto .
Nota
Un oggetto stato della pipeline mantiene lo stato di tutti gli shader attualmente impostati, nonché di alcuni oggetti di stato della funzione fissa, ad esempio l'assembler di input, il tesselator, il rasterizzatore e la fusione dell'output.
Creare l'elenco di comandi.
Chiudere l'elenco dei comandi.
Creare e caricare i vertex buffer.
Creare le viste del buffer dei vertici.
Creare un recinto.
Nota
Un limite viene usato per sincronizzare la CPU con la GPU (vedere Sincronizzazione multi-motore).
Creare un handle di evento.
Attendere il completamento della GPU.
Nota
Controlla la recinzione!
Fare riferimento alla classe D3D12HelloTriangle, OnInit, LoadPipeline e LoadAssets.
Aggiornamento
Aggiornare tutti gli elementi da modificare dopo l'ultimo fotogramma.
- Modificare la costante, il vertice, i buffer di indice e tutto il resto, in base alle esigenze.
Fare riferimento a OnUpdate.
Rendering
Disegna il nuovo mondo.
- Popolare l'elenco di comandi.
Reimpostare l'allocatore dell'elenco di comandi.
Nota
Riutilizzare la memoria associata all'allocatore del comando.
Reimpostare l'elenco dei comandi.
Impostare la firma radice grafica.
Nota
Imposta la firma radice grafica da utilizzare per l'elenco dei comandi corrente.
Impostare il riquadro di visualizzazione e i rettangoli di forbice.
Impostare una barriera di risorse, che indica che il buffer nascosto deve essere usato come destinazione di rendering.
Nota
Le barriere alle risorse vengono usate per gestire le transizioni di risorse.
Registrare i comandi nell'elenco dei comandi.
Indicare che il buffer nascosto verrà usato per presentare dopo l'esecuzione dell'elenco di comandi.
Nota
Un'altra chiamata per impostare una barriera di risorse.
Chiudere l'elenco dei comandi per ulteriori registrazioni.
- Eseguire l'elenco di comandi.
- Presentare la cornice.
- Attendere il completamento della GPU.
Nota
Continuare ad aggiornare e controllare la recinzione.
Fare riferimento a OnRender.
Destroy
Chiudere l'app in modo pulito.
Attendere il completamento della GPU.
Nota
Controllo finale sulla recinzione.
Chiudere l'handle eventi.
Fare riferimento a OnDestroy.
Esempio di codice per un'app semplice
Di seguito viene espanso il flusso di codice precedente per includere il codice necessario per un esempio di base.
classe D3D12HelloTriangle
Definire prima di tutto la classe in un file di intestazione, tra cui un riquadro di visualizzazione, un rettangolo scissor e un buffer dei vertici usando le strutture:
#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()
Nel file di origine principale dei progetti avviare l'inizializzazione degli oggetti:
void D3D12HelloTriangle::OnInit()
{
LoadPipeline();
LoadAssets();
}
LoadPipeline()
Il codice seguente crea le nozioni di base per una pipeline grafica. Il processo di creazione di dispositivi e catene di scambio è molto simile a Direct3D 11.
- Abilitare il livello di debug con chiamate a:
D3D12GetDebugInterface
ID3D12Debug::EnableDebugLayer
- Creare il dispositivo:
CreateDXGIFactory1
D3D12CreateDevice
- Compilare una descrizione della coda dei comandi, quindi creare la coda dei comandi:
D3D12_COMMAND_QUEUE_DESC
ID3D12Device::CreateCommandQueue
- Compilare una descrizione di swapchain, quindi creare la catena di scambio:
DXGI_SWAP_CHAIN_DESC
IDXGIFactory::CreateSwapChain
IDXGISwapChain3::GetCurrentBackBufferIndex
- Compilare una descrizione dell'heap. creare quindi un heap descrittore:
D3D12_DESCRIPTOR_HEAP_DESC
ID3D12Device::CreateDescriptorHeap
ID3D12Device::GetDescriptorHandleIncrementSize
- Creare la visualizzazione di destinazione del rendering:
CD3DX12_CPU_DESCRIPTOR_HANDLE
GetCPUDescriptorHandleForHeapStart
IDXGISwapChain::GetBuffer
ID3D12Device::CreateRenderTargetView
- Creare l'allocatore del comando : ID3D12Device::CreateCommandAllocator.
Nei passaggi successivi, gli elenchi di comandi vengono ottenuti dall'allocatore dei comandi e inviati alla coda dei comandi.
Caricare le dipendenze della pipeline di rendering (si noti che la creazione di un dispositivo WARP software è completamente facoltativa).
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()
Il caricamento e la preparazione degli asset è un processo lungo. Molte di queste fasi sono simili a D3D 11, anche se alcuni sono nuovi a D3D 12.
In Direct3D 12 lo stato della pipeline richiesto viene collegato a un elenco di comandi tramite un oggetto stato della pipeline (PSO). In questo esempio viene illustrato come creare un PSO. È possibile archiviare il PSO come variabile membro e riutilizzarlo quante volte necessario.
Un heap descrittore definisce le visualizzazioni e come accedere alle risorse, ad esempio una visualizzazione di destinazione di rendering.
Con l'allocatore dell'elenco di comandi e PSO, è possibile creare l'elenco di comandi effettivo, che verrà eseguito in un secondo momento.
Le API e i processi seguenti vengono chiamati in successione.
- Creare una firma radice vuota usando la struttura helper disponibile:
CD3DX12_ROOT_SIGNATURE_DESC
D3D12SerializeRootSignature
ID3D12Device::CreateRootSignature
- Caricare e compilare gli shader: D3DCompileFromFile.
- Creare il layout di input del vertice: D3D12_INPUT_ELEMENT_DESC.
- Compilare una descrizione dello stato della pipeline usando le strutture helper disponibili, quindi creare lo stato della pipeline grafica:
D3D12_GRAPHICS_PIPELINE_STATE_DESC
CD3DX12_RASTERIZER_DESC
CD3DX12_BLEND_DESC
ID3D12Device::CreateGraphicsPipelineState
- Creare, quindi chiudere, un elenco di comandi:
ID3D12Device::CreateCommandList
ID3D12GraphicsCommandList::Close
- Creare il buffer del vertice: ID3D12Device::CreateCommittedResource.
- Copiare i dati del vertice nel buffer dei vertici:
ID3D12Resource::Map
ID3D12Resource::Unmap
- Inizializzare la vista del buffer del vertice: GetGPUVirtualAddress.
- Creare e inizializzare la recinzione: ID3D12Device::CreateFence.
- Creare un handle di eventi da usare con la sincronizzazione dei frame.
- Attendere il completamento della 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()
Per un semplice esempio, non viene aggiornato nulla.
void D3D12HelloTriangle::OnUpdate()
{
}
OnRender()
Durante la configurazione, la variabile membro m_commandList è stata usata per registrare ed eseguire tutti i comandi configurati. È ora possibile riutilizzare tale membro nel ciclo di rendering principale.
Il rendering prevede una chiamata per popolare l'elenco di comandi, quindi l'elenco di comandi può essere eseguito e il buffer successivo nella catena di scambio presentato:
- Popolare l'elenco dei comandi.
- Eseguire l'elenco dei comandi: ID3D12CommandQueue::ExecuteCommandLists.
- IDXGISwapChain1::P resent1 la cornice.
- Attendere il completamento della 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();
}
PopolamentoCommandList()
È necessario reimpostare l'allocatore dell'elenco di comandi e l'elenco di comandi stesso prima di poterli riutilizzare. Negli scenari più avanzati potrebbe essere opportuno reimpostare l'allocatore ogni più fotogrammi. La memoria è associata all'allocatore che non può essere rilasciata immediatamente dopo l'esecuzione di un elenco di comandi. In questo esempio viene illustrato come reimpostare l'allocatore dopo ogni frame.
Riutilizzare ora l'elenco dei comandi per il frame corrente. Riassegnare il riquadro di visualizzazione all'elenco di comandi (che deve essere eseguito ogni volta che viene reimpostato un elenco di comandi e prima dell'esecuzione dell'elenco di comandi), indicare che la risorsa verrà usata come destinazione di rendering, comandi di record e quindi indicare che la destinazione di rendering verrà usata per presentare al termine dell'esecuzione dell'elenco di comandi.
Gli elenchi di comandi popolamento chiamano i metodi e i processi seguenti:
- Reimpostare l'allocatore dei comandi e l'elenco di comandi:
ID3D12CommandAllocator::Reset
ID3D12GraphicsCommandList::Reset
- Impostare le firme radice, il riquadro di visualizzazione e i rettangoli di forbice:
ID3D12GraphicsCommandList::SetGraphicsRootSignature
ID3D12GraphicsCommandList::RSSetViewports
ID3D12GraphicsCommandList::RSSetScissorRects
- Indicare che il buffer back deve essere usato come destinazione di rendering:
ID3D12GraphicsCommandList::ResourceBarrier
ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart
ID3D12GraphicsCommandList::OMSetRenderTargets
- Registrare i comandi:
ID3D12GraphicsCommandList::ClearRenderTargetView
ID3D12GraphicsCommandList::IASetPrimitiveTopology
ID3D12GraphicsCommandList::IASetVertexBuffers
ID3D12GraphicsCommandList::D rawInstanced
- Indicare che il buffer nascosto verrà ora usato per presentare: ID3D12GraphicsCommandList::ResourceBarrier.
- Chiudere l'elenco dei comandi : 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()
Il codice seguente illustra un uso eccessivamente semplificato delle recinzioni.
Nota
L'attesa del completamento di un frame è troppo inefficiente per la maggior parte delle app.
Le API e i processi seguenti vengono chiamati in ordine:
- ID3D12CommandQueue::Signal
- ID3D12Fence::GetCompletedValue
- ID3D12Fence::SetEventOnCompletion
- Attendere l'evento.
- Aggiornare l'indice del frame: 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()
Chiudere l'app in modo pulito.
- Attendere il completamento della GPU.
- Chiudere l'evento.
void D3D12HelloTriangle::OnDestroy()
{
// Wait for the GPU to be done with all resources.
WaitForPreviousFrame();
CloseHandle(m_fenceEvent);
}
Argomenti correlati