Crear un componente básico de Direct3D 12
Nota
Consulte el repositorio DirectX-Graphics-Samples para ejemplos de gráficos de DirectX 12 que muestran cómo crear aplicaciones con uso intensivo de gráficos para Windows 10.
En este tema se describe el flujo de llamadas para crear un componente básico de Direct3D 12.
- Flujo de código para una aplicación sencilla
- Ejemplo de código para una aplicación sencilla
- Temas relacionados
Flujo de código para una aplicación sencilla
El bucle más externo de un programa D3D 12 sigue un proceso gráfico muy estándar:
Sugerencia
Las características nuevas en Direct3D 12 van seguidas de una nota.
- Inicialización
- Repetir
- Destruir
Initialize
La inicialización implica configurar primero las variables globales y las clases, y una función initialize debe preparar la canalización y los recursos.
- Inicialice la canalización.
Habilite la capa de depuración.
Cree el dispositivo.
Cree la cola de comandos.
Cree la cadena de intercambio.
Cree un montón de descriptores de vista de destino de representación (RTV).
Nota
Un montón de descriptores se puede considerar como una matriz de descriptores. Donde cada descriptor describe completamente un objeto a la GPU.
Cree recursos de marco (una vista de destino de representación para cada fotograma).
Cree un asignador de comandos.
Nota
Un asignador de comandos administra el almacenamiento subyacente para listas y agrupaciones de comandos.
- Inicialice los recursos.
Cree una firma raíz vacía.
Nota
Una firma raíz de gráficos define qué recursos están enlazados a la canalización de gráficos.
Compile los sombreadores.
Cree el diseño de entrada del vértice.
Cree una descripción del objeto de estado de canalización y, a continuación, cree el objeto .
Nota
Un objeto de estado de canalización mantiene el estado de todos los sombreadores establecidos actualmente, así como ciertos objetos de estado de función fijos (como el ensamblador de entrada, el tesselator, el rasterizador y la fusión de salida).
Cree la lista de comandos.
Cierre la lista de comandos.
Cree y cargue los búferes de vértices.
Cree las vistas del búfer de vértices.
Cree una valla.
Nota
Se usa una barrera para sincronizar la CPU con la GPU (consulte Sincronización de varios motores).
Cree un identificador de eventos.
Espere a que finalice la GPU.
Nota
¡Compruebe la valla!
Consulte la clase D3D12HelloTriangle, OnInit, LoadPipeline y LoadAssets.
Actualizar
Actualice todo lo que debe cambiar desde el último fotograma.
- Modifique la constante, el vértice, los búferes de índice y todo lo demás, según sea necesario.
Consulte OnUpdate.
Representación
Dibuja el nuevo mundo.
- Rellene la lista de comandos.
Restablezca el asignador de lista de comandos.
Nota
Vuelva a usar la memoria asociada al asignador de comandos.
Restablezca la lista de comandos.
Establezca la firma raíz de gráficos.
Nota
Establece la firma raíz de gráficos que se va a usar para la lista de comandos actual.
Establezca los rectángulos de tijera y ventanilla.
Establezca una barrera de recursos, lo que indica que el búfer de reserva se va a usar como destino de representación.
Nota
Las barreras de recursos se usan para administrar las transiciones de recursos.
Registre los comandos en la lista de comandos.
Indique que el búfer de reserva se usará para presentar una vez ejecutada la lista de comandos.
Nota
Otra llamada para establecer una barrera de recursos.
Cierre la lista de comandos para grabar más.
- Ejecute la lista de comandos.
- Presente el marco.
- Espere a que finalice la GPU.
Nota
Siga actualizando y comprobando la valla.
Consulte OnRender.
Destruir
Cierre limpiamente la aplicación.
Espere a que finalice la GPU.
Nota
Comprobación final sobre la valla.
Cierre el identificador de eventos.
Consulte OnDestroy.
Ejemplo de código para una aplicación sencilla
A continuación se expande el flujo de código anterior para incluir el código necesario para un ejemplo básico.
Clase D3D12HelloTriangle
En primer lugar, defina la clase en un archivo de encabezado, incluida una ventanilla, un rectángulo de tijera y un búfer de vértices mediante las estructuras:
#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()
En el archivo de código fuente principal de los proyectos, empiece a inicializar los objetos:
void D3D12HelloTriangle::OnInit()
{
LoadPipeline();
LoadAssets();
}
LoadPipeline()
El código siguiente crea los conceptos básicos de una canalización de gráficos. El proceso de creación de dispositivos y cadenas de intercambio es muy similar a Direct3D 11.
- Habilite la capa de depuración, con llamadas a:
D3D12GetDebugInterface
ID3D12Debug::EnableDebugLayer
- Cree el dispositivo:
CreateDXGIFactory1
D3D12CreateDevice
- Rellene una descripción de la cola de comandos y, a continuación, cree la cola de comandos:
D3D12_COMMAND_QUEUE_DESC
ID3D12Device::CreateCommandQueue
- Rellene una descripción de la cadena de intercambio y, a continuación, cree la cadena de intercambio:
DXGI_SWAP_CHAIN_DESC
IDXGIFactory::CreateSwapChain
IDXGISwapChain3::GetCurrentBackBufferIndex
- Rellene una descripción del montón. después, cree un montón de descriptores:
D3D12_DESCRIPTOR_HEAP_DESC
ID3D12Device::CreateDescriptorHeap
ID3D12Device::GetDescriptorHandleIncrementSize
- Cree la vista de destino de representación:
CD3DX12_CPU_DESCRIPTOR_HANDLE
GetCPUDescriptorHandleForHeapStart
IDXGISwapChain::GetBuffer
ID3D12Device::CreateRenderTargetView
- Cree el asignador de comandos: ID3D12Device::CreateCommandAllocator.
En pasos posteriores, las listas de comandos se obtienen del asignador de comandos y se envían a la cola de comandos.
Cargue las dependencias de canalización de representación (tenga en cuenta que la creación de un dispositivo WARP de software es 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()
La carga y preparación de recursos es un proceso largo. Muchas de estas fases son similares a D3D 11, aunque algunas son nuevas en D3D 12.
En Direct3D 12, el estado de canalización necesario se adjunta a una lista de comandos a través de un objeto de estado de canalización (XAML). En este ejemplo se muestra cómo crear un ARCHIVO). Puede almacenar el ARCHIVO COMO variable miembro y reutilizarlo tantas veces como sea necesario.
Un montón de descriptores define las vistas y cómo acceder a los recursos (por ejemplo, una vista de destino de representación).
Con el asignador de lista de comandos y EL ARCHIVO, puede crear la lista de comandos real, que se ejecutará más adelante.
Se llama a las siguientes API y procesos en sucesión.
- Cree una firma raíz vacía mediante la estructura auxiliar disponible:
CD3DX12_ROOT_SIGNATURE_DESC
D3D12SerializeRootSignature
ID3D12Device::CreateRootSignature
- Cargue y compile los sombreadores: D3DCompileFromFile.
- Cree el diseño de entrada de vértices: D3D12_INPUT_ELEMENT_DESC.
- Rellene una descripción del estado de la canalización, con las estructuras auxiliares disponibles y, a continuación, cree el estado de la canalización de gráficos:
D3D12_GRAPHICS_PIPELINE_STATE_DESC
CD3DX12_RASTERIZER_DESC
CD3DX12_BLEND_DESC
ID3D12Device::CreateGraphicsPipelineState
- Cree y, a continuación, cierre una lista de comandos:
ID3D12Device::CreateCommandList
ID3D12GraphicsCommandList::Close
- Cree el búfer de vértices: ID3D12Device::CreateCommittedResource.
- Copie los datos del vértice en el búfer de vértices:
ID3D12Resource::Map
ID3D12Resource::Unmap
- Inicialice la vista del búfer de vértices: GetGPUVirtualAddress.
- Cree e inicialice la valla: ID3D12Device::CreateFence.
- Cree un identificador de evento para usarlo con la sincronización de fotogramas.
- Espere a que finalice la 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 obtener un ejemplo sencillo, no se actualiza nada.
void D3D12HelloTriangle::OnUpdate()
{
}
OnRender()
Durante la configuración, la variable miembro m_commandList se usó para registrar y ejecutar todos los comandos de configuración. Ahora puede reutilizar ese miembro en el bucle de representación principal.
La representación implica una llamada para rellenar la lista de comandos y, a continuación, se puede ejecutar la lista de comandos y el siguiente búfer de la cadena de intercambio que se presenta:
- Rellene la lista de comandos.
- Ejecute la lista de comandos: ID3D12CommandQueue::ExecuteCommandLists.
- IDXGISwapChain1::P resent1 el marco.
- Espere a que finalice la 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()
Debe restablecer el asignador de lista de comandos y la propia lista de comandos para poder reutilizarlos. En escenarios más avanzados, podría tener sentido restablecer el asignador cada varios fotogramas. La memoria está asociada al asignador que no se puede liberar inmediatamente después de ejecutar una lista de comandos. En este ejemplo se muestra cómo restablecer el asignador después de cada fotograma.
Ahora, reutilice la lista de comandos para el marco actual. Vuelva a adjuntar la ventanilla a la lista de comandos (que debe realizarse siempre que se restablezca una lista de comandos y antes de ejecutar la lista de comandos), indique que el recurso se usará como destino de representación, comandos de registro y, a continuación, indique que el destino de representación se usará para presentar cuando la lista de comandos haya terminado de ejecutarse.
Rellenar listas de comandos llama a los métodos y procesos siguientes a su vez:
- Restablezca el asignador de comandos y la lista de comandos:
ID3D12CommandAllocator::Reset
ID3D12GraphicsCommandList::Reset
- Establezca la firma raíz, la ventanilla y los rectángulos de tijeras:
ID3D12GraphicsCommandList::SetGraphicsRootSignature
ID3D12GraphicsCommandList::RSSetViewports
ID3D12GraphicsCommandList::RSSetScissorRects
- Indique que el búfer de reserva se va a usar como destino de representación:
ID3D12GraphicsCommandList::ResourceBarrier
ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart
ID3D12GraphicsCommandList::OMSetRenderTargets
- Registre los comandos:
ID3D12GraphicsCommandList::ClearRenderTargetView
ID3D12GraphicsCommandList::IASetPrimitiveTopology
ID3D12GraphicsCommandList::IASetVertexBuffers
ID3D12GraphicsCommandList::D rawInstanced
- Indique que el búfer de reserva se usará ahora para presentar: ID3D12GraphicsCommandList::ResourceBarrier.
- Cierre la 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()
En el código siguiente se muestra un uso demasiado simplificado de barreras.
Nota
Esperar a que se complete un fotograma es demasiado ineficaz para la mayoría de las aplicaciones.
Se llama a las SIGUIENTES API y procesos en orden:
- ID3D12CommandQueue::Signal
- ID3D12Fence::GetCompletedValue
- ID3D12Fence::SetEventOnCompletion
- Espere al evento.
- Actualice el índice de fotogramas: 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()
Cierre limpiamente la aplicación.
- Espere a que finalice la GPU.
- Cierre el evento.
void D3D12HelloTriangle::OnDestroy()
{
// Wait for the GPU to be done with all resources.
WaitForPreviousFrame();
CloseHandle(m_fenceEvent);
}
Temas relacionados