Simulasi gravitasi n-body multi-engine

Sampel D3D12nBodyGravity menunjukkan cara melakukan pekerjaan komputasi secara asinkron. Sampel memutar sejumlah utas masing-masing dengan antrean perintah komputasi dan menjadwalkan pekerjaan komputasi pada GPU yang melakukan simulasi gravitasi n-body. Setiap utas beroperasi pada dua buffer yang penuh dengan posisi dan data kecepatan. Dengan setiap perulangan, shader komputasi membaca posisi saat ini dan data kecepatan dari satu buffer dan menulis iterasi berikutnya ke buffer lainnya. Ketika iterasi selesai, shader komputasi menukar buffer mana yang merupakan SRV untuk membaca data posisi/kecepatan dan yang merupakan UAV untuk menulis pembaruan posisi/kecepatan dengan mengubah status sumber daya pada setiap buffer.

Membuat tanda tangan akar

Kita mulai dengan membuat grafik dan tanda tangan akar komputasi, dalam metode LoadAssets . Kedua tanda tangan akar memiliki tampilan buffer konstan akar (CBV) dan tabel deskriptor tampilan sumber daya shader (SRV). Tanda tangan akar komputasi juga memiliki tabel deskriptor tampilan akses tidak berurut (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)));
       }
Alur panggilan Parameter
CD3DX12_DESCRIPTOR_RANGE D3D12_DESCRIPTOR_RANGE_TYPE
CD3DX12_ROOT_PARAMETER D3D12_SHADER_VISIBILITY
CD3DX12_ROOT_SIGNATURE_DESC D3D12_ROOT_SIGNATURE_FLAGS
ID3DBlob
D3D12SerializeRootSignature D3D_ROOT_SIGNATURE_VERSION
CreateRootSignature
CD3DX12_ROOT_SIGNATURE_DESC
D3D12SerializeRootSignature D3D_ROOT_SIGNATURE_VERSION
CreateRootSignature

 

Membuat buffer SRV dan UAV

Buffer SRV dan UAV terdiri dari array posisi dan data kecepatan.

 // 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;
       };
Alur panggilan Parameter
XMFLOAT4

 

Membuat buffer CBV dan vertex

Untuk alur grafis, CBV adalah struct yang berisi dua matriks yang digunakan oleh shader geometri. Shader geometri mengambil posisi setiap partikel dalam sistem dan menghasilkan quad untuk mewakilinya menggunakan matriks ini.

 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];
       };
Alur panggilan Parameter
XMMATRIX

 

Akibatnya, buffer vertex yang digunakan oleh shader vertex sebenarnya tidak berisi data posisi apa pun.

 // "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;
       };
Alur panggilan Parameter
XMFLOAT4

 

Untuk alur komputasi, CBV adalah struct yang berisi beberapa konstanta yang digunakan oleh simulasi gravitasi n-body di shader komputasi.

 struct ConstantBufferCS
       {
              UINT param[4];
              float paramf[4];
       };

Menyinkronkan rangkaian penyajian dan komputasi

Setelah buffer semuanya diinisialisasi, penyajian dan pekerjaan komputasi akan dimulai. Utas komputasi akan mengubah status buffer dua posisi/kecepatan bolak-balik antara SRV dan UAV saat berulang pada simulasi, dan utas penyajian perlu memastikan bahwa ia menjadwalkan pekerjaan pada alur grafis yang beroperasi pada SRV. Pagar digunakan untuk menyinkronkan akses ke dua buffer.

Pada utas Render:

// 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();
}
Alur panggilan Parameter
InterlockedExchange
InterlockedGetValue
GetCompletedValue
Tunggu
ID3D12CommandList
ExecuteCommandLists
IDXGISwapChain1::P resent1

 

Untuk menyederhanakan sampel sedikit, utas komputasi menunggu GPU menyelesaikan setiap iterasi sebelum menjadwalkan pekerjaan komputasi lagi. Dalam praktiknya, aplikasi kemungkinan ingin menjaga antrean komputasi tetap penuh untuk mencapai performa maksimum dari GPU.

Pada utas Komputasi:

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;
}
Alur panggilan Parameter
ID3D12CommandQueue
ID3D12CommandAllocator
ID3D12GraphicsCommandList
ID3D12Fence
InterlockedGetValue
Tutup
ID3D12CommandList
ExecuteCommandLists
InterlockedIncrement
Sinyal
SetEventOnCompletion
WaitForSingleObject
InterlockedGetValue
GetCompletedValue
Tunggu
InterlockedExchange
ID3D12CommandAllocator::Reset
ID3D12GraphicsCommandList::Reset

 

Menjalankan sampel

cuplikan layar simulasi gravitasi n tubuh akhir

Panduan Kode D3D12

Sinkronisasi multi-mesin