Bicameral Engine
GPU-resident engine for a magic-research open-world game
Loading...
Searching...
No Matches
gpu_physics.h
Go to the documentation of this file.
1// gpu_physics.h — 整数の AVBD の 1 刻み(08 §2)を GPU の Compute で走らせる(T-0090)。
2// パスは shaders/sim/physics_step.hlsl、物ごと・組ごとの手順は shaders/common/physics_step.hlsli(CPU の sim::PhysicsWorld と共通)。
3// CPU の世界とビット一致することを tests/gpu_physics_test.cpp が確かめる。Work Graphs 版と測って比べるのは T-0092。
4//
5// 使い方:
6// auto physics = GpuPhysics::Create(device, scene, PxDefaultParameters());
7// physics->RecordInitialize(list, debugRingAddress); // 初めの 1 回
8// physics->RecordStep(list, debugRingAddress); // 1 刻み(何刻みでも続けて記録してよい)
9// physics->RecordReadback(list); → 投げて待つ → physics->ReadBodies() / ReadStats()
10// 使わない色(どの物もその色でない)の解の Dispatch は述語(SetPredication)で飛ばす(T-0094。GpuPhysicsOptions::skipEmptyColors)。
11// 計測(T-0092): EnableProfiling の後は、パスごとにタイムスタンプを打ち、RecordReadback で読み戻しに入れる。
12// リストが終わってから AccumulateProfile → ProfileResult でパスの種類ごとの GPU 時間。
13// debugRingAddress は debug のビルドでシェーダーの FX_ASSERT が書くデバッグのリング(gpu::DebugRing。必ず結ぶ)。
14#pragma once
15
16#include <array>
17#include <cstdint>
18#include <expected>
19#include <memory>
20#include <span>
21#include <string>
22#include <vector>
23
24#include "gpu/com_ptr.h"
25#include "gpu/work_graph.h"
26#include "sim/physics_world.h"
27
28namespace bicameral::sim {
29
30 // 色ごとの解き方(T-0092 で測って Compute にした。ADR-0002「計測」)
31 enum class GpuPhysicsSolver : uint8_t {
32 Compute, // 色ごとに Dispatch、1 グループ = 1 物、1 スレッド = 1 接触点(physics_step.hlsl の SolveColor)
33 Graph, // 同じ解き方を、色ごとの物の一覧を入力にした Work Graph で(physics_graph.hlsl の SolveBodyNode)
34 };
35
36 // 数は固定(M1 の原理の確認。足りなければ GpuPhysicsStats::overflow に印が付く)
38 uint32_t slotsPerBody = 16; // 持ち主の物 1 つあたりの組の枠(PX_GPU_MAX_SLOTS_PER_BODY まで)
39 uint32_t incidentPerBody = 24; // 動く物 1 つあたりの、入っている組の数
40 uint32_t colorRounds = 24; // 彩色(Jones-Plassmann)の回数
41
42 // 広域の選別 → 接触の幾何を Work Graph(physics_graph.hlsl)で走らせる。false なら Compute の 2 パス(T-0092。ADR-0002「計測」)
43 bool broadphaseGraph = true;
45
46 // どの物も使わない色の解の Dispatch を述語(SetPredication)で飛ばす(T-0094。solver = Compute のとき)
47 bool skipEmptyColors = true;
48 };
49
50 // shaders/sim/physics_step.hlsl の統計の並び(g_stats)
52 int64_t maxPenetration = 0; // 2^-20 m
53 uint32_t contactCount = 0;
54 uint32_t colorCount = 0;
55 uint32_t solveFailures = 0;
56 uint32_t overflow = 0; // 1 枠 / 2 一覧 / 4 塗れなかった物 / 8 色が多すぎる。0 でなければ結果は信用できない
57 };
58 static_assert(sizeof(GpuPhysicsStats) == 24);
59
60 // パスの種類ごとの GPU 時間の合計(計測。T-0092)。時間はタイムスタンプの刻み(キューの GetTimestampFrequency で割る。
61 // シミュのコードに浮動小数点を置かないため、ミリ秒にするのは呼ぶ側)
63 const char* name = "";
64 uint64_t count = 0; // 投げた回数
65 uint64_t timestampTicks = 0;
66 };
67
68 // 記録の進み(CPU 側の控え)。巻き戻し(T-0143)で保存点と一緒に写し、戻すときに戻す
70 uint64_t tick = 0;
71 uint32_t currentHalf = 1; // 今の小刻みの組の枠
72 };
73
74 class GpuPhysics {
75 public:
76 [[nodiscard]] static std::expected<GpuPhysics, std::string> Create(ID3D12Device5* device,
77 const PhysicsScene& scene,
78 const physics::PxParameters& parameters,
79 const GpuPhysicsOptions& options = {});
80
81 void RecordInitialize(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing);
82 void RecordStep(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing);
83 void RecordReadback(ID3D12GraphicsCommandList10* list) const;
84
85 // RecordReadback を含むリストが終わってから
86 [[nodiscard]] std::vector<PhysicsBody> ReadBodies() const;
87 [[nodiscard]] GpuPhysicsStats ReadStats() const;
88
89 [[nodiscard]] uint64_t Tick() const { return m_tick; }
90 [[nodiscard]] uint32_t BodyCount() const { return m_bodyCount; }
91
92 // 物のバッファ(PhysicsBody × BodyCount。physics_bindings.hlsli の u0)。仮の世界の刻みが押す・ハッシュ・抽出で読み書きする(T-0098)
93 [[nodiscard]] ID3D12Resource* Bodies() const { return m_uavs[0].Get(); }
94
95 // --- 巻き戻し(T-0143。sim/probe_save_point.cpp)---
96 // 刻みをまたいで残る GPU の状態(u0〜u13。一時的なものも含めて全部写す。リストの間は COMMON)
97 [[nodiscard]] std::span<const ComPtr<ID3D12Resource>> StateBuffers() const { return m_uavs; }
98 [[nodiscard]] GpuPhysicsCursor Cursor() const { return {.tick = m_tick, .currentHalf = m_half}; }
99 void SetCursor(const GpuPhysicsCursor& cursor) {
100 m_tick = cursor.tick;
101 m_half = cursor.currentHalf;
102 }
103
104 // --- 計測(T-0092)---
105 [[nodiscard]] bool EnableProfiling(ID3D12Device* device);
106 void AccumulateProfile();
107 [[nodiscard]] std::vector<GpuPhysicsPassTime> ProfileResult() const;
108
109 // 物の状態のハッシュ(PhysicsWorld::StateHash と同じ式)
110 [[nodiscard]] static uint64_t StateHash(uint64_t tick, const std::vector<PhysicsBody>& bodies);
111
112 private:
113 // shaders/sim/physics_step.hlsl の入口(.cso の名前と同じ順)
114 enum class Pass : uint8_t {
127 BuildManifolds, // Work Graph の後で組を作る(broadphaseGraph のとき)
128 BroadphaseGraph, // Work Graph(Pipeline は無い。m_graph)
129 SolveColorGraph, // Work Graph(GpuPhysicsSolver::Graph)
131 };
132
133 // b0 のルート定数(physics_step.hlsl の RootConstants と同じ順)
134 struct Constants {
135 uint32_t bodyCount = 0;
136 uint32_t slotsPerBody = 0;
137 uint32_t incidentPerBody = 0;
138 uint32_t currentHalf = 0;
139 uint32_t tickLow = 0;
140 uint32_t tickHigh = 0;
141 uint32_t frictionQ16 = 0;
142 int32_t color = 0;
143 uint32_t alphaQ16 = 0;
144 uint32_t colorIn = 0;
145 uint32_t resetStats = 0;
146 uint32_t solveEntry = 0;
147 uint32_t colorBodiesLow = 0;
148 uint32_t colorBodiesHigh = 0;
149 };
150
151 [[nodiscard]] std::expected<void, std::string> CreatePipelines(ID3D12Device5* device);
152 [[nodiscard]] std::expected<void, std::string> CreateBuffers(ID3D12Device5* device, const PhysicsScene& scene);
153 void RecordSubstep(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, uint32_t substep);
154 void RecordColoring(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
155 Constants constants);
156 void RecordIterations(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
157 Constants constants);
158 void RecordSolveColors(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
159 Constants constants);
160 void RecordColorPredicateStates(ID3D12GraphicsCommandList10* list, bool toPredication) const;
161 [[nodiscard]] bool SkipsEmptyColors() const {
162 return m_options.skipEmptyColors && m_options.solver == GpuPhysicsSolver::Compute;
163 }
164 void RecordPass(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, Pass pass,
165 const Constants& constants, uint32_t threadCount);
166 void RecordSolveColorGraph(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
167 const Constants& constants, int32_t color);
168 void RecordColorListStates(ID3D12GraphicsCommandList10* list, bool toGraphInput) const;
169 void SetGraphProgram(ID3D12GraphicsCommandList10* list);
170 void RecordBroadphaseGraph(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
171 const Constants& constants);
172 void BindRoot(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
173 const Constants& constants) const;
174 void RecordProfileStart(ID3D12GraphicsCommandList10* list) const;
175 void RecordTimestamp(ID3D12GraphicsCommandList10* list, Pass pass);
176 [[nodiscard]] Constants BaseConstants() const;
177
178 physics::PxParameters m_parameters{};
180 uint32_t m_bodyCount = 0;
181 uint32_t m_frictionQ16 = 0;
182 uint64_t m_tick = 0;
183 uint32_t m_half = 1; // 今の小刻みの組の枠(小刻みの初めに入れ替える)
184
185 ComPtr<ID3D12RootSignature> m_rootSignature;
186 std::array<ComPtr<ID3D12PipelineState>, (size_t)Pass::Count> m_pipelines; // BroadphaseGraph は空
187 std::unique_ptr<gpu::WorkGraph> m_graph; // broadphaseGraph か solver == Graph のときだけ(physics_graph.hlsl)
188 uint32_t m_broadphaseEntry = 0;
189 uint32_t m_solveEntry = 0;
190 bool m_graphInitialized = false; // 裏のメモリを初期化する SetProgram を記録したか
191 bool m_graphProgramSet = false; // 今のリストの状態がグラフか(Compute のパスが PSO に戻す)
192 std::array<ComPtr<ID3D12Resource>, 14> m_uavs; // u0〜u13(physics_bindings.hlsli の結び付け)
193 std::array<ComPtr<ID3D12Resource>, 2> m_srvs; // t0 パラメータ・t1 初めの物
194 ComPtr<ID3D12Resource> m_bodiesReadback;
195 ComPtr<ID3D12Resource> m_statsReadback;
196
197 // --- 計測(T-0092)---
198 ComPtr<ID3D12QueryHeap> m_timestamps; // [0] リストの始め、[1 + n] n 番目のパスの後
199 ComPtr<ID3D12Resource> m_timestampReadback;
200 std::vector<Pass> m_profiledPasses; // 今のリストで打ったパスの順
201 std::array<uint64_t, (size_t)Pass::Count> m_profileCounts{};
202 std::array<uint64_t, (size_t)Pass::Count> m_profileTicks{};
203 };
204
205} // namespace bicameral::sim
std::unique_ptr< gpu::WorkGraph > m_graph
std::span< const ComPtr< ID3D12Resource > > StateBuffers() const
Definition gpu_physics.h:97
void SetCursor(const GpuPhysicsCursor &cursor)
Definition gpu_physics.h:99
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)
GpuPhysicsCursor Cursor() const
Definition gpu_physics.h:98
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)
uint32_t BodyCount() const
Definition gpu_physics.h:90
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
ID3D12Resource * Bodies() const
Definition gpu_physics.h:93
Constants BaseConstants() const
void SetGraphProgram(ID3D12GraphicsCommandList10 *list)
GpuPhysicsOptions m_options
ComPtr< ID3D12Resource > m_timestampReadback
uint64_t Tick() const
Definition gpu_physics.h:89
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)