Erstellen und Aufzeichnen von Befehlslisten und Bündeln
In diesem Thema werden die Listen und Bundles der Aufzeichnungsbefehle in Direct3D 12-Apps beschrieben. Befehlslisten und -Bundles ermöglichen Apps das Aufzeichnen von Zeichnungs- oder Zustandsänderungsaufrufen für die spätere Ausführung der Grafikverarbeitungseinheit (GPU).
Über Befehlslisten hinaus nutzt die API Funktionen, die in GPU-Hardware vorhanden sind, indem eine zweite Ebene von Befehlslisten hinzugefügt wird, die als Bundlesbezeichnet werden. Der Zweck von Bündeln besteht darin, Apps das Gruppieren einer kleinen Anzahl von API-Befehlen zur späteren Ausführung zu ermöglichen. Bei der Erstellung des Bündels führt der Treiber so viel Vorverarbeitung wie möglich aus, um diese später kostengünstig auszuführen. Bündel sind so konzipiert, dass sie beliebig oft verwendet und wiederverwendet werden. Befehlslisten werden dagegen in der Regel nur einmal ausgeführt. Eine Befehlsliste kann jedoch mehrmals ausgeführt werden (solange die Anwendung sicherstellt, dass die vorherigen Ausführungen abgeschlossen wurden, bevor neue Ausführungen übermittelt werden).
In der Regel werden API-Aufrufe in Paketen, API-Aufrufe und Pakete in Befehlslisten und Befehlslisten in einem einzigen Frame zusammengefasst, wie im folgenden Diagramm dargestellt, wobei die Wiederverwendung von Bundle 1 in den Befehlsliste 1 und Befehlsliste 2 zu beachten ist. Auch sind die API-Methodennamen im Diagramm beispielhaft, es können viele verschiedene API-Aufrufe verwendet werden.
Es gibt unterschiedliche Einschränkungen für das Erstellen und Ausführen von Bündeln und direkten Befehlslisten, und diese Unterschiede werden in diesem Thema behandelt.
Erstellen von Befehlslisten
Direkte Befehlslisten und Pakete werden erstellt, indem ID3D12Device::CreateCommandList oder ID3D12Device4::CreateCommandList1aufgerufen wird.
Verwenden Sie ID3D12Device4::CreateCommandList1, um eine geschlossene Befehlsliste zu erstellen, anstatt eine neue Liste zu erstellen und sie sofort zu schließen. Dadurch wird vermieden, dass eine Liste mit einer Zuweisung und einem PSO erstellt wird, diese aber nicht verwendet werden.
ID3D12Device::CreateCommandList verwendet die folgenden Parameter als Eingabe:
D3D12_COMMAND_LIST_TYPE
Die D3D12_COMMAND_LIST_TYPE-Aufzählung gibt den Typ der Befehlsliste an, die erstellt wird. Dabei kann es sich um eine direkte Befehlsliste, ein Bündel, eine Computebefehlsliste oder eine Kopierbefehlsliste sein.
ID3D12CommandAllocator
Ein Befehls-Allocator ermöglicht der App das Verwalten des Speichers, der für Befehlslisten zugewiesen ist. Die Befehlszuweisung wird beim Aufrufen von CreateCommandAllocator erstellt. Beim Erstellen einer Befehlsliste muss der Befehlslistentyp der durch D3D12_COMMAND_LIST_TYPE angegebenen Zuweisung mit dem Typ der zu erstellenden Befehlsliste übereinstimmen. Eine bestimmte Zuweisung kann jeweils nur einer aktuell aufgezeichneten Befehlsliste zugeordnet werden. Eine Befehlszuweisung kann jedoch verwendet werden, um eine beliebige Anzahl von GraphicsCommandList-Objekten zu erstellen.
Um den durch einen Befehlsverteiler zugewiesenen Speicher zurückzugeben, ruft eine App ID3D12CommandAllocator::Resetauf. Dadurch kann die Zuweisung für neue Befehle wiederverwendet werden, verringert jedoch nicht die zugrunde liegende Größe. Bevor dies geschieht, muss die App jedoch sicherstellen, dass die GPU keine Befehlslisten mehr ausführt, die dem Allocator zugeordnet sind; andernfalls schlägt der Aufruf fehl. Beachten Sie außerdem, dass diese API nicht threadsicher ist und daher nicht gleichzeitig von mehreren Threads für dieselbe Zuweisung aufgerufen werden kann.
ID3D12PipelineState
Der anfängliche Pipelinestatus für die Befehlsliste. In Microsoft Direct3D 12 wird der meiste Grafik-Pipeline-Status in einer Befehlsliste mithilfe des ID3D12PipelineState--Objekts eingestellt. Eine App erstellt eine große Anzahl dieser Objekte, in der Regel während der App-Initialisierung, und dann wird der Zustand aktualisiert, indem das aktuell gebundene Zustandsobjekt mithilfe ID3D12GraphicsCommandList::SetPipelineStategeändert wird. Weitere Informationen zu Pipelinestatusobjekten finden Sie unter Verwalten des Grafikpipelinezustands in Direct3D 12.
Beachten Sie, dass Bundles nicht den Pipelinestatus erben, der durch vorherige Aufrufe in direkten, ihnen übergeordneten Befehlslisten, festgelegt wurde.
Wenn dieser Parameter NULL ist, wird ein Standardstatus verwendet.
Aufzeichnen von Befehlslisten
Unmittelbar nach der Erstellung befinden sich Befehlslisten im Aufzeichnungszustand. Sie können auch eine vorhandene Befehlsliste erneut verwenden, indem Sie ID3D12GraphicsCommandList::Resetaufrufen, wodurch auch die Befehlsliste im Aufzeichnungszustand verbleibt. Im Gegensatz zu ID3D12CommandAllocator::Resetkönnen Sie Reset aufrufen, während die Befehlsliste noch ausgeführt wird. Ein typisches Muster besteht darin, eine Befehlsliste zu übermitteln und dann sofort zurückzusetzen, um den zugewiesenen Speicher für eine andere Befehlsliste wiederzuverwenden. Beachten Sie, dass sich jeweils nur eine Befehlsliste, die jedem Befehlszuweisungstyp zugeordnet ist, gleichzeitig in einem Aufzeichnungszustand befinden kann.
Sobald sich eine Befehlsliste im Aufzeichnungszustand befindet, rufen Sie einfach Methoden der ID3D12GraphicsCommandList Schnittstelle auf, um der Liste Befehle hinzuzufügen. Viele dieser Methoden ermöglichen allgemeine Direct3D-Funktionen, die Microsoft Direct3D 11-Entwicklern vertraut sind; Andere APIs sind neu für Direct3D 12.
Nach dem Hinzufügen von Befehlen zur Befehlsliste übergehen Sie die Befehlsliste aus dem Aufzeichnungszustand, indem Sie Closeaufrufen.
Befehlszuweisungen können wachsen, aber nicht schrumpfen – das Poolen und Wiederverwenden von Zuweisungen sollte in Betracht gezogen werden, um die Effizienz Ihrer App zu maximieren. Sie können mehrere Listen mit derselben Zuweisung aufzeichnen, bevor sie zurückgesetzt wird, vorausgesetzt, nur eine Liste wird gleichzeitig mit einer bestimmten Zuweisung aufgezeichnet. Sie können sich jede Liste als Teil des Allocators vorstellen, der angibt, was ID3D12CommandQueue::ExecuteCommandLists ausführen wird.
Eine einfache Zuweisungspoolstrategie sollte auf ungefähr numCommandLists * MaxFrameLatency
Zuweisungen abzielen. Wenn Sie beispielsweise 6 Listen aufzeichnen und bis zu 3 latente Frames zulassen, könnten Sie vernünftigerweise 18-20 Zuweisungen erwarten. Eine fortgeschrittenere Poolingstrategie, die Allokatoren für mehrere Listen im selben Thread wiederverwendet, könnte auf numRecordingThreads * MaxFrameLatency
Allokatoren abzielen. Wenn im vorherigen Beispiel 2 Listen in Thread A, 2 für Thread B, 1 für Thread C und 1 für Thread D aufgezeichnet wurden, könnten Sie realistischerweise auf 12-14 Allokatoren abzielen.
Verwenden Sie einen Zeitraum, um zu bestimmen, wann eine bestimmte Zuweisung wiederverwendet werden kann.
Da Befehlslisten nach der Ausführung sofort zurückgesetzt werden können, können sie leicht in einem Pool gesammelt und nach jedem Aufruf von ID3D12CommandQueue::ExecuteCommandListsdem Pool wieder hinzugefügt werden.
Beispiel
Die folgenden Codeausschnitte veranschaulichen die Erstellung und Aufzeichnung einer Befehlsliste. Beachten Sie, dass dieses Beispiel die folgenden Direct3D 12-Features enthält:
- Pipelinestatusobjekte – Diese werden verwendet, um die meisten Statusparameter der Renderpipeline aus einer Befehlsliste festzulegen. Weitere Informationen finden Sie unter Verwalten des Grafikpipelinestatus in Direct3D 12.
- Deskriptor-Heaps – Apps verwenden Deskriptor-Heaps zur Verwaltung der Anbindung der Pipeline an Speicherressourcen.
- Ressourcenbarriere – Dies wird verwendet, um den Übergang von Ressourcen von einem Zustand in einen anderen zu verwalten, z. B. aus einer Renderzielansicht zu einer Shaderressourcenansicht. Weitere Informationen finden Sie unter Verwenden von Ressourcenbarrieren zum Synchronisieren von Ressourcenzuständen.
Zum Beispiel
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.
ThrowIfFailed(m_device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize),
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();
}
}
Nachdem eine Befehlsliste erstellt und aufgezeichnet wurde, kann sie mithilfe einer Befehlswarteschlange ausgeführt werden. Weitere Informationen finden Sie unter Ausführen und Synchronisieren von Befehlslisten.
Verweisanzahl
Die meisten D3D12-APIs verwenden weiterhin Referenzzählungen nach COM-Konventionen. Eine bemerkenswerte Ausnahme hierfür sind die D3D12-Grafikbefehlslisten-APIs. Alle APIs für ID3D12GraphicsCommandList halten keine Verweise auf die Objekte, die an diese APIs übergeben werden. Dies bedeutet, dass Anwendungen dafür verantwortlich sind, sicherzustellen, dass eine Befehlsliste nie zur Ausführung übermittelt wird, die auf eine zerstörte Ressource verweist.
Befehlslistenfehler
Die meisten APIs für ID3D12GraphicsCommandList geben keine Fehler zurück. Fehler beim Erstellen von Befehlslisten werden bis ID3D12GraphicsCommandList::Close zurückgestellt. Die einzige Ausnahme ist DXGI_ERROR_DEVICE_REMOVED, die noch weiter zurückgestellt wird. Beachten Sie, dass sich dies von D3D11 unterscheidet, bei dem viele Parameterüberprüfungsfehler im Hintergrund gelöscht und nie an den Aufrufer zurückgegeben werden.
Anwendungen können erwarten, dass DXGI_DEVICE_REMOVED Fehler in den folgenden API-Aufrufen angezeigt werden:
- Jede Methode für die Ressourcenerstellung
- ID3D12Resource::Map
- IDXGISwapChain1::Present1
- GetDeviceRemovedReason
API-Einschränkungen für Befehlslisten
Einige Befehlslisten-APIs können nur für bestimmte Befehlslistentypen aufgerufen werden. Die folgende Tabelle zeigt, welche Befehlslisten-APIs für jeden Befehlslistentyp aufgerufen werden können. Außerdem wird gezeigt, welche APIs für den Aufruf in einem D3D12-Renderpass gültig sind.
API-Name | Graphik | Berechnen | Kopieren | Paket | Im Renderpass |
---|---|---|---|---|---|
AtomicCopyBufferUINT | • Gültig | • Gültig | • Gültig | ||
AtomicCopyBufferUINT64 | • Gültig | • Gültig | • Gültig | ||
BeginQuery | • Gültig | • Gültig | |||
BeginRenderPass | • Gültig | ||||
BuildRaytracingAccelerationStructure | • Gültig | • Gültig | |||
ClearDepthStencilView | • Gültig | ||||
ClearRenderTargetView | • Gültig | ||||
ClearState | • Gültig | • Gültig | |||
ClearUnorderedAccessViewFloat | • Gültig | • Gültig | |||
ClearUnorderedAccessViewUint | • Gültig | • Gültig | |||
CopyBufferRegion | • Gültig | • Gültig | • Gültig | ||
CopyRaytracingAccelerationStructure | • Gültig | • Gültig | |||
CopyResource | • Gültig | • Gültig | • Gültig | ||
CopyTextureRegion | • Gültig | • Gültig | • Gültig | ||
CopyTiles | • Gültig | • Gültig | • Gültig | ||
DiscardResource | • Gültig | • Gültig | |||
Versand | • Gültig | • Gültig | • Gültig | ||
DispatchRays | • Gültig | • Gültig | • Gültig | ||
DrawIndexedInstanced | • Gültig | • Gültig | • Gültig | ||
DrawInstanced | • Gültig | • Gültig | • Gültig | ||
EmitRaytracingAccelerationStructurePostbuildInfo | • Gültig | • Gültig | |||
Abfrage beenden | • Gültig | • Gültig | • Gültig | • Gültig | |
EndRenderPass | • Gültig | • Gültig | |||
ExecuteBundle | • Gültig | • Gültig | |||
ExecuteIndirect | • Gültig | • Gültig | • Gültig | • Gültig | |
ExecuteMetaCommand | • Gültig | • Gültig | |||
IASetIndexBuffer | • Gültig | • Gültig | • Gültig | ||
IASetPrimitiveTopology | • Gültig | • Gültig | • Gültig | ||
IASetVertexBuffers | • Gültig | • Gültig | • Gültig | ||
Meta-Befehl initialisieren | • Gültig | • Gültig | |||
OMSetBlendFactor | • Gültig | • Gültig | • Gültig | ||
OMSetDepthBounds | • Gültig | • Gültig | • Gültig | ||
OMSetRenderTargets | • Gültig | ||||
OMSetStencilRef | • Gültig | • Gültig | • Gültig | ||
ResolveQueryData | • Gültig | • Gültig | • Gültig | ||
ResolveSubresource | • Gültig | ||||
ResolveSubresourceRegion | • Gültig | ||||
ResourceBarrier | • Gültig | • Gültig | • Gültig | • Gültig | |
RSSetScissorRects | • Gültig | • Gültig | |||
RSSetShadingRate | • Gültig | • Gültig | • Gültig | ||
RSSetShadingRateImage | • Gültig | • Gültig | • Gültig | ||
RSSetViewports | • Gültig | • Gültig | |||
SetComputeRoot32BitConstant | • Gültig | • Gültig | • Gültig | • Gültig | |
SetComputeRoot32BitConstants | • Gültig | • Gültig | • Gültig | • Gültig | |
SetComputeRootConstantBufferView | • Gültig | • Gültig | • Gültig | • Gültig | |
SetComputeRootDescriptorTable | • Gültig | • Gültig | • Gültig | • Gültig | |
SetComputeRootShaderResourceView | • Gültig | • Gültig | • Gültig | • Gültig | |
SetComputeRootSignature | • Gültig | • Gültig | • Gültig | • Gültig | |
SetComputeRootUnorderedAccessView | • Gültig | • Gültig | • Gültig | • Gültig | |
SetDescriptorHeaps | • Gültig | • Gültig | • Gültig | • Gültig | |
SetGraphicsRoot32BitConstant | • Gültig | • Gültig | • Gültig | ||
SetGraphicsRoot32BitConstants | • Gültig | • Gültig | • Gültig | ||
SetGraphicsRootConstantBufferView | • Gültig | • Gültig | • Gültig | ||
SetGraphicsRootDescriptorTable | • Gültig | • Gültig | • Gültig | ||
SetGraphicsRootShaderResourceView | • Gültig | • Gültig | • Gültig | ||
SetGraphicsRootSignature | • Gültig | • Gültig | • Gültig | ||
SetGraphicsRootUnorderedAccessView | • Gültig | • Gültig | • Gültig | ||
SetPipelineState | • Gültig | • Gültig | • Gültig | • Gültig | |
SetPipelineState1 | • Gültig | • Gültig | • Gültig | ||
SetPredication | • Gültig | • Gültig | • Gültig | ||
SetProtectedResourceSession | • Gültig | • Gültig | • Gültig | ||
SetSamplePositions | • Gültig | • Gültig | • Gültig | ||
SetViewInstanceMask | • Gültig | • Gültig | • Gültig | ||
SOSetTargets | • Gültig | • Gültig | |||
WriteBufferImmediate | • Gültig | • Gültig | • Gültig | • Gültig | • Gültig |
Bundleeinschränkungen
Einschränkungen ermöglichen es Direct3D 12-Treibern, die meiste Arbeit im Zusammenhang mit Bundles zum Zeitpunkt der Aufzeichnung auszuführen, sodass die ExecuteBundle-API mit geringem Mehraufwand ausgeführt werden kann. Alle Pipelinestatusobjekte, auf die von einem Bundle verwiesen wird, müssen dieselben Renderzielformate, tiefenpufferformat und Beispielbeschreibungen aufweisen.
Die folgenden API-Aufrufe der Befehlsliste sind in mit dem Typ D3D12_COMMAND_LIST_TYPE_BUNDLE erstellten Befehlslisten nicht zulässig:
- Beliebige Clear-Methode
- Beliebige Kopiermethode
- DiscardResource
- ExecuteBundle
- ResourceBarrier
- ResolveSubresource
- SetPredication
- BeginQuery
- EndQuery
- SOSetTargets
- OMSetRenderTargets
- RSSetViewports
- RSSetScissorRects
SetDescriptorHeaps kann für ein Bundle aufgerufen werden, aber die Descriptor-Heaps des Bundles müssen mit dem Descriptor-Heap der aufrufenden Befehlsliste übereinstimmen.
Wenn eine dieser APIs für ein Bundle aufgerufen wird, weist die Laufzeit den Aufruf zurück. Die Debugebene wird jedes Mal, wenn dies auftritt, einen Fehler ausgeben.