Erstellen einer einfachen Direct3D 12-Komponente
Hinweis
Sehen Sie sich das DirectX-Graphics-Samples-Repository für DirectX 12 Graphics-Beispiele an, die veranschaulichen, wie Sie grafikintensive Anwendungen für Windows 10 erstellen.
In diesem Thema wird der Aufruffluss zum Erstellen einer grundlegenden Direct3D 12-Komponente beschrieben.
Codeflow für eine einfache App
Die äußerste Schleife eines D3D 12-Programms folgt einem sehr standardmäßigen Grafikprozess:
Tipp
Features, die in Direct3D 12 neu sind, werden von einer Notiz gefolgt.
- Initialisieren
- Wiederholen
- Zerstören
Initialize
Die Initialisierung umfasst zunächst das Einrichten der globalen Variablen und Klassen, und eine Initialisierungsfunktion muss die Pipeline und die Ressourcen vorbereiten.
- Initialisieren Sie die Pipeline.
Aktivieren Sie die Debugebene.
Erstellen Sie das Gerät.
Erstellen Sie die Befehlswarteschlange.
Erstellen Sie die Swapchain.
Erstellen Sie einen RTV-Deskriptor-Heap.
Hinweis
Ein Deskriptorheap kann als Array von Deskriptoren betrachtet werden. Wobei jeder Deskriptor ein Objekt für die GPU vollständig beschreibt.
Erstellen sie Frameressourcen (eine Renderzielansicht für jeden Frame).
Erstellen Sie einen Befehlszuordnungsator.
Hinweis
Eine Befehlszuweisung verwaltet den zugrunde liegenden Speicher für Befehlslisten und -bundles.
- Initialisieren Sie die Ressourcen.
Erstellen Sie eine leere Stammsignatur.
Hinweis
Eine Grafikstammsignatur definiert, welche Ressourcen an die Grafikpipeline gebunden sind.
Kompilieren Sie die Shader.
Erstellen Sie das Vertex-Eingabelayout.
Erstellen Sie eine Beschreibung des Pipelinezustandsobjekts , und erstellen Sie dann das Objekt.
Hinweis
Ein Pipelinezustandsobjekt verwaltet den Zustand aller aktuell festgelegten Shader sowie bestimmter fester Funktionszustandsobjekte (z. B . Eingabeassemler, Tesselator, Rasterizer und Ausgabezusammenführung).
Erstellen Sie die Befehlsliste.
Schließen Sie die Befehlsliste.
Erstellen und laden Sie die Vertexpuffer.
Erstellen Sie die Vertexpuffersichten.
Erstellen Sie einen Zaun.
Hinweis
Ein Zaun wird verwendet, um die CPU mit der GPU zu synchronisieren (siehe Synchronisierung mit mehreren Engines).
Erstellen Sie ein Ereignishandle.
Warten Sie, bis die GPU abgeschlossen ist.
Hinweis
Überprüfen Sie den Zaun!
Siehe Klasse D3D12HelloTriangle, OnInit, LoadPipeline und LoadAssets.
Aktualisieren
Aktualisieren Sie alles, was sich seit dem letzten Frame ändern sollte.
- Ändern Sie die Konstante, den Scheitelpunkt, die Indexpuffer und alles andere nach Bedarf.
Weitere Informationen finden Sie unter OnUpdate.
Rendern
Zeichnen Sie die neue Welt.
- Füllen Sie die Befehlsliste auf.
Setzen Sie den Befehlslistenzuteilungsbefehl zurück.
Hinweis
Verwenden Sie den Arbeitsspeicher, der dem Befehlszuteilungsbefehl zugeordnet ist.
Setzen Sie die Befehlsliste zurück.
Legen Sie die Grafikstammsignatur fest.
Hinweis
Legt die Grafikstammsignatur fest, die für die aktuelle Befehlsliste verwendet werden soll.
Legen Sie den Viewport und das Scherenrechteck fest.
Legen Sie eine Ressourcenbarriere fest, die angibt, dass der Backpuffer als Renderziel verwendet werden soll.
Hinweis
Ressourcenbarrieren werden zum Verwalten von Ressourcenübergängen verwendet.
Notieren Sie Befehle in der Befehlsliste.
Geben Sie an, dass der Backpuffer verwendet wird, um anzuzeigen, nachdem die Befehlsliste ausgeführt wurde.
Hinweis
Ein weiterer Aufruf zum Festlegen einer Ressourcenbarriere.
Schließen Sie die Befehlsliste für die weitere Aufzeichnung.
- Führen Sie die Befehlsliste aus.
- Zeigen Sie den Frame an.
- Warten Sie, bis die GPU abgeschlossen ist.
Hinweis
Aktualisieren Und überprüfen Sie den Zaun.
Weitere Informationen finden Sie unter OnRender.
Zerstören
Schließen Sie die App sauber.
Warten Sie, bis die GPU abgeschlossen ist.
Hinweis
Abschließende Überprüfung des Zauns.
Schließen Sie das Ereignishandle.
Weitere Informationen finden Sie unter OnDestroy.
Codebeispiel für eine einfache App
Im Folgenden wird der obige Codeflow erweitert, um den code einzuschließen, der für ein einfaches Beispiel erforderlich ist.
Klasse D3D12HelloTriangle
Definieren Sie zunächst die -Klasse in einer Headerdatei, einschließlich eines Viewports, eines Scherenrechtecks und eines Vertexpuffers, indem Sie die -Strukturen verwenden:
#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()
Beginnen Sie in den Projekten Standard Quelldatei mit der Initialisierung der Objekte:
void D3D12HelloTriangle::OnInit()
{
LoadPipeline();
LoadAssets();
}
LoadPipeline()
Der folgende Code erstellt die Grundlagen für eine Grafikpipeline. Der Prozess zum Erstellen von Geräten und Swapchains ist Direct3D 11 sehr ähnlich.
- Aktivieren Sie die Debugebene mit Aufrufen von:
D3D12GetDebugInterface
ID3D12Debug::EnableDebugLayer
- Erstellen Sie das Gerät:
CreateDXGIFactory1
D3D12CreateDevice
- Füllen Sie eine Befehlswarteschlangenbeschreibung aus, und erstellen Sie dann die Befehlswarteschlange:
D3D12_COMMAND_QUEUE_DESC
ID3D12Device::CreateCommandQueue
- Füllen Sie eine Swapchainbeschreibung aus, und erstellen Sie dann die Swapchain:
DXGI_SWAP_CHAIN_DESC
IDXGIFactory::CreateSwapChain
IDXGISwapChain3::GetCurrentBackBufferIndex
- Füllen Sie eine Heapbeschreibung aus. erstellen Sie dann einen Deskriptorheap:
D3D12_DESCRIPTOR_HEAP_DESC
ID3D12Device::CreateDescriptorHeap
ID3D12Device::GetDescriptorHandleIncrementSize
- Erstellen Sie die Renderzielansicht:
CD3DX12_CPU_DESCRIPTOR_HANDLE
GetCPUDescriptorHandleForHeapStart
IDXGISwapChain::GetBuffer
ID3D12Device::CreateRenderTargetView
- Erstellen Sie die Befehlszuweisung: ID3D12Device::CreateCommandAllocator.
In späteren Schritten werden Befehlslisten aus der Befehlszuweisung abgerufen und an die Befehlswarteschlange übermittelt.
Laden Sie die Renderingpipelineabhängigkeiten (beachten Sie, dass die Erstellung eines Software-WARP-Geräts völlig optional ist).
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()
Das Laden und Vorbereiten von Ressourcen ist ein langwieriger Prozess. Viele dieser Phasen ähneln D3D 11, einige sind jedoch neu in D3D 12.
In Direct3D 12 wird der erforderliche Pipelinestatus über ein Pipelinezustandsobjekt (PSO ) an eine Befehlsliste angefügt. In diesem Beispiel wird gezeigt, wie Ein PSO erstellt wird. Sie können den PSO als Membervariable speichern und so oft wie nötig wiederverwenden.
Ein Deskriptorheap definiert die Ansichten und den Zugriff auf Ressourcen (z. B. eine Renderzielansicht).
Mit der Befehlslistenzuweisung und PSO können Sie die tatsächliche Befehlsliste erstellen, die zu einem späteren Zeitpunkt ausgeführt wird.
Die folgenden APIs und Prozesse werden nacheinander aufgerufen.
- Erstellen Sie mithilfe der verfügbaren Hilfsstruktur eine leere Stammsignatur:
CD3DX12_ROOT_SIGNATURE_DESC
D3D12SerializeRootSignature
ID3D12Device::CreateRootSignature
- Laden und kompilieren Sie die Shader : D3DCompileFromFile.
- Erstellen Sie das Vertexeingabelayout: D3D12_INPUT_ELEMENT_DESC.
- Füllen Sie eine Beschreibung des Pipelinezustands mithilfe der verfügbaren Hilfsstrukturen aus, und erstellen Sie dann den Zustand der Grafikpipeline:
D3D12_GRAPHICS_PIPELINE_STATE_DESC
CD3DX12_RASTERIZER_DESC
CD3DX12_BLEND_DESC
ID3D12Device::CreateGraphicsPipelineState
- Erstellen Und schließen Sie dann eine Befehlsliste:
ID3D12Device::CreateCommandList
ID3D12GraphicsCommandList::Close
- Erstellen Sie den Vertexpuffer: ID3D12Device::CreateCommittedResource.
- Kopieren Sie die Vertexdaten in den Vertexpuffer:
ID3D12Resource::Map
ID3D12Resource::Unmap
- Initialisieren Sie die Vertexpufferansicht: GetGPUVirtualAddress.
- Erstellen und initialisieren Sie den Zaun: ID3D12Device::CreateFence.
- Erstellen Sie ein Ereignishandle für die Framesynchronisierung.
- Warten Sie, bis die GPU abgeschlossen ist.
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()
Für ein einfaches Beispiel wird nichts aktualisiert.
void D3D12HelloTriangle::OnUpdate()
{
}
OnRender()
Während der Einrichtung wurde die Membervariable m_commandList verwendet, um alle Setupbefehle aufzuzeichnen und auszuführen. Sie können diesen Member jetzt in der Standard Renderschleife wiederverwenden.
Das Rendern umfasst einen Aufruf zum Auffüllen der Befehlsliste. Anschließend kann die Befehlsliste ausgeführt werden, und der nächste Puffer in der Swapchain wird angezeigt:
- Füllen Sie die Befehlsliste auf.
- Führen Sie die Befehlsliste aus: ID3D12CommandQueue::ExecuteCommandLists.
- IDXGISwapChain1::P resent1 den Frame.
- Warten Sie, bis die GPU fertig ist.
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()
Sie müssen die Befehlslistenzuweisung und die Befehlsliste selbst zurücksetzen, bevor Sie sie wiederverwenden können. In komplexeren Szenarien kann es sinnvoll sein, die Zuweisung alle mehrere Frames zurückzusetzen. Arbeitsspeicher ist dem Zuweisungssteuerelement zugeordnet, der nicht sofort nach dem Ausführen einer Befehlsliste freigegeben werden kann. In diesem Beispiel wird gezeigt, wie die Zuweisung nach jedem Frame zurückgesetzt wird.
Verwenden Sie nun die Befehlsliste für den aktuellen Frame wieder. Fügen Sie den Viewport erneut an die Befehlsliste an (dies muss immer erfolgen, wenn eine Befehlsliste zurückgesetzt wird und bevor die Befehlsliste ausgeführt wird). Geben Sie an, dass die Ressource als Renderziel verwendet wird, zeichnen Sie Befehle auf, und geben Sie dann an, dass das Renderziel verwendet wird, um anzuzeigen, wenn die Ausführung der Befehlsliste abgeschlossen ist.
Beim Auffüllen von Befehlslisten werden wiederum die folgenden Methoden und Prozesse aufgerufen:
- Setzen Sie die Befehlszuweisung und die Befehlsliste zurück:
ID3D12CommandAllocator::Reset
ID3D12GraphicsCommandList::Reset
- Legen Sie die Rechte der Stammsignatur, des Viewports und der Schere fest:
ID3D12GraphicsCommandList::SetGraphicsRootSignature
ID3D12GraphicsCommandList::RSSetViewports
ID3D12GraphicsCommandList::RSSetScissorRects
- Geben Sie an, dass der Hintergrundpuffer als Renderziel verwendet werden soll:
ID3D12GraphicsCommandList::ResourceBarrier
ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart
ID3D12GraphicsCommandList::OMSetRenderTargets
- Notieren Sie sich die Befehle:
ID3D12GraphicsCommandList::ClearRenderTargetView
ID3D12GraphicsCommandList::IASetPrimitiveTopology
ID3D12GraphicsCommandList::IASetVertexBuffers
ID3D12GraphicsCommandList::D rawInstanced
- Geben Sie an, dass der Hintergrundpuffer jetzt für die Präsentation verwendet wird: ID3D12GraphicsCommandList::ResourceBarrier.
- Schließen Sie die Befehlsliste: 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()
Der folgende Code zeigt eine zu vereinfachte Verwendung von Zäunen.
Hinweis
Das Warten auf den Abschluss eines Frames ist für die meisten Apps zu ineffizient.
Die folgenden APIs und Prozesse werden in der reihenfolge aufgerufen:
- ID3D12CommandQueue::Signal
- ID3D12Fence::GetCompletedValue
- ID3D12Fence::SetEventOnCompletion
- Warten Sie auf das Ereignis.
- Aktualisieren Sie den Frameindex: 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()
Schließen Sie die App sauber.
- Warten Sie, bis die GPU abgeschlossen ist.
- Schließen Sie das Ereignis.
void D3D12HelloTriangle::OnDestroy()
{
// Wait for the GPU to be done with all resources.
WaitForPreviousFrame();
CloseHandle(m_fenceEvent);
}
Zugehörige Themen