次の方法で共有


マルチエンジンの n 体重力シミュレーション

D3D12nBodyGravity サンプルでは、コンピューティング処理を非同期的に実行する方法を示します。 このサンプルでは、コンピューティング コマンド キューを使用して各スレッドの数をスピンアップし、n 体重力シミュレーションを実行する GPU でコンピューティング作業をスケジュールします。 各スレッドは、位置データと速度データでいっぱいの 2 つのバッファーで動作します。 反復処理のたびに、コンピューティング シェーダーは、あるバッファーから現在の位置と速度データを読み取り、次のイテレーションをもう一方のバッファーに書き込みます。 イテレーションが完了すると、コンピューティング シェーダーは、位置/速度データを読み取るための SRV であるバッファーと、各バッファーのリソース状態を変更して位置/速度の更新を書き込むための UAV をスワップします。

ルート署名を作成する

最初に、LoadAssets メソッドで、グラフィックスとコンピューティング ルートシグネチャの両方を作成します。 両方のルート署名には、ルート定数バッファー ビュー (CBV) とシェーダー リソース ビュー (SRV) 記述子テーブルがあります。 コンピューティング ルート署名には、順序なしアクセス ビュー (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)));
       }
通話フロー パラメーター
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

 

SRV バッファーと UAV バッファーを作成する

SRV バッファーと UAV バッファーは、位置データと速度データの配列で構成されます。

 // 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;
       };
通話フロー パラメーター
XMFLOAT4

 

CBV バッファーと頂点バッファーを作成する

グラフィックス パイプラインの場合、CBV はジオメトリ シェーダーによって使用される 2 つのマトリックスを含む 構造体 です。 ジオメトリ シェーダーは、システム内の各パーティクルの位置を取得し、これらのマトリックスを使用してそれを表すクワッドを生成します。

 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];
       };
通話フロー パラメーター
XMMATRIX

 

その結果、頂点シェーダーによって使用される頂点バッファーには、実際には位置データは含まれません。

 // "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;
       };
通話フロー パラメーター
XMFLOAT4

 

コンピューティング パイプラインの場合、CBV は 構造体 で、コンピューティング シェーダーの n 体重力シミュレーションで使用される定数がいくつか含まれています。

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

レンダリング スレッドとコンピューティング スレッドを同期する

バッファーがすべて初期化されると、レンダリングとコンピューティングの処理が開始されます。 計算スレッドは、シミュレーションで反復処理を行う SRV と UAV の間で 2 つの位置/速度バッファーの状態を変更します。レンダリング スレッドは、SRV で動作するグラフィックス パイプラインで作業をスケジュールする必要があります。 フェンスは、2 つのバッファーへのアクセスを同期するために使用されます。

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();
}
通話フロー パラメーター
InterlockedExchange
InterlockedGetValue
GetCompletedValueする
wait
ID3D12CommandList
ExecuteCommandLists
IDXGISwapChain1::P resent1

 

サンプルを少し簡略化するために、コンピューティング スレッドは GPU が各イテレーションを完了するのを待ってから、コンピューティング作業をスケジュールします。 実際には、アプリケーションでは、GPU から最大のパフォーマンスを実現するために、コンピューティング キューを完全に保つことを望む可能性があります。

コンピューティング スレッドで次の手順を実行します。

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;
}
通話フロー パラメーター
ID3D12CommandQueue
ID3D12CommandAllocator
ID3D12GraphicsCommandList
ID3D12Fence
InterlockedGetValue
閉じる
ID3D12CommandList
ExecuteCommandLists
InterlockedIncrement
シグナル
SetEventOnCompletion
WaitForSingleObject
InterlockedGetValue
GetCompletedValueする
wait
InterlockedExchange
ID3D12CommandAllocator::Reset
ID3D12GraphicsCommandList::Reset

 

サンプルを実行する

最終的な n 個の重力シミュレーションする

D3D12 コード のチュートリアル

マルチエンジン同期