Bicameral Engine
GPU-resident engine for a magic-research open-world game
Loading...
Searching...
No Matches
gpu_physics.cpp
Go to the documentation of this file.
1// gpu_physics.cpp — 整数の AVBD の 1 刻みを GPU の Compute で走らせる(gpu_physics.h)。
2// パスを呼ぶ順番は CPU の PhysicsWorld::Substep と同じ。パスの間は全部の UAV の書き込みを待つ(グローバルな UAV バリア)。
3// 色の数と彩色の回数は GPU の中で決まるので、読み戻さずに固定の回数(PX_GPU_MAX_COLORS・colorRounds)だけ投げる
4// (空の色・塗る物の無い回は何もしない)。回数を詰めるのは T-0092(Work Graphs・間接の起動)。
5#include "sim/gpu_physics.h"
6
7#include "gpu/resources.h"
8
9namespace bicameral::sim {
10
11 namespace {
12
13 using namespace bicameral::physics;
14
15 constexpr uint32_t THREADS_PER_GROUP = 64; // physics_step.hlsl の numthreads
16 constexpr uint32_t ROOT_CONSTANT_COUNT = 14;
17 constexpr uint32_t STATS_BYTES = sizeof(GpuPhysicsStats);
18
19 // u0〜u13 / b0 / デバッグのリング / t0〜t1(ルート署名の大きさ 48 / 64 語)
21 .uavCount = 14, .rootConstantCount = ROOT_CONSTANT_COUNT, .debugRing = true, .srvCount = 2};
22
23 constexpr uint32_t COLOR_PREDICATE_UAV = 13; // u13 色ごとの「その色の物がいる」(述語。T-0094)
24
25 // shaders/CMakeLists.txt で入口ごとに作る .cso(Pass の順)
26 constexpr std::array<const char*, 13> SHADER_NAMES = {
27 "sim/physics_initialize.cso", "sim/physics_begin_substep.cso", "sim/physics_broadphase.cso",
28 "sim/physics_narrowphase.cso", "sim/physics_prepare_manifolds.cso", "sim/physics_color_round.cso",
29 "sim/physics_finish_coloring.cso", "sim/physics_recollide.cso", "sim/physics_solve_color.cso",
30 "sim/physics_update_duals.cso", "sim/physics_update_velocity.cso", "sim/physics_finish.cso",
31 "sim/physics_build_manifolds.cso"};
32
33 // 計測の表示名(Pass の順)
34 constexpr std::array<const char*, 15> PASS_NAMES = {
35 "Initialize", "BeginSubstep", "Broadphase", "Narrowphase", "PrepareManifolds",
36 "ColorRound", "FinishColoring", "Recollide", "SolveColor", "UpdateDuals",
37 "UpdateVelocity", "Finish", "BuildManifolds", "BroadphaseGraph", "SolveColorGraph"};
38
39 constexpr auto COLOR_COUNT = (uint64_t)PX_GPU_MAX_COLORS;
40
41 // physics_bindings.hlsli の COLOR_INPUT_BYTES(色ごとの GPU の入力の間隔)
42 static_assert(sizeof(D3D12_NODE_GPU_INPUT) == 24);
43
44 // physics_graph.hlsl の BroadphaseNode の NumThreads と NodeMaxDispatchGrid
45 constexpr uint32_t BROADPHASE_THREADS = 16;
46 constexpr uint32_t BROADPHASE_MAX_GROUPS = 1024;
47
48 // 1 つのリストで打てるタイムスタンプの数(60 刻み × 約 230 パス + 余り)
49 constexpr uint32_t MAX_TIMESTAMPS = 1u << 16;
50
51 // physics_step.hlsl の ManifoldHeader(組の点を除いた部分)
52 constexpr uint64_t MANIFOLD_HEADER_BYTES = 80;
53
54 template <typename T>
55 ComPtr<ID3D12Resource> CreateUploadBuffer(ID3D12Device* device, std::span<const T> data) {
56 const auto bytes = std::as_bytes(data);
57 ComPtr<ID3D12Resource> buffer = gpu::CreateBuffer(device, bytes.size(), gpu::BufferKind::Upload);
58 void* mapped = nullptr;
59 const D3D12_RANGE noRead{.Begin = 0, .End = 0};
60 if (!buffer || FAILED(buffer->Map(0, &noRead, &mapped)))
61 return nullptr;
62
63 std::memcpy(mapped, bytes.data(), bytes.size());
64 buffer->Unmap(0, nullptr);
65
66 return buffer;
67 }
68
69 uint32_t GroupsFor(uint32_t threadCount) {
70 return (threadCount + THREADS_PER_GROUP - 1) / THREADS_PER_GROUP;
71 }
72
73 } // namespace
74
75 std::expected<GpuPhysics, std::string> GpuPhysics::Create(ID3D12Device5* device, const PhysicsScene& scene,
76 const PxParameters& parameters,
77 const GpuPhysicsOptions& options) {
78 GpuPhysics result;
79 result.m_parameters = parameters;
80 result.m_options = options;
81 result.m_bodyCount = (uint32_t)scene.bodies.size();
82 result.m_frictionQ16 = scene.frictionQ16;
83
84 if (options.slotsPerBody > PX_GPU_MAX_SLOTS_PER_BODY)
85 return std::unexpected(std::format("slotsPerBody は {} まで", PX_GPU_MAX_SLOTS_PER_BODY));
86
87 if (auto created = result.CreatePipelines(device); !created)
88 return std::unexpected(created.error());
89
90 if (auto created = result.CreateBuffers(device, scene); !created)
91 return std::unexpected(created.error());
92
93 return result;
94 }
95
96 // ルート署名・Compute のパス・Work Graph(使うときだけ)
97 std::expected<void, std::string> GpuPhysics::CreatePipelines(ID3D12Device5* device) {
98 m_rootSignature = gpu::CreateRootSignature(device, ROOT_LAYOUT);
99 if (!m_rootSignature)
100 return std::unexpected("物理のルート署名を作れない");
101
102 for (size_t pass = 0; pass < SHADER_NAMES.size(); ++pass) {
103 const auto bytecode = gpu::LoadShader(SHADER_NAMES[pass]);
104 if (!bytecode)
105 return std::unexpected(bytecode.error());
106
107 m_pipelines[pass] = gpu::CreateComputePipeline(device, m_rootSignature.Get(), *bytecode);
108 if (!m_pipelines[pass])
109 return std::unexpected(std::format("パイプラインを作れない: {}", SHADER_NAMES[pass]));
110 }
111
112 if (m_options.broadphaseGraph || m_options.solver == GpuPhysicsSolver::Graph) {
113 if ((m_bodyCount + BROADPHASE_THREADS - 1) / BROADPHASE_THREADS > BROADPHASE_MAX_GROUPS)
114 return std::unexpected("物が多すぎる(広域の選別の Work Graph の NodeMaxDispatchGrid)");
115
116 const auto library = gpu::LoadShader("sim/physics_graph.cso");
117 if (!library)
118 return std::unexpected(library.error());
119
120 auto graph = gpu::WorkGraph::Create(device, m_rootSignature.Get(), *library, L"PhysicsBroadphase");
121 if (!graph)
122 return std::unexpected(graph.error());
123
124 m_graph = std::make_unique<gpu::WorkGraph>(std::move(*graph));
125 m_broadphaseEntry = m_graph->EntrypointIndex(L"BroadphaseNode");
126 m_solveEntry = m_graph->EntrypointIndex(L"SolveBodyNode");
127 if (m_broadphaseEntry == UINT32_MAX || m_solveEntry == UINT32_MAX)
128 return std::unexpected("physics_graph.cso に BroadphaseNode か SolveBodyNode が無い");
129 }
130
131 return {};
132 }
133
134 // バッファ(physics_bindings.hlsli の結び付けの順)・アップロード・読み戻し
135 std::expected<void, std::string> GpuPhysics::CreateBuffers(ID3D12Device5* device, const PhysicsScene& scene) {
136 const uint64_t n = m_bodyCount;
137 const uint64_t slots = n * m_options.slotsPerBody;
138 const std::array<uint64_t, 14> uavBytes = {
139 n * sizeof(PhysicsBody), // u0 物
140 2 * slots * MANIFOLD_HEADER_BYTES, // u1 組の見出し
141 2 * n * sizeof(uint32_t), // u2 持ち主の枠の数
142 slots * sizeof(uint32_t), // u3 枠の相手
143 n * m_options.incidentPerBody * sizeof(uint32_t), // u4 物の組の一覧
144 n * sizeof(uint32_t), // u5 一覧の数
145 n * sizeof(uint64_t), // u6 触れている質量
146 2 * n * sizeof(int32_t), // u7 彩色の回ごとの色
147 STATS_BYTES, // u8 統計
148 2 * slots * PX_MANIFOLD_POINTS * sizeof(PhysicsContactPoint), // u9 組の点
149 slots * sizeof(PxContactGeometry), // u10 接触の幾何(GPU だけが読み書き。HLSL の間隔は C++ の大きさ以下)
150 COLOR_COUNT * sizeof(D3D12_NODE_GPU_INPUT), // u11 色ごとの GPU の入力
151 COLOR_COUNT * n * sizeof(uint32_t), // u12 色ごとの物の一覧
152 COLOR_COUNT * sizeof(uint64_t)}; // u13 その色の物がいる(述語)
153 for (size_t i = 0; i < uavBytes.size(); ++i) {
155 if (!m_uavs[i])
156 return std::unexpected("物理のバッファを作れない");
157 }
158
159 std::vector<PhysicsBody> initialBodies;
160 initialBodies.reserve(scene.bodies.size());
161 const int64_t rate = PxStepRate(m_parameters);
162 for (const PhysicsSceneBody& source : scene.bodies)
163 initialBodies.push_back(MakePhysicsBody(source, rate));
164
165 const std::array<PxParameters, 1> parameterArray = {m_parameters};
166 m_srvs = {CreateUploadBuffer(device, std::span<const PxParameters>(parameterArray)),
167 CreateUploadBuffer(device, std::span<const PhysicsBody>(initialBodies))};
170 if (!m_srvs[0] || !m_srvs[1] || !m_bodiesReadback || !m_statsReadback)
171 return std::unexpected("物理のアップロード・読み戻しのバッファを作れない");
172
173 return {};
174 }
175
177 return {.bodyCount = m_bodyCount,
178 .slotsPerBody = m_options.slotsPerBody,
179 .incidentPerBody = m_options.incidentPerBody,
180 .currentHalf = m_half,
181 .tickLow = (uint32_t)m_tick,
182 .tickHigh = (uint32_t)(m_tick >> 32),
183 .frictionQ16 = m_frictionQ16,
184 .solveEntry = m_solveEntry,
185 .colorBodiesLow = (uint32_t)m_uavs[12]->GetGPUVirtualAddress(),
186 .colorBodiesHigh = (uint32_t)(m_uavs[12]->GetGPUVirtualAddress() >> 32)};
187 }
188
189 // 1 つのパスを投げ、全部の UAV の書き込みを次のパスより前に終わらせる
190 void GpuPhysics::RecordPass(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, Pass pass,
191 const Constants& constants, uint32_t threadCount) {
192 RecordProfileStart(list);
193 list->SetComputeRootSignature(m_rootSignature.Get());
194 list->SetPipelineState(m_pipelines[(size_t)pass].Get());
195 m_graphProgramSet = false;
196 BindRoot(list, debugRing, constants);
197 list->Dispatch(GroupsFor(threadCount), 1, 1);
198 const D3D12_RESOURCE_BARRIER barrier = gpu::UavBarrier(nullptr);
199 list->ResourceBarrier(1, &barrier);
200 RecordTimestamp(list, pass);
201 }
202
203 // 広域の選別 → 接触の生成の Work Graph(Broadphase・Narrowphase の 2 パスの代わり)。CPU の入力 1 件 = グループの数
204 void GpuPhysics::RecordBroadphaseGraph(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
205 const Constants& constants) {
206 RecordProfileStart(list);
207 SetGraphProgram(list);
208 BindRoot(list, debugRing, constants);
209
210 const uint32_t groupCount = (m_bodyCount + BROADPHASE_THREADS - 1) / BROADPHASE_THREADS;
211 gpu::WorkGraph::DispatchFromCpu(list, m_broadphaseEntry, &groupCount, 1, sizeof(groupCount));
212 const D3D12_RESOURCE_BARRIER barrier = gpu::UavBarrier(nullptr);
213 list->ResourceBarrier(1, &barrier);
215 }
216
217 // 色 color の物の一覧(FinishColoring が作った)を GPU の入力にして、その色の物だけを 1 グループずつ解く
218 void GpuPhysics::RecordSolveColorGraph(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
219 const Constants& constants, int32_t color) {
220 RecordProfileStart(list);
221 SetGraphProgram(list);
222 BindRoot(list, debugRing, constants);
223
224 const D3D12_GPU_VIRTUAL_ADDRESS input = m_uavs[11]->GetGPUVirtualAddress() +
225 (uint64_t)color * sizeof(D3D12_NODE_GPU_INPUT);
227 const D3D12_RESOURCE_BARRIER barrier = gpu::UavBarrier(nullptr);
228 list->ResourceBarrier(1, &barrier);
230 }
231
232 // 色ごとの GPU の入力と物の一覧は、DispatchGraph が読む間 NON_PIXEL_SHADER_RESOURCE(仕様の D3D12_NODE_GPU_INPUT)。
233 // 反復の前に UAV から移し、反復の後で戻す(FinishColoring と次の小刻みの BeginSubstep が UAV で書く)
234 void GpuPhysics::RecordColorListStates(ID3D12GraphicsCommandList10* list, bool toGraphInput) const {
235 const D3D12_RESOURCE_STATES uav = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
236 const D3D12_RESOURCE_STATES input = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
237 const D3D12_RESOURCE_STATES before = toGraphInput ? uav : input;
238 const D3D12_RESOURCE_STATES after = toGraphInput ? input : uav;
239 const std::array<D3D12_RESOURCE_BARRIER, 2> barriers = {gpu::Transition(m_uavs[11].Get(), before, after),
240 gpu::Transition(m_uavs[12].Get(), before, after)};
241 list->ResourceBarrier((uint32_t)barriers.size(), barriers.data());
242 }
243
244 // グラフを設定する(Compute のパスの後だけ)。裏のメモリの初期化は最初の 1 回だけ(リストは記録した順に実行される)。
245 // 呼んだ後でルートの引数を結ぶ
246 void GpuPhysics::SetGraphProgram(ID3D12GraphicsCommandList10* list) {
248 return;
249
250 list->SetComputeRootSignature(m_rootSignature.Get());
251 m_graph->SetProgram(list, !m_graphInitialized);
252 m_graphInitialized = true;
253 m_graphProgramSet = true;
254 }
255
256 // ルートの引数(u0〜u13・b0・デバッグのリング・t0〜t1)
257 void GpuPhysics::BindRoot(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
258 const Constants& constants) const {
259 static_assert(sizeof(Constants) == ROOT_CONSTANT_COUNT * sizeof(uint32_t));
260 static_assert(std::tuple_size_v<decltype(m_uavs)> == ROOT_LAYOUT.uavCount);
261 for (uint32_t i = 0; i < m_uavs.size(); ++i)
262 list->SetComputeRootUnorderedAccessView(i, m_uavs[i]->GetGPUVirtualAddress());
263
264 list->SetComputeRoot32BitConstants(ROOT_LAYOUT.RootConstantIndex(), ROOT_CONSTANT_COUNT, &constants, 0);
265 list->SetComputeRootUnorderedAccessView(ROOT_LAYOUT.DebugRingIndex(), debugRing);
266 for (uint32_t i = 0; i < m_srvs.size(); ++i)
267 list->SetComputeRootShaderResourceView(ROOT_LAYOUT.SrvIndex(i), m_srvs[i]->GetGPUVirtualAddress());
268 }
269
270 // --- 計測(T-0092)---
271
272 bool GpuPhysics::EnableProfiling(ID3D12Device* device) {
273 const D3D12_QUERY_HEAP_DESC queryDesc{
274 .Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP, .Count = MAX_TIMESTAMPS + 1, .NodeMask = 0};
275 if (FAILED(device->CreateQueryHeap(&queryDesc, IID_PPV_ARGS(&m_timestamps))))
276 return false;
277
278 m_timestampReadback = gpu::CreateBuffer(device, (uint64_t)(MAX_TIMESTAMPS + 1) * sizeof(uint64_t),
280
281 return m_timestampReadback != nullptr;
282 }
283
284 // リストの最初のパスの前に 1 個
285 void GpuPhysics::RecordProfileStart(ID3D12GraphicsCommandList10* list) const {
286 if (m_timestamps && m_profiledPasses.empty())
287 list->EndQuery(m_timestamps.Get(), D3D12_QUERY_TYPE_TIMESTAMP, 0);
288 }
289
290 // パスの後ろ(UAV バリアの後)に打つ
291 void GpuPhysics::RecordTimestamp(ID3D12GraphicsCommandList10* list, Pass pass) {
292 if (!m_timestamps || m_profiledPasses.size() >= MAX_TIMESTAMPS)
293 return;
294
295 m_profiledPasses.push_back(pass);
296 list->EndQuery(m_timestamps.Get(), D3D12_QUERY_TYPE_TIMESTAMP, (uint32_t)m_profiledPasses.size());
297 }
298
299 // RecordReadback を含むリストが終わってから。打ったパスの時間を種類ごとに足す
301 if (!m_timestamps || m_profiledPasses.empty())
302 return;
303
304 std::vector<uint64_t> ticks(m_profiledPasses.size() + 1);
305 if (!gpu::ReadBuffer(m_timestampReadback.Get(), std::as_writable_bytes(std::span(ticks))))
306 return;
307
308 for (size_t n = 0; n < m_profiledPasses.size(); ++n) {
309 const auto pass = (size_t)m_profiledPasses[n];
310 m_profileCounts[pass] += 1;
311 m_profileTicks[pass] += ticks[n + 1] - ticks[n];
312 }
313
314 m_profiledPasses.clear();
315 }
316
317 std::vector<GpuPhysicsPassTime> GpuPhysics::ProfileResult() const {
318 static_assert(PASS_NAMES.size() == (size_t)Pass::Count);
319 static_assert(SHADER_NAMES.size() + 2 ==
320 (size_t)Pass::Count); // BroadphaseGraph・SolveColorGraph は Work Graph
321
322 std::vector<GpuPhysicsPassTime> result;
323 for (size_t pass = 0; pass < PASS_NAMES.size(); ++pass) {
324 if (m_profileCounts[pass] > 0)
325 result.push_back(
326 {.name = PASS_NAMES[pass], .count = m_profileCounts[pass], .timestampTicks = m_profileTicks[pass]});
327 }
328
329 return result;
330 }
331
332 void GpuPhysics::RecordInitialize(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing) {
333 m_tick = 0;
334 m_half = 1;
336 }
337
338 void GpuPhysics::RecordStep(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing) {
339 m_graphProgramSet = false; // リストが変わったかもしれない
340 for (uint32_t substep = 0; substep < m_parameters.substeps; ++substep)
341 RecordSubstep(list, debugRing, substep);
342
343 ++m_tick;
344 }
345
346 // PhysicsWorld::Substep と同じ順
347 void GpuPhysics::RecordSubstep(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
348 uint32_t substep) {
349 m_half = 1 - m_half;
350 Constants constants = BaseConstants();
351 const uint32_t slotCount = m_bodyCount * m_options.slotsPerBody;
352
353 constants.resetStats = substep == 0 ? 1 : 0;
354 RecordPass(list, debugRing, Pass::BeginSubstep, constants, m_bodyCount);
355 constants.resetStats = 0;
356
357 if (m_graph) {
358 RecordBroadphaseGraph(list, debugRing, constants);
359 RecordPass(list, debugRing, Pass::BuildManifolds, constants, slotCount);
360 } else {
361 RecordPass(list, debugRing, Pass::Broadphase, constants, m_bodyCount);
362 RecordPass(list, debugRing, Pass::Narrowphase, constants, slotCount);
363 }
364
365 RecordPass(list, debugRing, Pass::PrepareManifolds, constants, slotCount);
366 RecordColoring(list, debugRing, constants);
367 if (SkipsEmptyColors())
368 RecordColorPredicateStates(list, true); // FinishColoring が書いた「その色の物がいる」を述語に
369
370 RecordIterations(list, debugRing, constants);
371 if (SkipsEmptyColors())
372 RecordColorPredicateStates(list, false);
373
374 RecordPass(list, debugRing, Pass::Finish, constants, m_bodyCount);
375 }
376
377 // 彩色: 回ごとに色の組を入れ替え、最後の回の結果を物に移す
378 void GpuPhysics::RecordColoring(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
379 Constants constants) {
380 for (uint32_t colorRound = 0; colorRound < m_options.colorRounds; ++colorRound) {
381 constants.colorIn = colorRound % 2;
382 RecordPass(list, debugRing, Pass::ColorRound, constants, m_bodyCount);
383 }
384
385 constants.colorIn = m_options.colorRounds % 2;
386 RecordPass(list, debugRing, Pass::FinishColoring, constants, m_bodyCount);
387 }
388
389 // 本反復は α = 1、最後の 1 回は α = 0。途中で接触を探し直し、最後の本反復の後で速度を決める
390 void GpuPhysics::RecordIterations(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
391 Constants constants) {
392 const uint32_t slotCount = m_bodyCount * m_options.slotsPerBody;
393 const uint32_t iterations = m_parameters.iterations;
394 const bool graph = m_options.solver == GpuPhysicsSolver::Graph;
395 if (graph)
396 RecordColorListStates(list, true);
397
398 for (uint32_t iteration = 0; iteration <= iterations; ++iteration) {
399 if (iteration == m_parameters.recollideIteration)
400 RecordPass(list, debugRing, Pass::Recollide, constants, slotCount);
401
402 constants.alphaQ16 = iteration < iterations ? (uint32_t)PX_ALPHA_ONE_Q16 : 0;
403 RecordSolveColors(list, debugRing, constants);
404
405 if (iteration < iterations)
406 RecordPass(list, debugRing, Pass::UpdateDuals, constants, slotCount);
407
408 if (iteration + 1 == iterations)
409 RecordPass(list, debugRing, Pass::UpdateVelocity, constants, m_bodyCount);
410 }
411
412 if (graph)
413 RecordColorListStates(list, false);
414 }
415
416 // 1 回の反復の色ごとの解。skipEmptyColors なら「その色の物がいる」(u13)の述語つき。
417 // 述語をかけるときの色ごとの解は Compute(述語と DispatchGraph は組み合わせない。SkipsEmptyColors)
418 void GpuPhysics::RecordSolveColors(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
419 Constants constants) {
420 const bool colorPredicates = SkipsEmptyColors();
421 for (int32_t color = 0; color < PX_GPU_MAX_COLORS; ++color) {
422 constants.color = color;
423 if (colorPredicates) {
424 list->SetPredication(m_uavs[COLOR_PREDICATE_UAV].Get(), (uint64_t)color * sizeof(uint64_t),
425 D3D12_PREDICATION_OP_EQUAL_ZERO); // 0 ならその色の解を飛ばす
426 }
427
429 RecordSolveColorGraph(list, debugRing, constants, color);
430 else
431 RecordPass(list, debugRing, Pass::SolveColor, constants,
432 m_bodyCount * THREADS_PER_GROUP); // 1 グループ = 1 物
433 }
434
435 if (colorPredicates)
436 list->SetPredication(nullptr, 0, D3D12_PREDICATION_OP_EQUAL_ZERO); // 戻さないと後ろのパスが黙って飛ぶ
437 }
438
439 // 述語のバッファ(u13)の状態: BeginSubstep が UAV で 0 にし、FinishColoring が UAV で書く → 反復の間だけ PREDICATION →
440 // 小刻みの終わりに UAV へ戻す(リストの終わりで COMMON に戻り、次のリストの BeginSubstep で UAV に昇格する)
441 void GpuPhysics::RecordColorPredicateStates(ID3D12GraphicsCommandList10* list, bool toPredication) const {
442 const D3D12_RESOURCE_STATES unordered = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
443 const D3D12_RESOURCE_STATES predication = D3D12_RESOURCE_STATE_PREDICATION;
444 const D3D12_RESOURCE_BARRIER barrier = gpu::Transition(m_uavs[COLOR_PREDICATE_UAV].Get(),
445 toPredication ? unordered : predication,
446 toPredication ? predication : unordered);
447 list->ResourceBarrier(1, &barrier);
448 }
449
450 void GpuPhysics::RecordReadback(ID3D12GraphicsCommandList10* list) const {
452 gpu::RecordCopyToReadback(list, m_uavs[8].Get(), m_statsReadback.Get());
453 if (m_timestamps && !m_profiledPasses.empty()) {
454 list->ResolveQueryData(m_timestamps.Get(), D3D12_QUERY_TYPE_TIMESTAMP, 0,
455 (uint32_t)m_profiledPasses.size() + 1, m_timestampReadback.Get(), 0);
456 }
457 }
458
459 std::vector<PhysicsBody> GpuPhysics::ReadBodies() const {
460 std::vector<PhysicsBody> bodies(m_bodyCount);
461 if (!gpu::ReadBuffer(m_bodiesReadback.Get(), std::as_writable_bytes(std::span(bodies))))
462 bodies.clear();
463
464 return bodies;
465 }
466
468 GpuPhysicsStats stats;
469 if (!gpu::ReadBuffer(m_statsReadback.Get(), std::as_writable_bytes(std::span(&stats, 1))))
470 stats.overflow = 0xFFFFFFFFu;
471
472 return stats;
473 }
474
475 uint64_t GpuPhysics::StateHash(uint64_t tick, const std::vector<PhysicsBody>& bodies) {
476 uint64_t hash = fx::FxMix64(tick);
477 for (const PhysicsBody& body : bodies)
478 hash = PxHashBody(hash, body);
479
480 return hash;
481 }
482
483} // namespace bicameral::sim
static std::expected< WorkGraph, std::string > Create(ID3D12Device5 *device, ID3D12RootSignature *globalRootSignature, std::span< const std::byte > library, std::wstring_view programName)
static void DispatchFromCpu(ID3D12GraphicsCommandList10 *list, uint32_t entrypointIndex, const void *records, uint32_t recordCount, uint64_t recordStrideBytes)
static void DispatchFromGpu(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS nodeGpuInput)
std::unique_ptr< gpu::WorkGraph > m_graph
bool EnableProfiling(ID3D12Device *device)
ComPtr< ID3D12Resource > m_bodiesReadback
void RecordIterations(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, Constants constants)
std::array< ComPtr< ID3D12Resource >, 2 > m_srvs
std::array< uint64_t,(size_t) Pass::Count > m_profileCounts
static std::expected< GpuPhysics, std::string > Create(ID3D12Device5 *device, const PhysicsScene &scene, const physics::PxParameters &parameters, const GpuPhysicsOptions &options={})
void RecordSolveColorGraph(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, const Constants &constants, int32_t color)
std::expected< void, std::string > CreatePipelines(ID3D12Device5 *device)
std::expected< void, std::string > CreateBuffers(ID3D12Device5 *device, const PhysicsScene &scene)
void RecordInitialize(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing)
std::array< ComPtr< ID3D12Resource >, 14 > m_uavs
std::array< uint64_t,(size_t) Pass::Count > m_profileTicks
ComPtr< ID3D12RootSignature > m_rootSignature
void RecordColorPredicateStates(ID3D12GraphicsCommandList10 *list, bool toPredication) const
void RecordBroadphaseGraph(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, const Constants &constants)
physics::PxParameters m_parameters
void RecordReadback(ID3D12GraphicsCommandList10 *list) const
GpuPhysicsStats ReadStats() const
void RecordColoring(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, Constants constants)
void RecordTimestamp(ID3D12GraphicsCommandList10 *list, Pass pass)
std::vector< GpuPhysicsPassTime > ProfileResult() const
ComPtr< ID3D12QueryHeap > m_timestamps
void RecordPass(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, Pass pass, const Constants &constants, uint32_t threadCount)
void RecordSubstep(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, uint32_t substep)
void RecordStep(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing)
static uint64_t StateHash(uint64_t tick, const std::vector< PhysicsBody > &bodies)
std::vector< Pass > m_profiledPasses
void BindRoot(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, const Constants &constants) const
void RecordColorListStates(ID3D12GraphicsCommandList10 *list, bool toGraphInput) const
ComPtr< ID3D12Resource > m_statsReadback
Constants BaseConstants() const
void SetGraphProgram(ID3D12GraphicsCommandList10 *list)
GpuPhysicsOptions m_options
ComPtr< ID3D12Resource > m_timestampReadback
std::vector< PhysicsBody > ReadBodies() const
std::array< ComPtr< ID3D12PipelineState >,(size_t) Pass::Count > m_pipelines
void RecordProfileStart(ID3D12GraphicsCommandList10 *list) const
void RecordSolveColors(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, Constants constants)
ComPtr< ID3D12RootSignature > CreateRootSignature(ID3D12Device *device, const RootSignatureLayout &layout)
Definition resources.cpp:93
std::expected< std::vector< std::byte >, std::string > LoadShader(std::string_view relativePath)
bool ReadBuffer(ID3D12Resource *readback, std::span< std::byte > destination)
Definition resources.cpp:52
ComPtr< ID3D12PipelineState > CreateComputePipeline(ID3D12Device *device, ID3D12RootSignature *rootSignature, std::span< const std::byte > bytecode)
D3D12_RESOURCE_BARRIER Transition(ID3D12Resource *resource, D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after)
Definition resources.cpp:65
ComPtr< ID3D12Resource > CreateBuffer(ID3D12Device *device, uint64_t sizeBytes, BufferKind kind)
Definition resources.cpp:18
void RecordCopyToReadback(ID3D12GraphicsCommandList *list, ID3D12Resource *source, ID3D12Resource *readback)
Definition resources.cpp:78
D3D12_RESOURCE_BARRIER UavBarrier(ID3D12Resource *resource)
Definition resources.cpp:74
constexpr std::array< const char *, 13 > SHADER_NAMES
constexpr std::array< const char *, 15 > PASS_NAMES
constexpr gpu::RootSignatureLayout ROOT_LAYOUT
ComPtr< ID3D12Resource > CreateUploadBuffer(ID3D12Device *device, std::span< const T > data)
physics::PxContactPoint PhysicsContactPoint
PhysicsBody MakePhysicsBody(const PhysicsSceneBody &source, int64_t rate)
physics::PxBody PhysicsBody
uint32_t SrvIndex(uint32_t shaderRegister) const
Definition resources.h:42
std::vector< PhysicsSceneBody > bodies