Bicameral Engine
GPU-resident engine for a magic-research open-world game
Loading...
Searching...
No Matches
gpu_implicit.h
Go to the documentation of this file.
1// gpu_implicit.h — 細かいレベルの熱の陰解法(方式②。ADR-0019)の GPU 版(T-0117)。CPU リファレンス sim/implicit_conduction の
2// StepImplicit(多重格子の V サイクル・誤差の見込みで止める・安全網)と毎刻みビット一致する Compute の段(shaders/sim/implicit_conduct.hlsl)を記録する。
3// 試作の約束は CPU と同じ(セルの一覧・熱容量一定)。木につないだ系(T-0119 の multires_implicit_conduction が作る、刻みの初めの温度と
4// 粗い側の端数の枠つきのセル)もそのまま解ける(T-0127)。系のセル・面・面の一覧は GPU で作れる(GpuImplicitBuild。T-0129)。GPU の伝導の段から呼ぶ形は GpuMultiresImplicit(T-0132)。
5// 多重格子の段の形(節・隣・重み・親子)は CPU の BuildImplicitGrid が作ったものを写す(RecordUpload)か、GPU で作った段
6// (GpuImplicitLevels。T-0134)を写す(RecordCopyLevels。段の数・節の始まりと数の表も写す)。
7// 大きさは上限(GpuImplicitLimits)で決め、段の形・数は GPU のバッファ(計画 u9)から読む(T-0136): 刻みの初めに ImPlanLevels・ImPlanArgs が
8// 長い行の節の一覧・ImTail の境・間接の Dispatch の引数を作り、V サイクルは記録の上限の段(GpuImplicitTuning::dispatchLevels)まで
9// ExecuteIndirect で積む(その段が無い・下りが止まった段より下なら引数が 0 グループ)。CPU は段の数も ImTail の境も知らない。
10//
11// 使い方(テスト):
12// auto gpu = GpuImplicit::Create(device, limits); // または Create(device, grid)(上限 = その系の大きさ)
13// gpu->RecordUpload(list, grid); // CPU の系を写す。GPU の系なら RecordReset → RecordCopySystem・RecordCopyLevels
14// gpu->RecordStep(list, ring, options); // 1 刻み。何回でも積める
15// gpu->RecordReadback(list); → 投げて待つ → gpu->Read(grid, cost);
16// V サイクルの回数と安全網の回数は GPU が決める。記録は上限(options.cycles・maxLimitRounds)の回数だけ積み、要らない回は
17// 述語(SetPredication。GPU が書いた「止めた」の語)で Dispatch ごと飛ばす。
18#pragma once
19
20#include <array>
21#include <cstdint>
22#include <expected>
23#include <span>
24#include <string>
25#include <vector>
26
27#include "gpu/com_ptr.h"
29
30namespace bicameral::sim {
31
32 // GPU の 1 刻みの結果(CPU の ImplicitCost のうち、GPU が数えるもの)
34 uint32_t cycles = 0; // 回した V サイクルの数
35 uint32_t limitedCells = 0; // 安全網で陽解法に戻したセルの数
36 uint32_t limitRounds = 0; // 安全網を繰り返した回数
37 int64_t worstExcessMillikelvin = 0; // 安全網の前に範囲を超えた最大
38 bool limitFinished = false; // 安全網が記録した回数の中で止まった(false なら maxLimitRounds が足りない)
39
40 // --- GPU が決めた段の形(計測の表に使う。T-0136)---
41 uint32_t levelCount = 0; // 段の数
42 uint32_t tailDepth = 0; // ImTail が受け持つ最初の段(段の数なら受け持たない)
43 uint32_t wantedTailDepth = 0; // 記録の形で切る前の ImTail の境(次の刻みの形を選ぶのに使う。T-0154)
44 };
45
46 // 大きさの上限(バッファの大きさ。T-0136)。写す系と段はこれに収まること
48 uint32_t cells = 0;
49 uint32_t faces = 0;
50 uint32_t nodes = 0; // 全部の段の節
51 uint32_t links = 0; // 全部の段の隣
52 uint32_t levels = 64; // 段の数(CPU の MAX_GRID_LEVELS)
53 };
54
55 // 段の分け方の調整(計測で選ぶ。既定は T-0120 の値、T-0127 で木につないだ場面で測り直した。docs/perf.md)
57 // 節の数がこれ以下で、隣がこれ以下の段から下は、1 グループ(implicit_conduct.hlsl の IM_TAIL_THREADS)で
58 // V サイクルを回す(ImTail。T-0120)。ImTail は 1 スレッドで隣を回すので、長い行のある段は入れない。
59 // 2048 節にすると試作の鎖(レベル 3〜6)が 1 刻み 1.64 → 1.97 ms と重くなった(1 グループに仕事が寄りすぎる)
60 uint32_t tailMaxNodes = 1024;
61 uint32_t tailMaxLinks = 32;
62 // 上の条件に合わない時も、最も粗い段は節がこれ以下なら(隣の数によらず)ImTail で回す。超えたら掃き出しの Dispatch で回す。
63 // 木のたくさんの要求の場面の最も粗い段(3396 節・隣 64)を ImTail に入れると 1 刻み 9.4 ms、入れないと 8.1 ms(T-0127。docs/perf.md)。
64 // 試作の熱い点(最も粗い段が小さく隣が多い)は ImTail の方が軽かった(T-0120)
65 uint32_t coarsestTailMaxNodes = 1024;
66 // 段ごとの Dispatch(ExecuteIndirect)を積む段の数(T-0136)。段の数と ImTail の境は GPU が決めるので、記録はこの段まで積み、
67 // 要らない段は 0 グループの Dispatch になる(バリアは残る)。下りがこれより深くなる系は、ここから下を ImTail が受け持つ(値は同じ・遅くなりうる)
68 uint32_t dispatchLevels = 8;
69 };
70
71 // 1 刻みの記録の形(T-0154)。段の数と ImTail の境は GPU が決めるので、CPU が積んだ段ごとの Dispatch のうち要らないものは
72 // 0 グループの Dispatch とバリアになり、それが小さい場面の費用の大半になる(述語で飛ばした回もバリアは残る)。
73 // 形を変えても解いた値は同じ(積まなかった段は ImTail の 1 グループが同じ順で回す)。変わるのは費用だけなので、
74 // 前の刻みの GPU の数(GpuImplicitCost)から選んでよい(ShapeFrom。遅れて読んだ数でもよい)。
76 // 段ごとの Dispatch を積む段の数(1〜Create の dispatchLevels。0 なら Create の値)。下りがこれより深い系はそこから下を ImTail が回す
77 uint32_t dispatchLevels = 0;
78 // 最も粗い段の掃き出しを Dispatch で積むか(ImTail に入らない大きい最も粗い段の時だけ要る)。false なら ImTail が受け持つ
79 bool coarsestDispatch = true;
80 };
81
83 public:
84 static constexpr uint32_t MAX_TIMESTAMPS = 64;
85 static constexpr uint32_t DEFAULT_MAX_LIMIT_ROUNDS = 16;
86
87 [[nodiscard]] static std::expected<GpuImplicit, std::string> Create(ID3D12Device5* device,
88 const GpuImplicitLimits& limits,
89 const GpuImplicitTuning& tuning = {});
90
91 // 上限 = grid の大きさ(段ごとの Dispatch は grid の段の数まで)
92 [[nodiscard]] static std::expected<GpuImplicit, std::string> Create(ID3D12Device5* device,
93 const ImplicitGrid& grid,
94 const GpuImplicitTuning& tuning = {});
95
96 [[nodiscard]] static GpuImplicitLimits LimitsOf(const ImplicitGrid& grid);
97
98 // 前の刻みの数から記録の形を選ぶ(T-0154): 段は下りが止まった段(切る前の ImTail の境)まで積み、
99 // 最も粗い段の掃き出しは前の刻みが ImTail を使わなかった時だけ積む
100 [[nodiscard]] static GpuImplicitRecordShape ShapeFrom(const GpuImplicitCost& previous);
101
102 // CPU の系(セル・面・段の形と段の表)を写す。grid は上限に収まること
103 [[nodiscard]] bool RecordUpload(ID3D12GraphicsCommandList* list, const ImplicitGrid& grid);
104
105 // GPU の系を写す前に、バッファを UAV にする(中身は RecordCopySystem・RecordCopyLevels が写す)
106 void RecordReset(ID3D12GraphicsCommandList* list);
107
108 // GPU が作った系(GpuImplicitBuild。T-0129)のセル・面・セルの面の一覧と面の数を写す(RecordUpload か RecordReset の後。
109 // source・header は COPY_SOURCE の状態で、並びはこの系と同じ: セル・面・面の一覧〔面の数 × 2〕。面の数は header の faceCountOffset の 1 語)。
110 // 上限の大きさ(source の終わりまで)を写す
111 void RecordCopySystem(ID3D12GraphicsCommandList* list, ID3D12Resource* source, uint64_t cellsOffset,
112 uint64_t facesOffset, uint64_t listsOffset, ID3D12Resource* header,
113 uint64_t faceCountOffset);
114
115 // GPU が作った多重格子の段(GpuImplicitLevels。T-0134)の段の表(header の先頭。implicit_levels.hlsl の見出し)・節・隣・子の一覧を写す
116 // (RecordUpload か RecordReset の後。どれも COPY_SOURCE の状態で、並びはこの系と同じ)
117 void RecordCopyLevels(ID3D12GraphicsCommandList* list, ID3D12Resource* header, ID3D12Resource* nodes,
118 ID3D12Resource* links, ID3D12Resource* children);
119
120 // 1 刻み。options.method は Multigrid だけ(赤黒・RKL2 は CPU の比べる相手で、GPU には載せない)
121 [[nodiscard]] bool RecordStep(ID3D12GraphicsCommandList* list, uint64_t debugRing,
122 const ImplicitOptions& options,
123 uint32_t maxLimitRounds = DEFAULT_MAX_LIMIT_ROUNDS,
124 const GpuImplicitRecordShape& shape = {});
125
126 // バッファを COMMON に戻す(RecordStep の後。同じリストで次の刻みに RecordReset をもう一度呼ぶため。T-0132)
127 void RecordRelease(ID3D12GraphicsCommandList* list);
128
129 // 解いたセル(ImGpuCell の並び。RecordStep の後は UAV の状態。伝導の段が変化を読む。T-0132)
130 [[nodiscard]] ID3D12Resource* CellsBuffer() const { return m_buffers[BufferCells].Get(); }
131
132 void RecordTimestamp(ID3D12GraphicsCommandList* list, uint32_t index);
133 void RecordReadback(ID3D12GraphicsCommandList* list);
134 // 数(V の回数・安全網・段の形)だけを読み戻す(セルは写さない。次の刻みの記録の形を選ぶ用。T-0132)
135 void RecordCostReadback(ID3D12GraphicsCommandList* list);
136
137 // 読み戻したセルのエネルギー・端数を grid に、最後の刻みの数を cost に
138 [[nodiscard]] bool Read(ImplicitGrid& grid, GpuImplicitCost& cost) const;
139 [[nodiscard]] bool ReadCost(GpuImplicitCost& cost) const;
140 [[nodiscard]] std::vector<uint64_t> ReadTimestamps(uint32_t count) const;
141
142 // V サイクル 1 回に積む Dispatch の数(0 グループのものも含む。最後に RecordStep した形で。計測の表に使う)
143 [[nodiscard]] uint32_t DispatchesPerCycle(const ImplicitOptions& options) const;
144
145 // 要らない回を述語で飛ばすか(既定 true。false なら段が空で抜けるだけ。計測で比べる用)
146 void UsePredication(bool use) { m_predication = use; }
147
148 private:
163
182
183 // implicit_conduct.hlsl の cbuffer ImConstants と同じ並び
184 struct Constants {
185 uint32_t maxNodes = 0;
186 uint32_t maxCells = 0;
187 uint32_t maxFaces = 0;
188 uint32_t depth = 0;
189 uint32_t dispatchLevels = 0;
190 uint32_t color = 0;
191 uint32_t tolerance = 0;
192 uint32_t slack = 0;
193 int32_t correctionScale = 0;
194 uint32_t tailMaxNodes = 0;
195 uint32_t tailMaxLinks = 0;
197 uint32_t maxLevels = 0;
198 uint32_t coarsestDispatch = 1;
199 uint32_t unused1 = 0;
200 uint32_t sweeps = 0;
201 };
202
203 // 写す 1 つ(RecordCopies。大きさは写す先と source の残りで切る)
204 struct CopyRegion {
206 uint64_t targetOffset = 0;
207 ID3D12Resource* source = nullptr;
208 uint64_t sourceOffset = 0;
209 uint64_t bytes = 0;
210 };
211
212 GpuImplicit() = default;
213
214 std::expected<void, std::string> CreatePipelines(ID3D12Device5* device);
215 std::expected<void, std::string> CreateBuffers(ID3D12Device5* device);
216 [[nodiscard]] uint64_t BufferBytes(Buffer buffer) const;
217 void Dispatch(ID3D12GraphicsCommandList* list, Pass pass, uint32_t groups);
218 void DispatchIndirect(ID3D12GraphicsCommandList* list, Pass pass, uint32_t slot);
219 void RecordPlan(ID3D12GraphicsCommandList* list);
220 void RecordCopies(ID3D12GraphicsCommandList* list, std::span<const CopyRegion> regions);
221 void RecordSmooth(ID3D12GraphicsCommandList* list, uint32_t depth, uint32_t slot, uint32_t sweeps);
222 void RecordVCycle(ID3D12GraphicsCommandList* list, const ImplicitOptions& options);
223 void BeginSkippable(ID3D12GraphicsCommandList* list, uint32_t word);
224 void EndSkippable(ID3D12GraphicsCommandList* list);
225
226 // --- 上限 ---
230 uint32_t m_maxDispatchLevels = 1; // Create で決めた段ごとの Dispatch の段の上限(引数のバッファの大きさ)
231 bool m_predication = true;
232
233 // --- GPU ---
234 ComPtr<ID3D12RootSignature> m_rootSignature;
235 ComPtr<ID3D12CommandSignature> m_dispatchSignature; // ExecuteIndirect の Dispatch だけの引数
236 std::array<ComPtr<ID3D12PipelineState>, PassCount> m_pipelines;
237 std::array<ComPtr<ID3D12Resource>, BufferCount> m_buffers;
238 std::array<ComPtr<ID3D12Resource>, BufferCount> m_uploads;
239 ComPtr<ID3D12Resource> m_cellsReadback;
240 ComPtr<ID3D12Resource> m_stateReadback;
241 ComPtr<ID3D12Resource> m_wideReadback;
242 ComPtr<ID3D12Resource> m_planReadback; // 計画の見出し(段の数・ImTail の境)
243 ComPtr<ID3D12QueryHeap> m_timestamps;
244 ComPtr<ID3D12Resource> m_timestampReadback;
245 uint32_t m_timestampCount = 0;
246 };
247
248} // namespace bicameral::sim
std::vector< uint64_t > ReadTimestamps(uint32_t count) const
ComPtr< ID3D12QueryHeap > m_timestamps
ComPtr< ID3D12Resource > m_wideReadback
static constexpr uint32_t DEFAULT_MAX_LIMIT_ROUNDS
ComPtr< ID3D12Resource > m_cellsReadback
void RecordCopies(ID3D12GraphicsCommandList *list, std::span< const CopyRegion > regions)
bool ReadCost(GpuImplicitCost &cost) const
void RecordSmooth(ID3D12GraphicsCommandList *list, uint32_t depth, uint32_t slot, uint32_t sweeps)
std::expected< void, std::string > CreatePipelines(ID3D12Device5 *device)
ComPtr< ID3D12Resource > m_planReadback
void RecordVCycle(ID3D12GraphicsCommandList *list, const ImplicitOptions &options)
static constexpr uint32_t MAX_TIMESTAMPS
bool RecordUpload(ID3D12GraphicsCommandList *list, const ImplicitGrid &grid)
ComPtr< ID3D12RootSignature > m_rootSignature
void RecordRelease(ID3D12GraphicsCommandList *list)
std::expected< void, std::string > CreateBuffers(ID3D12Device5 *device)
static std::expected< GpuImplicit, std::string > Create(ID3D12Device5 *device, const GpuImplicitLimits &limits, const GpuImplicitTuning &tuning={})
void RecordCopySystem(ID3D12GraphicsCommandList *list, ID3D12Resource *source, uint64_t cellsOffset, uint64_t facesOffset, uint64_t listsOffset, ID3D12Resource *header, uint64_t faceCountOffset)
static GpuImplicitRecordShape ShapeFrom(const GpuImplicitCost &previous)
uint32_t DispatchesPerCycle(const ImplicitOptions &options) const
void RecordCopyLevels(ID3D12GraphicsCommandList *list, ID3D12Resource *header, ID3D12Resource *nodes, ID3D12Resource *links, ID3D12Resource *children)
void Dispatch(ID3D12GraphicsCommandList *list, Pass pass, uint32_t groups)
bool Read(ImplicitGrid &grid, GpuImplicitCost &cost) const
ComPtr< ID3D12CommandSignature > m_dispatchSignature
void BeginSkippable(ID3D12GraphicsCommandList *list, uint32_t word)
void RecordPlan(ID3D12GraphicsCommandList *list)
ComPtr< ID3D12Resource > m_timestampReadback
void RecordReadback(ID3D12GraphicsCommandList *list)
void RecordReset(ID3D12GraphicsCommandList *list)
void RecordCostReadback(ID3D12GraphicsCommandList *list)
void EndSkippable(ID3D12GraphicsCommandList *list)
ID3D12Resource * CellsBuffer() const
void RecordTimestamp(ID3D12GraphicsCommandList *list, uint32_t index)
ComPtr< ID3D12Resource > m_stateReadback
static GpuImplicitLimits LimitsOf(const ImplicitGrid &grid)
std::array< ComPtr< ID3D12PipelineState >, PassCount > m_pipelines
std::array< ComPtr< ID3D12Resource >, BufferCount > m_buffers
std::array< ComPtr< ID3D12Resource >, BufferCount > m_uploads
GpuImplicitTuning m_tuning
void DispatchIndirect(ID3D12GraphicsCommandList *list, Pass pass, uint32_t slot)
bool RecordStep(ID3D12GraphicsCommandList *list, uint64_t debugRing, const ImplicitOptions &options, uint32_t maxLimitRounds=DEFAULT_MAX_LIMIT_ROUNDS, const GpuImplicitRecordShape &shape={})
uint64_t BufferBytes(Buffer buffer) const
GpuImplicitLimits m_limits