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.
Przykład D3D12nBodyGravity pokazuje, jak wykonywać obliczenia asynchronicznie. Przykład uruchamia wiele wątków z kolejką poleceń obliczeniowych i planuje pracę obliczeniową na procesorze GPU, który wykonuje symulację grawitacji n-ciała. Każdy wątek działa na dwóch pełnych pozycji i danych prędkości. W przypadku każdej iteracji moduł cieniowania obliczeniowego odczytuje bieżące położenie i dane prędkości z jednego buforu i zapisuje następną iterację w drugim buforze. Po zakończeniu iteracji cieniowanie obliczeniowe zamienia bufor, który bufor jest SRV do odczytywania danych o pozycji/prędkości i który jest UAV do zapisywania pozycji/prędkości aktualizacji przez zmianę stanu zasobu na każdym buforze.
- Tworzenie podpisów głównych
- Tworzenie SRV i UAV
- Tworzenie CBV i wierzchołków
- Synchronizowanie wątków renderowania i obliczeń
- Uruchom przykładową
- Tematy pokrewne
Tworzenie podpisów głównych
Zaczynamy od utworzenia zarówno grafiki, jak i podpisu głównego obliczeń w metodzie LoadAssets. Oba podpisy główne mają widok stałej buforu głównego (CBV) i tabelę deskryptora widoku zasobów cieniowania (SRV). Sygnatura główna obliczeń ma również tabelę deskryptora widoku dostępu nieuporządkowanego (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)));
}
Tworzenie SRV i UAV
SRV i UAV składają się z tablicy danych położenia i prędkości.
// 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;
};
| Przepływ wywołań | Parametry |
|---|---|
| XMFLOAT4 |
Tworzenie CBV i wierzchołków
W przypadku potoku grafiki CBV jest strukturą zawierającą dwie macierze używane przez cieniowanie geometrii. Cieniowanie geometrii przyjmuje pozycję każdej cząstki w układzie i generuje czworokąt, aby przedstawić go przy użyciu tych macierzy.
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];
};
| Przepływ wywołań | Parametry |
|---|---|
| XMMATRIX |
W rezultacie bufor wierzchołka używany przez cieniator wierzchołków w rzeczywistości nie zawiera żadnych danych pozycyjnych.
// "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;
};
| Przepływ wywołań | Parametry |
|---|---|
| XMFLOAT4 |
W przypadku potoku obliczeniowego CBV jest strukturą zawierającą pewne stałe używane przez symulację grawitacji n-ciała w cieniowaniu obliczeniowym.
struct ConstantBufferCS
{
UINT param[4];
float paramf[4];
};
Synchronizowanie wątków renderowania i obliczeń
Po zainicjowaniu rozpocznie się renderowanie i praca obliczeniowa. Wątek obliczeniowy zmieni stan dwóch położenia/prędkości z powrotem między SRV i UAV, ponieważ iteruje w symulacji, a wątek renderowania musi zapewnić, że planuje pracę nad potokiem grafiki, który działa na serwerze SRV. Ogrodzenia służą do synchronizowania dostępu do dwóch.
W wątku renderowania:
// 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();
}
| Przepływ wywołań | Parametry |
|---|---|
| InterlockedExchange | |
| InterlockedGetValue | |
| GetCompletedValue | |
| oczekiwania | |
| ID3D12CommandList | |
| ExecuteCommandLists | |
| IDXGISwapChain1::P resent1 |
Aby uprościć próbkę, wątek obliczeniowy czeka na ukończenie każdej iteracji procesora GPU przed zaplanowaniem kolejnych prac obliczeniowych. W praktyce aplikacje prawdopodobnie będą chciały zachować pełną kolejkę obliczeniową, aby osiągnąć maksymalną wydajność z procesora GPU.
W wątku Obliczenia:
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;
}
Uruchamianie przykładu
Tematy pokrewne
przewodniki kodu D3D12