Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Das D3D12nBodyGravity- Beispiel veranschaulicht, wie Die Berechnung asynchron ausgeführt wird. Im Beispiel wird eine Reihe von Threads mit einer Computebefehlswarteschlange gedreht und die Berechnung der Arbeit an der GPU geplant, die eine n-Body-Schwerkraftsimulation durchführt. Jeder Thread arbeitet mit zwei Puffern voller Positions- und Geschwindigkeitsdaten. Bei jeder Iteration liest der Compute-Shader die aktuelle Position und Geschwindigkeitsdaten aus einem Puffer und schreibt die nächste Iteration in den anderen Puffer. Nach Abschluss der Iteration wechselt der Compute-Shader, welcher Puffer der SRV zum Lesen von Positions-/Geschwindigkeitsdaten ist und welche UAV zum Schreiben von Positions-/Geschwindigkeitsaktualisierungen ist, indem der Ressourcenzustand für jeden Puffer geändert wird.
- Erstellen der Stammsignaturen
- Erstellen der SRV- und UAV-Puffer
- Erstellen der CBV- und Vertexpuffer
- Synchronisieren der Rendering- und Computethreads
- Ausführen des Beispiel-
- Verwandte Themen
Erstellen der Stammsignaturen
Zunächst erstellen wir sowohl eine Grafik als auch eine Computestammsignatur in der LoadAssets-Methode. Beide Stammsignaturen verfügen über eine Stammkonstantenpufferansicht (CBV) und eine Shaderressourcenansicht (SRV)-Beschreibungstabelle. Die Computestammsignatur verfügt auch über eine ungeordnete Zugriffsansichtstabelle (UAV).
// Create the root signatures.
{
CD3DX12_DESCRIPTOR_RANGE ranges[2];
ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0);
ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0);
CD3DX12_ROOT_PARAMETER rootParameters[RootParametersCount];
rootParameters[RootParameterCB].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_ALL);
rootParameters[RootParameterSRV].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_VERTEX);
rootParameters[RootParameterUAV].InitAsDescriptorTable(1, &ranges[1], D3D12_SHADER_VISIBILITY_ALL);
// The rendering pipeline does not need the UAV parameter.
CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init(_countof(rootParameters) - 1, rootParameters, 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 compute signature. Must change visibility for the SRV.
rootParameters[RootParameterSRV].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
CD3DX12_ROOT_SIGNATURE_DESC computeRootSignatureDesc(_countof(rootParameters), rootParameters, 0, nullptr);
ThrowIfFailed(D3D12SerializeRootSignature(&computeRootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));
ThrowIfFailed(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_computeRootSignature)));
}
Erstellen der SRV- und UAV-Puffer
Die SRV- und UAV-Puffer bestehen aus einem Array von Positions- und Geschwindigkeitsdaten.
// Position and velocity data for the particles in the system.
// Two buffers full of Particle data are utilized in this sample.
// The compute thread alternates writing to each of them.
// The render thread renders using the buffer that is not currently
// in use by the compute shader.
struct Particle
{
XMFLOAT4 position;
XMFLOAT4 velocity;
};
Anruffluss | Parameter |
---|---|
XMFLOAT4 |
Erstellen der CBV- und Vertexpuffer
Für die Grafikpipeline ist cbV eine Struktur mit zwei Matrizen, die vom Geometrie-Shader verwendet werden. Der Geometrie-Shader übernimmt die Position der einzelnen Partikel im System und generiert ein Quad, um es mithilfe dieser Matrizen darzustellen.
struct ConstantBufferGS
{
XMMATRIX worldViewProjection;
XMMATRIX inverseView;
// Constant buffers are 256-byte aligned in GPU memory. Padding is added
// for convenience when computing the struct's size.
float padding[32];
};
Anruffluss | Parameter |
---|---|
XMMATRIX- |
Daher enthält der vom Vertex-Shader verwendete Vertexpuffer tatsächlich keine Positionsdaten.
// "Vertex" definition for particles. Triangle vertices are generated
// by the geometry shader. Color data will be assigned to those
// vertices via this struct.
struct ParticleVertex
{
XMFLOAT4 color;
};
Anruffluss | Parameter |
---|---|
XMFLOAT4 |
Für die Computepipeline ist der CBV eine Struktur, die einige Konstanten enthält, die von der n-Body-Schwerkraftsimulation im Computeshader verwendet werden.
struct ConstantBufferCS
{
UINT param[4];
float paramf[4];
};
Synchronisieren der Rendering- und Computethreads
Nachdem die Puffer initialisiert wurden, beginnt das Rendern und Berechnen der Arbeit. Der Computethread ändert den Zustand der beiden Positions-/Geschwindigkeitspuffer zwischen SRV und UAV, da er die Simulation durchläuft, und der Renderingthread muss sicherstellen, dass er die Arbeit an der Grafikpipeline plant, die auf dem SRV ausgeführt wird. Zäune werden verwendet, um den Zugriff auf die beiden Puffer zu synchronisieren.
Im Renderthread:
// Render the scene.
void D3D12nBodyGravity::OnRender()
{
// Let the compute thread know that a new frame is being rendered.
for (int n = 0; n < ThreadCount; n++)
{
InterlockedExchange(&m_renderContextFenceValues[n], m_renderContextFenceValue);
}
// Compute work must be completed before the frame can render or else the SRV
// will be in the wrong state.
for (UINT n = 0; n < ThreadCount; n++)
{
UINT64 threadFenceValue = InterlockedGetValue(&m_threadFenceValues[n]);
if (m_threadFences[n]->GetCompletedValue() < threadFenceValue)
{
// Instruct the rendering command queue to wait for the current
// compute work to complete.
ThrowIfFailed(m_commandQueue->Wait(m_threadFences[n].Get(), threadFenceValue));
}
}
// 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(0, 0));
MoveToNextFrame();
}
Anruffluss | Parameter |
---|---|
InterlockedExchange- | |
InterlockedGetValue- | |
GetCompletedValue- | |
Warten | |
ID3D12CommandList- | |
ExecuteCommandLists- | |
IDXGISwapChain1::P resent1 |
Um das Beispiel etwas zu vereinfachen, wartet der Computethread, bis die GPU jede Iteration abgeschlossen hat, bevor weitere Computearbeit geplant wird. In der Praxis möchten Anwendungen wahrscheinlich die Computewarteschlange voll halten, um eine maximale Leistung von der GPU zu erzielen.
Im Computethread:
DWORD D3D12nBodyGravity::AsyncComputeThreadProc(int threadIndex)
{
ID3D12CommandQueue* pCommandQueue = m_computeCommandQueue[threadIndex].Get();
ID3D12CommandAllocator* pCommandAllocator = m_computeAllocator[threadIndex].Get();
ID3D12GraphicsCommandList* pCommandList = m_computeCommandList[threadIndex].Get();
ID3D12Fence* pFence = m_threadFences[threadIndex].Get();
while (0 == InterlockedGetValue(&m_terminating))
{
// Run the particle simulation.
Simulate(threadIndex);
// Close and execute the command list.
ThrowIfFailed(pCommandList->Close());
ID3D12CommandList* ppCommandLists[] = { pCommandList };
pCommandQueue->ExecuteCommandLists(1, ppCommandLists);
// Wait for the compute shader to complete the simulation.
UINT64 threadFenceValue = InterlockedIncrement(&m_threadFenceValues[threadIndex]);
ThrowIfFailed(pCommandQueue->Signal(pFence, threadFenceValue));
ThrowIfFailed(pFence->SetEventOnCompletion(threadFenceValue, m_threadFenceEvents[threadIndex]));
WaitForSingleObject(m_threadFenceEvents[threadIndex], INFINITE);
// Wait for the render thread to be done with the SRV so that
// the next frame in the simulation can run.
UINT64 renderContextFenceValue = InterlockedGetValue(&m_renderContextFenceValues[threadIndex]);
if (m_renderContextFence->GetCompletedValue() < renderContextFenceValue)
{
ThrowIfFailed(pCommandQueue->Wait(m_renderContextFence.Get(), renderContextFenceValue));
InterlockedExchange(&m_renderContextFenceValues[threadIndex], 0);
}
// Swap the indices to the SRV and UAV.
m_srvIndex[threadIndex] = 1 - m_srvIndex[threadIndex];
// Prepare for the next frame.
ThrowIfFailed(pCommandAllocator->Reset());
ThrowIfFailed(pCommandList->Reset(pCommandAllocator, m_computeState.Get()));
}
return 0;
}
Ausführen des Beispiels