Создание базового компонента Direct3D 12
Примечание
Ознакомьтесь с репозиторием DirectX-Graphics-Samples для примеров графики DirectX 12, демонстрирующих создание приложений с интенсивным использованием графики для Windows 10.
В этом разделе описывается поток вызовов для создания базового компонента Direct3D 12.
Поток кода для простого приложения
Самый внешний цикл программы D3D 12 следует очень стандартному графическому процессу:
Совет
За функциями, новыми для Direct3D 12, следует примечание.
- Initialize
- Повторите
- Уничтожить
Initialize
Инициализация включает в себя сначала настройку глобальных переменных и классов, а функция инициализации должна подготовить конвейер и ресурсы.
- Инициализируйте конвейер.
Включите уровень отладки.
Создайте устройство.
Создайте очередь команд.
Создайте цепочку буферов.
Создайте кучу дескрипторов целевого представления отрисовки (RTV).
Примечание
Кучу дескрипторов можно рассматривать как массив дескрипторов. Где каждый дескриптор полностью описывает объект для GPU.
Создание ресурсов кадра (целевое представление отрисовки для каждого кадра).
Создайте распределитель команд.
Примечание
Распределитель команд управляет базовым хранилищем для списков команд и пакетов.
- Инициализируйте ресурсы.
Создайте пустую корневую подпись.
Примечание
Корневая подпись графики определяет, какие ресурсы привязаны к графическому конвейеру.
Скомпилируйте шейдеры.
Создайте макет входных данных вершины.
Создайте описание объекта состояния конвейера , а затем создайте объект .
Примечание
Объект состояния конвейера поддерживает состояние всех заданных в настоящее время шейдеров, а также некоторых объектов состояния фиксированной функции (таких как входной ассемблер, тесселатор, растеризатор и объединение выходных данных).
Создайте список команд.
Закройте список команд.
Создайте и загрузите буферы вершин.
Создайте представления буфера вершин.
Создайте ограждение.
Примечание
Ограждение используется для синхронизации ЦП с GPU (см. раздел Синхронизация с несколькими ядрами).
Создайте дескриптор события.
Дождитесь завершения работы GPU.
Примечание
Проверьте на заборе!
Обратитесь к классам D3D12HelloTriangle, OnInit, LoadPipeline и LoadAssets.
Update
Обновите все, что должно измениться с момента последнего кадра.
- При необходимости измените константы, вершины, буферы индексов и все остальное.
Render
Нарисуйте новый мир.
- Заполните список команд.
Сбросьте распределитель списка команд.
Примечание
Повторно используйте память, связанную с распределителем команд.
Сбросьте список команд.
Задайте подпись корня графики.
Примечание
Задает корневую подпись графического элемента, используемую для текущего списка команд.
Задайте прямоугольники окна просмотра и ножницы.
Задайте барьер ресурсов, указывающий, что задний буфер будет использоваться в качестве целевого объекта отрисовки.
Примечание
Барьеры ресурсов используются для управления переходами ресурсов.
Запишите команды в список команд.
Укажите, что задний буфер будет использоваться для представления после выполнения списка команд.
Примечание
Еще один вызов для установки барьера ресурса.
Закройте список команд для дальнейшей записи.
- Выполните список команд.
- Представить кадр.
- Дождитесь завершения работы GPU.
Примечание
Продолжайте обновлять и проверять забор.
См. раздел OnRender.
Уничтожить
Закройте приложение.
Дождитесь завершения работы GPU.
Примечание
Заключительный проверка на заборе.
Закройте дескриптор события.
Обратитесь к OnDeхистро.
Пример кода для простого приложения
Ниже показано, как развернуть приведенный выше поток кода, чтобы включить код, необходимый для базового примера.
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()
В main исходном файле проектов начните инициализацию объектов :
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 использовалась для записи и выполнения всех команд настройки. Теперь этот элемент можно повторно использовать в цикле отрисовки main.
Отрисовка включает вызов для заполнения списка команд, после чего можно выполнить список команд и представить следующий буфер в цепочке буферов:
- Заполните список команд.
- Выполните список команд ID3D12CommandQueue::ExecuteCommandLists.
- IDXGISwapChain1::P resent1 кадр.
- Дождитесь завершения работы 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();
}
ЗаполнениеCommandList()
Прежде чем их можно будет использовать повторно, необходимо сбросить распределителя списков команд и сам список команд. В более сложных сценариях может быть целесообразно сбрасывать распределителе каждые несколько кадров. Память связана с распределителем, которая не может быть освобождена сразу после выполнения списка команд. В этом примере показано, как сбросить распределителя после каждого кадра.
Теперь повторно используйте список команд для текущего кадра. Повторно подключите окно просмотра к списку команд (что должно выполняться при сбросе списка команд и перед выполнением списка команд), укажите, что ресурс будет использоваться в качестве целевого объекта отрисовки, а затем укажите, что целевой объект отрисовки будет использоваться для представления после завершения выполнения списка команд.
Заполнение списков команд по очереди вызывает следующие методы и процессы:
- Сбросьте распределителя команд и список команд:
ID3D12CommandAllocator::Reset
ID3D12GraphicsCommandList::Reset
- Задайте прямоугольники корневой сигнатуры, окна просмотра и ножницы:
ID3D12GraphicsCommandList::SetGraphicsRootSignature
ID3D12GraphicsCommandList::RSSetViewports
ID3D12GraphicsCommandList::RSSetScissorRects
- Укажите, что задний буфер будет использоваться в качестве целевого объекта отрисовки:
ID3D12GraphicsCommandList::ResourceBarrier
ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart
ID3D12GraphicsCommandList::OMSetRenderTargets
- Запишите команды:
ID3D12GraphicsCommandList::ClearRenderTargetView
ID3D12GraphicsCommandList::IASetPrimitiveTopology
ID3D12GraphicsCommandList::IASetVertexBuffers
ID3D12GraphicsCommandList::D rawInstanced
- Укажите, что теперь для представления будет использоваться задний буфер: 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()
В следующем коде показано упрощенное использование ограждений.
Примечание
Ожидание завершения кадра слишком неэффективно для большинства приложений.
Следующие 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();
}
ОнДестрой()
Закройте приложение.
- Дождитесь завершения работы GPU.
- Закройте событие.
void D3D12HelloTriangle::OnDestroy()
{
// Wait for the GPU to be done with all resources.
WaitForPreviousFrame();
CloseHandle(m_fenceEvent);
}
Связанные темы