Criando um componente básico do Direct3D 12
Observação
Consulte o repositório DirectX-Graphics-Samples para obter exemplos de elementos gráficos DirectX 12 que demonstram como criar aplicativos com uso intensivo de elementos gráficos para Windows 10.
Este tópico descreve o fluxo de chamadas para criar um componente básico do Direct3D 12.
- Fluxo de código para um aplicativo simples
- Exemplo de código para um aplicativo simples
- Tópicos relacionados
Fluxo de código para um aplicativo simples
O loop mais externo de um programa D3D 12 segue um processo gráfico muito padrão:
Dica
Os recursos novos no Direct3D 12 são seguidos por uma observação.
- Initialize
- Repetir
- Destruir
Initialize
A inicialização envolve primeiro a configuração das variáveis e classes globais, e uma função de inicialização deve preparar o pipeline e os ativos.
- Inicialize o pipeline.
Habilite a camada de depuração.
Crie o dispositivo.
Crie a fila de comandos.
Crie a cadeia de troca.
Crie um heap de descritor rtv (exibição de destino de renderização).
Observação
Um heap de descritor pode ser considerado como uma matriz de descritores. Em que cada descritor descreve totalmente um objeto para a GPU.
Criar recursos de quadro (uma exibição de destino de renderização para cada quadro).
Crie um alocador de comandos.
Observação
Um alocador de comando gerencia o armazenamento subjacente para listas de comandos e pacotes.
- Inicialize os ativos.
Crie uma assinatura raiz vazia.
Observação
Uma assinatura raiz de gráficos define quais recursos estão associados ao pipeline gráfico.
Compile os sombreadores.
Crie o layout de entrada de vértice.
Crie uma descrição do objeto de estado do pipeline e, em seguida, crie o objeto .
Observação
Um objeto de estado de pipeline mantém o estado de todos os sombreadores atualmente definidos, bem como determinados objetos de estado de função fixa (como o assembler de entrada, o tesselator, o rasterizador e a fusão de saída).
Crie a lista de comandos.
Feche a lista de comandos.
Crie e carregue os buffers de vértice.
Crie as exibições do buffer de vértice.
Crie uma cerca.
Observação
Uma cerca é usada para sincronizar a CPU com a GPU (consulte Sincronização de vários mecanismos).
Criar um identificador de evento.
Aguarde a conclusão da GPU.
Observação
Verifique a cerca!
Consulte a classe D3D12HelloTriangle, OnInit, LoadPipeline e LoadAssets.
Atualizar
Atualize tudo o que deve mudar desde o último quadro.
- Modifique a constante, o vértice, os buffers de índice e todo o resto, conforme necessário.
Consulte OnUpdate.
Renderizar
Desenhe o novo mundo.
- Preencha a lista de comandos.
Redefina o alocador de lista de comandos.
Observação
Reutilize a memória associada ao alocador de comando.
Redefina a lista de comandos.
Defina a assinatura raiz de gráficos.
Observação
Define a assinatura raiz de gráficos a ser usada para a lista de comandos atual.
Defina os retângulos de visor e tesoura.
Defina uma barreira de recursos, indicando que o buffer de fundo deve ser usado como um destino de renderização.
Observação
As barreiras de recursos são usadas para gerenciar transições de recursos.
Registre comandos na lista de comandos.
Indique que o buffer de fundo será usado para apresentar após a execução da lista de comandos.
Observação
Outra chamada para definir uma barreira de recursos.
Feche a lista de comandos para gravação adicional.
- Execute a lista de comandos.
- Apresente o quadro.
- Aguarde a conclusão da GPU.
Observação
Continue atualizando e verificando a cerca.
Consulte OnRender.
Destruir
Feche o aplicativo corretamente.
Aguarde a conclusão da GPU.
Observação
O marcar final na cerca.
Feche o identificador de evento.
Consulte OnDestroy.
Exemplo de código para um aplicativo simples
O exemplo a seguir expande o fluxo de código acima para incluir o código necessário para um exemplo básico.
classe D3D12HelloTriangle
Primeiro, defina a classe em um arquivo de cabeçalho, incluindo um visor, um retângulo de tesoura e um buffer de vértice usando as estruturas:
#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()
Nos projetos main arquivo de origem, comece a inicializar os objetos:
void D3D12HelloTriangle::OnInit()
{
LoadPipeline();
LoadAssets();
}
LoadPipeline()
O código a seguir cria as noções básicas para um pipeline gráfico. O processo de criação de dispositivos e cadeias de troca é muito semelhante ao Direct3D 11.
- Habilite a camada de depuração, com chamadas para:
D3D12GetDebugInterface
ID3D12Debug::EnableDebugLayer
- Crie o dispositivo:
CreateDXGIFactory1
D3D12CreateDevice
- Preencha uma descrição da fila de comandos e crie a fila de comandos:
D3D12_COMMAND_QUEUE_DESC
ID3D12Device::CreateCommandQueue
- Preencha uma descrição de swapchain e crie a cadeia de troca:
DXGI_SWAP_CHAIN_DESC
IDXGIFactory::CreateSwapChain
IDXGISwapChain3::GetCurrentBackBufferIndex
- Preencha uma descrição de heap. em seguida, crie um heap de descritor:
D3D12_DESCRIPTOR_HEAP_DESC
ID3D12Device::CreateDescriptorHeap
ID3D12Device::GetDescriptorHandleIncrementSize
- Crie a exibição de destino de renderização:
CD3DX12_CPU_DESCRIPTOR_HANDLE
GetCPUDescriptorHandleForHeapStart
IDXGISwapChain::GetBuffer
ID3D12Device::CreateRenderTargetView
- Crie o alocador de comando: ID3D12Device::CreateCommandAllocator.
Em etapas posteriores, as listas de comandos são obtidas do alocador de comandos e enviadas para a fila de comandos.
Carregue as dependências do pipeline de renderização (observe que a criação de um dispositivo WARP de software é totalmente opcional).
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()
Carregar e preparar ativos é um processo longo. Muitos desses estágios são semelhantes ao D3D 11, embora alguns sejam novos no D3D 12.
No Direct3D 12, o estado do pipeline necessário é anexado a uma lista de comandos por meio de um PSO ( objeto de estado de pipeline ). Este exemplo mostra como criar um PSO. Você pode armazenar o PSO como uma variável de membro e reutilizá-lo quantas vezes forem necessárias.
Um heap de descritor define as exibições e como acessar recursos (por exemplo, uma exibição de destino de renderização).
Com o alocador de lista de comandos e o PSO, você pode criar a lista de comandos real, que será executada posteriormente.
As APIs e os processos a seguir são chamados em sucessão.
- Crie uma assinatura raiz vazia usando a estrutura auxiliar disponível:
CD3DX12_ROOT_SIGNATURE_DESC
D3D12SerializeRootSignature
ID3D12Device::CreateRootSignature
- Carregue e compile os sombreadores: D3DCompileFromFile.
- Crie o layout de entrada de vértice: D3D12_INPUT_ELEMENT_DESC.
- Preencha uma descrição do estado do pipeline, usando as estruturas auxiliares disponíveis e, em seguida, crie o estado do pipeline gráfico:
D3D12_GRAPHICS_PIPELINE_STATE_DESC
CD3DX12_RASTERIZER_DESC
CD3DX12_BLEND_DESC
ID3D12Device::CreateGraphicsPipelineState
- Crie e feche uma lista de comandos:
ID3D12Device::CreateCommandList
ID3D12GraphicsCommandList::Close
- Crie o buffer de vértice: ID3D12Device::CreateCommittedResource.
- Copie os dados de vértice para o buffer de vértice:
ID3D12Resource::Map
ID3D12Resource::Unmap
- Inicializar a exibição do buffer de vértice: GetGPUVirtualAddress.
- Crie e inicialize a cerca: ID3D12Device::CreateFence.
- Crie um identificador de evento para uso com sincronização de quadros.
- Aguarde a conclusão da 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()
Para um exemplo simples, nada é atualizado.
void D3D12HelloTriangle::OnUpdate()
{
}
OnRender()
Durante a configuração, a variável de membro m_commandList foi usada para registrar e executar todos os comandos de configuração. Agora você pode reutilizar esse membro no loop de renderização main.
A renderização envolve uma chamada para preencher a lista de comandos e, em seguida, a lista de comandos pode ser executada e o próximo buffer na cadeia de troca apresentado:
- Preencha a lista de comandos.
- Execute a lista de comandos: ID3D12CommandQueue::ExecuteCommandLists.
- IDXGISwapChain1::P resent1 o quadro.
- Aguarde até que a GPU seja concluída.
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()
Você deve redefinir o alocador de lista de comandos e a própria lista de comandos antes de poder reutilizá-los. Em cenários mais avançados, pode fazer sentido redefinir o alocador a cada vários quadros. A memória está associada ao alocador que não pode ser liberado imediatamente após a execução de uma lista de comandos. Este exemplo mostra como redefinir o alocador após cada quadro.
Agora, reutilize a lista de comandos para o quadro atual. Reanexe o visor à lista de comandos (o que deve ser feito sempre que uma lista de comandos for redefinida e antes que a lista de comandos seja executada), indique que o recurso estará em uso como um destino de renderização, registre comandos e indique que o destino de renderização será usado para apresentar quando a lista de comandos terminar de ser executada.
Preencher listas de comandos chama os seguintes métodos e processos por sua vez:
- Redefina o alocador de comandos e a lista de comandos:
ID3D12CommandAllocator::Reset
ID3D12GraphicsCommandList::Reset
- Defina os retângulos de assinatura raiz, visor e tesoura:
ID3D12GraphicsCommandList::SetGraphicsRootSignature
ID3D12GraphicsCommandList::RSSetViewports
ID3D12GraphicsCommandList::RSSetScissorRects
- Indique que o buffer de fundo deve ser usado como um destino de renderização:
ID3D12GraphicsCommandList::ResourceBarrier
ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart
ID3D12GraphicsCommandList::OMSetRenderTargets
- Registre os comandos:
ID3D12GraphicsCommandList::ClearRenderTargetView
ID3D12GraphicsCommandList::IASetPrimitiveTopology
ID3D12GraphicsCommandList::IASetVertexBuffers
ID3D12GraphicsCommandList::D rawInstanced
- Indique que o buffer traseiro agora será usado para apresentar: ID3D12GraphicsCommandList::ResourceBarrier.
- Feche a lista de comandos: 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()
O código a seguir mostra um uso simplificado demais de cercas.
Observação
Aguardar a conclusão de um quadro é muito ineficiente para a maioria dos aplicativos.
As seguintes APIs e processos são chamados na ordem:
- ID3D12CommandQueue::Signal
- ID3D12Fence::GetCompletedValue
- ID3D12Fence::SetEventOnCompletion
- Aguarde o evento.
- Atualize o índice de quadros: 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()
Feche o aplicativo de forma limpa.
- Aguarde até que a GPU seja concluída.
- Feche o evento.
void D3D12HelloTriangle::OnDestroy()
{
// Wait for the GPU to be done with all resources.
WaitForPreviousFrame();
CloseHandle(m_fenceEvent);
}
Tópicos relacionados