Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Het D3D12nBodyGravity voorbeeld laat zien hoe u asynchroon rekenwerk kunt uitvoeren. Het voorbeeld draait een aantal threads elk met een wachtrij voor rekenopdrachten en plant rekenwerk op de GPU die een zwaartekrachtsimulatie n-lichaam uitvoert. Elke thread werkt op twee buffers vol positie- en snelheidsgegevens. Bij elke iteratie leest de compute-shader de huidige positie- en snelheidsgegevens uit de ene buffer en schrijft de volgende iteratie naar de andere buffer. Wanneer de iteratie is voltooid, wisselt de compute-shader welke buffer de SRV is voor het lezen van positie-/snelheidsgegevens en dit is de UAV voor het schrijven van positie/snelheidsupdates door de resourcestatus voor elke buffer te wijzigen.
- de hoofdhandtekeningen maken
- De SRV- en UAV-buffers maken
- De CBV- en hoekpuntbuffers maken
- de rendering- en rekenthreads synchroniseren
- de voorbeeld- uitvoeren
- Verwante onderwerpen
De basishandtekeningen maken
We beginnen met het maken van een graphics en een basishandtekening voor berekeningen, in de methode LoadAssets. Beide basishandtekeningen hebben een hoofdconstante bufferweergave (CBV) en een arceringsresourceweergave (SRV) descriptortabel. De rekenhoofdhandtekening heeft ook een niet-geordende UAV-descriptortabel (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)));
}
De SRV- en UAV-buffers maken
De SRV- en UAV-buffers bestaan uit een matrix met positie- en snelheidsgegevens.
// 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;
};
| Oproepstroom | Parameters |
|---|---|
| XMFLOAT4 |
De CBV- en hoekpuntbuffers maken
Voor de grafische pijplijn is de CBV een struct met twee matrices die door de geometrie-shader worden gebruikt. De geometrie-shader neemt de positie van elk deeltje in het systeem en genereert een quad om deze weer te geven met behulp van deze matrices.
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];
};
| Oproepstroom | Parameters |
|---|---|
| XMMATRIX- |
Als gevolg hiervan bevat de hoekpuntbuffer die door de hoekpunt-shader wordt gebruikt, geen positionele gegevens.
// "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;
};
| Oproepstroom | Parameters |
|---|---|
| XMFLOAT4 |
Voor de rekenpijplijn is de CBV een struct- met enkele constanten die worden gebruikt door de n-body zwaartekrachtsimulatie in de compute-shader.
struct ConstantBufferCS
{
UINT param[4];
float paramf[4];
};
De rendering- en rekenthreads synchroniseren
Nadat de buffers zijn geïnitialiseerd, wordt het rendering- en rekenwerk gestart. De berekeningsthread wijzigt de status van de twee positie-/snelheidsbuffers tussen SRV en UAV terwijl deze wordt herhaald in de simulatie en de renderingthread moet ervoor zorgen dat de planning wordt uitgevoerd op de grafische pijplijn die op de SRV werkt. Omheiningen worden gebruikt om de toegang tot de twee buffers te synchroniseren.
Op de Render-thread:
// 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();
}
| Oproepstroom | Parameters |
|---|---|
| InterlockedExchange- | |
| InterlockedGetValue | |
| GetCompletedValue | |
| wachten | |
| ID3D12CommandList | |
| ExecuteCommandLists | |
| IDXGISwapChain1::P resent1 |
Om het voorbeeld een beetje te vereenvoudigen, wacht de rekenthread totdat de GPU elke iteratie heeft voltooid voordat er meer rekenwerk wordt gepland. In de praktijk willen toepassingen waarschijnlijk de rekenwachtrij vol houden om maximale prestaties van de GPU te bereiken.
Op de compute-thread:
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;
}
Het voorbeeld uitvoeren