Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Notatka
Zobacz repozytorium DirectX-Graphics-Samples z przykładami grafiki DirectX 12, które pokazują, jak tworzyć aplikacje intensywnie korzystające z grafiki dla systemu Windows 10.
W tym temacie opisano przepływ wywołań w celu utworzenia podstawowego składnika Direct3D 12.
- przepływ kodu dla prostej aplikacji
- przykład kodu dla prostej aplikacji
- Tematy pokrewne
Przepływ kodu dla prostej aplikacji
Najbardziej zewnętrzna pętla programu D3D 12 jest zgodna z bardzo standardowym procesem graficznym:
Napiwek
Funkcje nowe w Direct3D 12 są oznaczone adnotacją.
- zainicjować
- powtórz
- Zniszcz
Zainicjuj
Inicjowanie obejmuje najpierw skonfigurowanie zmiennych globalnych i klas, a funkcja inicjowania musi przygotować potok i zasoby.
- Zainicjuj pipeline.
Włącz warstwę debugowania.
Utwórz urządzenie.
Utwórz kolejkę poleceń.
Utwórz łańcuch wymiany.
Utwórz stertę deskryptorów widoku docelowego renderowania (RTV).
Utwórz zasoby ramek (widok docelowy renderowania dla każdej ramki).
Utwórz alokator poleceń.
Notatka
Alokator poleceń zarządza pamięcią podstawową dla list poleceń i pakietów.
- Zainicjuj zasoby.
Utwórz pusty podpis główny.
Notatka
Sygnatura główna grafiki definiuje, jakie zasoby są powiązane z potokiem graficznym.
Skompiluj shadery.
Utwórz wierzchołkowy układ wejściowy.
Utwórz obiekt stanu potoku opis, a następnie utwórz obiekt.
Notatka
Obiekt stanu potoku utrzymuje stan wszystkich aktualnie ustawionych shaderów, a także niektórych obiektów stanu ustalonych funkcji (takich jak asembler wejściowy, tesselator, rasteryzator i scalacz wyjściowy).
Utwórz listę poleceń.
Zamknij listę poleceń.
Utwórz i załaduj bufory wierzchołków.
Utwórz widoki buforów wierzchołków.
Utwórz ogrodzenie.
Notatka
Ogrodzenie służy do synchronizacji CPU z GPU (zobacz Synchronizacja wielu silników).
Utwórz uchwyt zdarzenia.
Poczekaj na zakończenie działania GPU.
Notatka
Sprawdź ogrodzenie!
Zapoznaj się z klasy D3D12HelloTriangle, OnInit, LoadPipeline i LoadAssets.
Aktualizacja
Zaktualizuj wszystko, co powinno się zmienić od ostatniego kadru.
- Zmodyfikuj bufory stałych, wierzchołków, indeksów i wszystkie inne elementy w razie potrzeby.
Zapoznaj się z artykułem OnUpdate.
Renderować
Rysuj nowy świat.
- Uzupełnij listę poleceń.
Zresetuj alokator listy poleceń.
Notatka
Ponownie użyj pamięci skojarzonej z alokatorem poleceń.
Zresetuj listę poleceń.
Ustaw podstawowy podpis graficzny.
Notatka
Ustawia podpis główny grafiki do użycia dla bieżącej listy poleceń.
Ustaw prostokąty widoku i nożyczki.
Ustaw barierę zasobów wskazującą, że bufor wsteczny ma być używany jako element docelowy renderowania.
Notatka
Bariery zasobów służą do zarządzania przejściami zasobów.
Zapisz polecenia na liście poleceń.
Wskaż, że bufor zwrotny zostanie użyty do wyświetlenia po zakończeniu wykonywania listy poleceń.
Notatka
Ponowne wywołanie funkcji ustawiania bariery zasobów.
Zamknij listę poleceń, aby kontynuować nagrywanie.
- Wykonaj listę poleceń.
- Zaprezentuj ramkę.
- Poczekaj, aż GPU zakończy działanie.
Notatka
Aktualizuj i sprawdzaj ogrodzenie.
Zapoznaj się z OnRender.
Niszczyć
Porządnie zamknij aplikację.
Poczekaj, aż GPU zakończy.
Notatka
Ostateczna kontrola na ogrodzeniu.
Zamknij uchwyt zdarzenia.
Zapoznaj się z OnDestroy.
Przykład kodu dla prostej aplikacji
Poniższy kod rozszerza powyższy przepływ, aby uwzględnić kod wymagany dla przykładu podstawowego.
klasa D3D12HelloTriangle
Najpierw zdefiniuj klasę w pliku nagłówka, który zawiera viewport, prostokąt nożycowy i bufor wierzchołków, korzystając ze struktur:
#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()
W głównym pliku źródłowym projektów rozpocznij inicjowanie obiektów:
void D3D12HelloTriangle::OnInit()
{
LoadPipeline();
LoadAssets();
}
LoadPipeline()
Poniższy kod tworzy podstawy potoku graficznego. Proces tworzenia urządzeń i łańcuchów wymiany jest bardzo podobny do direct3D 11.
- Włącz warstwę debugowania z wywołaniami do:
D3D12GetDebugInterface
ID3D12Debug::EnableDebugLayer
- Utwórz urządzenie:
CreateDXGIFactory1
D3D12CreateDevice
- Wypełnij opis kolejki poleceń, a następnie utwórz kolejkę poleceń:
D3D12_COMMAND_QUEUE_DESC
ID3D12Device::CreateCommandQueue
- Wypełnij opis swapchain, a następnie utwórz łańcuch wymiany:
DXGI_SWAP_CHAIN_DESC
IDXGIFactory::CreateSwapChain
IDXGISwapChain3::GetCurrentBackBufferIndex
- Wypełnij opis stosu. następnie utwórz stertę deskryptora:
D3D12_DESCRIPTOR_HEAP_DESC
ID3D12Device::CreateDescriptorHeap
ID3D12Device::GetDescriptorHandleIncrementSize
- Utwórz widok docelowy renderowania:
CD3DX12_CPU_DESCRIPTOR_HANDLE
GetCPUDescriptorHandleForHeapStart
IDXGISwapChain::GetBuffer
ID3D12Device::CreateRenderTargetView
- Utwórz alokator poleceń: ID3D12Device::CreateCommandAllocator.
W kolejnych krokach listy poleceń zostają uzyskane z alokatora poleceń i przesłane do kolejki komend.
Załaduj zależności potoku renderowania (pamiętaj, że tworzenie urządzenia WARP opartego na oprogramowaniu jest całkowicie opcjonalne).
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()
Ładowanie i przygotowywanie zasobów jest długotrwałym procesem. Wiele z tych etapów jest podobnych do D3D 11, choć niektóre są nowe dla D3D 12.
W Direct3D 12 stan potoku jest dołączany do listy poleceń za pośrednictwem obiektu stanu potoku (PSO). W tym przykładzie pokazano, jak utworzyć PSO. PsO można przechowywać jako zmienną składową i używać jej wielokrotnie w razie potrzeby.
Sterta deskryptorów definiuje widoki oraz sposób dostępu do zasobów (na przykład widok docelowy renderowania).
Za pomocą alokatora poleceń i funkcji PSO można utworzyć rzeczywistą listę poleceń, która zostanie wykonana w późniejszym czasie.
Następujące interfejsy API i procesy są wywoływane kolejno.
- Utwórz pusty podpis główny przy użyciu dostępnej struktury pomocnika:
CD3DX12_ROOT_SIGNATURE_DESC
D3D12SerializeRootSignature
ID3D12Device::CreateRootSignature
- Załaduj i skompiluj moduły cieniowania: D3DCompileFromFile.
- Utwórz układ wprowadzania wierzchołków: D3D12_INPUT_ELEMENT_DESC.
- Wypełnij opis stanu potoku graficznego, korzystając z dostępnych struktur pomocniczych, a następnie utwórz stan potoku graficznego.
D3D12_GRAPHICS_PIPELINE_STATE_DESC
CD3DX12_RASTERIZER_DESC
CD3DX12_BLEND_DESC
ID3D12Device::CreateGraphicsPipelineState
- Utwórz, a następnie zamknij listę poleceń:
ID3D12Device::CreateCommandList
ID3D12GraphicsCommandList::Close
- Utwórz bufor wierzchołka: ID3D12Device::CreateCommittedResource.
- Skopiuj dane wierzchołka do buforu wierzchołka:
ID3D12Resource::Map
ID3D12Resource::Unmap
- Zainicjuj widok buforu wierzchołka: GetGPUVirtualAddress.
- Utwórz i zainicjuj ogrodzenie: ID3D12Device::CreateFence.
- Utwórz uchwyt zdarzenia do użycia przy synchronizacji ramek.
- Poczekaj, aż GPU zakończy.
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()
W prostym przykładzie nic nie jest aktualizowane.
void D3D12HelloTriangle::OnUpdate()
{
}
OnRender()
Podczas konfigurowania zmienna składowa m_commandList była używana do rejestrowania i wykonywania wszystkich poleceń konfiguracji. Teraz możesz ponownie użyć tego członka w głównej pętli renderowania.
Renderowanie obejmuje wywołanie w celu wypełnienia listy poleceń, a następnie można wykonać tę listę i przedstawić następny bufor w łańcuchu wymiany.
- Uzupełnij listę poleceń.
- Wykonaj listę poleceń: ID3D12CommandQueue::ExecuteCommandLists.
- IDXGISwapChain1::Present1 ramkę.
- Poczekaj, aż GPU zakończy pracę.
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();
}
WypełnijListęPoleceń()
Należy zresetować alokator listy poleceń i listę poleceń przed ich ponownym użyciem. W bardziej zaawansowanych scenariuszach warto zresetować alokator co kilka ramek. Pamięć jest związana z alokatorem, którego nie można zwolnić od razu po wykonaniu listy poleceń. W tym przykładzie pokazano, jak zresetować alokator po każdej ramce.
Teraz użyj ponownie listy poleceń dla bieżącej ramki. Ponownie dołącz viewport do listy poleceń (co musi być zrobione za każdym razem, gdy lista poleceń zostanie zresetowana i przed jej wykonaniem), wskaż, że zasób będzie używany jako cel renderowania, zarejestruj polecenia, a następnie wskaż, że cel renderowania będzie używany do prezentacji, gdy lista poleceń zostanie wykonana.
Wypełnianie listy poleceń wywołuje następujące metody i procesy z kolei:
- Zresetuj alokator poleceń i listę poleceń:
ID3D12CommandAllocator::Reset
ID3D12GraphicsCommandList::Reset
- Ustaw prostokąty sygnatury głównej, widoku i nożyczki:
ID3D12GraphicsCommandList::SetGraphicsRootSignature
ID3D12GraphicsCommandList::RSSetViewports
ID3D12GraphicsCommandList::RSSetScissorRects
- Wskaż, że bufor wsteczny ma być używany jako element docelowy renderowania:
ID3D12GraphicsCommandList::ResourceBarrier
ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart
ID3D12GraphicsCommandList::OMSetRenderTargets
- Zarejestruj polecenia:
ID3D12GraphicsCommandList::ClearRenderTargetView
ID3D12GraphicsCommandList::IASetPrimitiveTopology
ID3D12GraphicsCommandList::IASetVertexBuffers
ID3D12GraphicsCommandList::DrawInstanced
- Wskaż bufor wsteczny, który będzie teraz używany do prezentowania: ID3D12GraphicsCommandList::ResourceBarrier.
- Zamknij listę poleceń: 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()
Poniższy kod przedstawia nadmiernie uproszczone użycie ogrodzeń.
Notatka
Oczekiwanie na ukończenie klatki jest zbyt nieefektywne dla większości aplikacji.
Następujące interfejsy API i procesy są wywoływane w następującej kolejności:
- ID3D12CommandQueue::Signal
- ID3D12Fence::GetCompletedValue
- ID3D12Fence::SetEventOnCompletion
- Poczekaj na zdarzenie.
- Zaktualizuj indeks ramki: 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()
Porządnie zamknij aplikację.
- Poczekaj na zakończenie działania GPU.
- Zamknij zdarzenie.
void D3D12HelloTriangle::OnDestroy()
{
// Wait for the GPU to be done with all resources.
WaitForPreviousFrame();
CloseHandle(m_fenceEvent);
}
Tematy pokrewne