Bicameral Engine
GPU-resident engine for a magic-research open-world game
Loading...
Searching...
No Matches
gpu_multires.cpp
Go to the documentation of this file.
1// gpu_multires.cpp — 多重解像度の木の GPU 版(gpu_multires.h)。
2// 要求の処理は Compute の段(multires_tree.hlsl)と Work Graph(細かくする鎖の再帰・粗くする要求)、影を作る・引き戻すは Work Graph の再帰
3// (1 レベル = 1 グループ)、刻むのは Compute(待ちの丸め。1 グループ = 1 枠)か、活性だけなら Work Graph(1 ブロック = 1 グループ。T-0100)。
4// 一様なブロック(T-0102)は刻む段が頁に広げる印を付け、TreeExpand が枠の順に頁を配り、StepExpandedWaitPass / ExpandStepNode が埋めて刻む。
5// 静かで一様になった頁は、要求の処理の前に TreeFoldCheck が調べ TreeFold が枠の順に畳む(T-0103)。
6// 熱の伝導(T-0107)は Compute の段(multires_conduct.hlsl)を刻みの中で順に投げる(RecordConduction)。細かいレベルの小刻み(T-0109)は
7// 小刻みごとに段を積み、小刻みの終わりに変わったブロックの隣を活性のグラフで起こす。
8// 結び付けは shaders/sim/multires_bindings.hlsli と同じ順(u0 見出し・u1 セル・u2 端数・u3 数える欄・u4 u5 外のバッファ・
9// u6〜u10 木の管理・u11 伝導の作業場・u12 GPU の入力・u13 書き足す活性の一覧、b0、デバッグのリング、t0〜t3 表)。
10// ルート署名は 62 / 64 語(UAV 14 × 2・定数 24・リング 2・表 4 × 2。T-0107 で uint32 の表を u6 にまとめて空けた)。
11#include "sim/gpu_multires.h"
12
13#include <limits>
14#include <utility>
15
16#include "common/multires_activity.hlsli"
17
18#include "core/log.h"
19#include "gpu/resources.h"
21
22using namespace bicameral::multires;
23using namespace bicameral::reaction;
24
25namespace bicameral::sim {
26
27 namespace {
28
29 constexpr uint32_t ROOT_CONSTANT_COUNT = 25;
30 constexpr uint32_t EXTERNAL_VIEW_FIRST = 4; // u4・u5
31 constexpr uint32_t UAV_COUNT = 14; // u0〜u13
32 constexpr uint32_t ACTIVITY_VIEW = 13; // u13
34 .uavCount = UAV_COUNT, .rootConstantCount = ROOT_CONSTANT_COUNT, .debugRing = true, .srvCount = 4};
35 constexpr uint32_t TREE_THREADS = 64; // multires_tree.hlsl の TREE_THREADS
36
43
44 // multires_activity_graph.hlsl の入口
46
47 // multires_tree.hlsl の段(呼ぶ順)
48 enum TreePass : uint8_t {
55 PassQuiet, // 静かな葉を粗くする要求(要求の処理の前。T-0101)
56 PassExpand, // 一様で反応が進むブロックに頁を配る(刻んだ後。T-0102)
57 PassFoldCheck, // 静かで一様になった頁を調べる(要求の処理の前。T-0103)
58 PassFold, // 調べた頁を枠の順に畳む(T-0103)
59 PassFractions, // 伝導の端数の枠を枠の順に配る(頁を配った後。T-0107)
60 PassQuietCheck, // 静かな葉を粗くできるか調べる(PassQuiet の前。T-0113)
61 };
62 constexpr std::array<const char*, 12> TREE_SHADERS = {
63 "sim/multires_tree_resolve.cso", "sim/multires_tree_settle.cso", "sim/multires_tree_allocate.cso",
64 "sim/multires_tree_release.cso", "sim/multires_tree_clear_index.cso", "sim/multires_tree_fill_index.cso",
65 "sim/multires_tree_quiet.cso", "sim/multires_tree_expand.cso", "sim/multires_tree_fold_check.cso",
66 "sim/multires_tree_fold.cso", "sim/multires_tree_fractions.cso", "sim/multires_tree_quiet_check.cso"};
67
68 // multires_conduct.hlsl の段(呼ぶ順。T-0107)
77 constexpr std::array<const char*, 6> CONDUCT_SHADERS = {
78 "sim/multires_conduct_begin.cso", "sim/multires_conduct_mark.cso", "sim/multires_conduct_prepare.cso",
79 "sim/multires_conduct_flows.cso", "sim/multires_conduct_apply.cso", "sim/multires_conduct_end.cso"};
80
81 // ルート定数の stepFlags(multires_bindings.hlsli の MR_STEP_*)
82 constexpr uint32_t STEP_FLAG_CONDUCTION = 1;
83 constexpr uint32_t STEP_FLAG_LISTED = 2;
84 constexpr uint32_t STEP_FLAG_SUBSTEP_WAKE = 4; // 小刻みの終わりに起こす(T-0109)
85 constexpr uint32_t STEP_FLAG_WAKE_SEEDS = 16; // 起こす段が種の一覧に足す(活性の刻みの待ちの丸め。T-0124)
86 constexpr uint32_t STEP_FLAG_IMPLICIT = 8; // 細かいレベルの熱の陰解法(T-0132)
87 constexpr uint32_t STEP_IMPLICIT_GAP_SHIFT = 5; // implicitMaxGap − 1(3bit)
88 constexpr uint32_t MAX_GPU_IMPLICIT_GAP = 8;
89 constexpr uint32_t STEP_FLAG_BITS = 0xFF;
90 constexpr uint32_t STEP_SUBSTEP_SHIFT = 8;
91 constexpr uint32_t STEP_GAP_SHIFT = 14;
92 constexpr uint32_t STEP_BASE_SHIFT = 16;
93
94 // 起こす段(multires_step.hlsl の WakeDue)の 1 グループのスレッドの数
95 constexpr uint32_t WAKE_THREADS = 64;
96
97 // 陰解法の印(T-0132。multires_bindings.hlsli の MR_STEP_IMPLICIT)。陰解法の刻みは小刻みに分けない(CPU の StepConduction と同じ約束)。
98 // implicitMaxGap は 3bit に詰めるので 1〜8 だけ(0 なら陰解法のセルが無いので印を付けない。9 以上は CPU と合わない)
99 uint32_t ImplicitFlags(const MultiresStepOptions& options) {
100 if (!options.implicitConduction || options.implicitMaxGap == 0)
101 return 0;
102
103 FX_ASSERT(options.maxSubcycleGap == 0);
104 FX_ASSERT(options.implicitMaxGap <= MAX_GPU_IMPLICIT_GAP);
105 const uint32_t gap = std::min(options.implicitMaxGap, MAX_GPU_IMPLICIT_GAP);
106
107 return STEP_FLAG_IMPLICIT | ((gap - 1) << STEP_IMPLICIT_GAP_SHIFT);
108 }
109
110 // stepFlags に小刻みの番号・maxSubcycleGap・subcycleBaseLevel(符号付き 16bit)を詰める(T-0109。multires_bindings.hlsli の
111 // MR_STEP_*_SHIFT。ルート署名の語が残り少ないので定数を足さない)
112 uint32_t SubcycleFlags(uint32_t flags, const MultiresStepOptions& options, uint32_t substep) {
113 FX_ASSERT(options.maxSubcycleGap <= MULTIRES_MAX_SUBCYCLE_GAP);
114 FX_ASSERT(options.subcycleBaseLevel >= std::numeric_limits<int16_t>::min() &&
115 options.subcycleBaseLevel <= std::numeric_limits<int16_t>::max());
116 const auto base = static_cast<uint16_t>(static_cast<int16_t>(options.subcycleBaseLevel));
117
118 return (flags & STEP_FLAG_BITS) | ImplicitFlags(options) | (substep << STEP_SUBSTEP_SHIFT) |
119 (options.maxSubcycleGap << STEP_GAP_SHIFT) | (uint32_t{base} << STEP_BASE_SHIFT);
120 }
121
122 // 伝導の作業場(multires_bindings.hlsli の CONDUCT_MARK_WORDS・CONDUCT_DELTA_BYTES)
123 constexpr uint64_t CONDUCT_MARK_BYTES = 16 * sizeof(uint32_t);
124 constexpr uint64_t CONDUCT_DELTA_BYTES = 16;
125
126 // Work Graph の GPU の入力(multires_bindings.hlsli の MR_GRAPH_INPUT_*)。見出しは D3D12_NODE_GPU_INPUT そのもの
127 constexpr uint32_t GRAPH_INPUT_REFINE_HEADER = 0;
128 constexpr uint32_t GRAPH_INPUT_COARSEN_HEADER = 32;
129 constexpr uint32_t REFINE_RECORD_BYTES = 24;
130 constexpr uint32_t GRAPH_INPUT_BYTES = 64 + ((REFINE_RECORD_BYTES + 4) * (MR_MAX_REQUESTS + 1));
131 constexpr uint32_t GRAPH_INPUT_EXPAND_HEADER = (GRAPH_INPUT_BYTES + 15) & ~15u; // 頁に広げて刻む一覧(T-0102)
132 // 伝導の一覧の見出し 4 つ(埋める・流れ・足す・小刻みの終わり。同じレコード。T-0107・T-0109。ConductPassPrepare からの順)
137 static_assert(offsetof(D3D12_NODE_GPU_INPUT, EntrypointIndex) == 0);
138 static_assert(offsetof(D3D12_NODE_GPU_INPUT, NumRecords) == 4);
139 static_assert(offsetof(D3D12_NODE_GPU_INPUT, Records) == 8);
140 static_assert(sizeof(D3D12_NODE_GPU_INPUT) <= GRAPH_INPUT_COARSEN_HEADER);
141
142 // multires_graph.hlsl・multires_bindings.hlsli のレコード
144 uint32_t request;
145 uint32_t parentSlot;
146 uint32_t childSlot;
147 uint32_t levelsLeft;
148 uint32_t kind;
149 uint32_t depth;
150 };
151 static_assert(sizeof(RefineRecord) == REFINE_RECORD_BYTES);
152 static_assert(sizeof(MrRequest) == 40);
153
154 // 活性の一覧の先頭(multires_activity.hlsli の MR_ACTIVITY_*)と、観察の枠を刻むノードのレコード
156 D3D12_NODE_GPU_INPUT input;
157 uint32_t reserved;
158 uint32_t dropped;
159 };
160 static_assert(offsetof(ActivityHeader, input) + offsetof(D3D12_NODE_GPU_INPUT, NumRecords) ==
161 MR_ACTIVITY_NUM_RECORDS);
162 static_assert(offsetof(ActivityHeader, reserved) == MR_ACTIVITY_RESERVED);
163 static_assert(offsetof(ActivityHeader, dropped) == MR_ACTIVITY_DROPPED);
164 static_assert(sizeof(ActivityHeader) == MR_ACTIVITY_RECORDS);
165
166 // 活性の写しの並び: [空の一覧 0][空の一覧 1][最初の一覧](空の一覧 = 先頭 + 空のレコード)
167 constexpr uint64_t ACTIVITY_EMPTY_BYTES = sizeof(ActivityHeader) + sizeof(uint32_t);
168 constexpr uint64_t ACTIVITY_EMPTY_STRIDE = 64;
171
173 std::array<uint32_t, 3> grid;
174 uint32_t firstSlot;
175 };
176
177 // 1 回の要求の写しの大きさ(要求の一覧 + 数)
178 constexpr uint64_t REQUEST_UPLOAD_BYTES = (uint64_t{MR_MAX_REQUESTS} * sizeof(MrRequest)) + 256;
179
180 uint32_t GroupsFor(uint32_t threads, uint32_t perGroup) {
181 return std::max(1u, (threads + perGroup - 1) / perGroup);
182 }
183
184 struct ChainRecord {
185 uint32_t slot;
186 uint32_t levelsLeft;
187 };
188
189 template <typename T>
190 ComPtr<ID3D12Resource> CreateFilledUpload(ID3D12Device* device, std::span<const T> data) {
191 const auto bytes = std::as_bytes(data);
192 ComPtr<ID3D12Resource> buffer = gpu::CreateBuffer(device, bytes.size(), gpu::BufferKind::Upload);
193 void* mapped = nullptr;
194 const D3D12_RANGE noRead{.Begin = 0, .End = 0};
195 if (!buffer || FAILED(buffer->Map(0, &noRead, &mapped)))
196 return nullptr;
197
198 std::memcpy(mapped, bytes.data(), bytes.size());
199 buffer->Unmap(0, nullptr);
200
201 return buffer;
202 }
203
204 bool WriteUpload(ID3D12Resource* upload, std::span<const std::byte> bytes) {
205 void* mapped = nullptr;
206 const D3D12_RANGE noRead{.Begin = 0, .End = 0};
207 if (FAILED(upload->Map(0, &noRead, &mapped)))
208 return false;
209
210 std::memcpy(mapped, bytes.data(), bytes.size());
211 upload->Unmap(0, nullptr);
212
213 return true;
214 }
215
216 // .cso を読んで Compute のパイプラインを作る
217 std::expected<ComPtr<ID3D12PipelineState>, std::string> LoadComputePipeline(ID3D12Device5* device,
218 ID3D12RootSignature* rootSignature,
219 const char* path) {
220 const auto bytecode = gpu::LoadShader(path);
221 if (!bytecode)
222 return std::unexpected(bytecode.error());
223
224 ComPtr<ID3D12PipelineState> pipeline = gpu::CreateComputePipeline(device, rootSignature, *bytecode);
225 if (!pipeline)
226 return std::unexpected(std::format("多重解像度のパイプラインを作れない({})", path));
227
228 return pipeline;
229 }
230
231 void UavBarrier(ID3D12GraphicsCommandList10* list) {
232 const D3D12_RESOURCE_BARRIER barrier = gpu::UavBarrier(nullptr);
233 list->ResourceBarrier(1, &barrier);
234 }
235
236 } // namespace
237
238 std::expected<GpuMultires, std::string> GpuMultires::Create(ID3D12Device5* device, const BakedReactionTable& table,
239 const MultiresCapacity& capacity,
240 const GpuMultiresOptions& options) {
241 GpuMultires result;
242 result.m_capacity = capacity;
243 result.m_blockCapacity = capacity.worldBlocks + capacity.observerBlocks;
244 result.m_constants.blockCount = result.m_blockCapacity;
245 result.m_constants.rootLevel = capacity.rootLevel;
246 result.m_constants.worldBlocks = capacity.worldBlocks;
247 result.m_constants.indexEntries = capacity.indexEntries;
248 result.m_constants.ledgerColumns = capacity.ledgerColumns;
249
250 if (auto pipelines = result.CreatePipelines(device, options); !pipelines)
251 return std::unexpected(pipelines.error());
252
253 if (auto buffers = result.CreateBuffers(device, table); !buffers)
254 return std::unexpected(buffers.error());
255
256 return result;
257 }
258
259 GpuMultires::GpuMultires(GpuMultires&& other) noexcept = default;
260 GpuMultires& GpuMultires::operator=(GpuMultires&& other) noexcept = default;
261 GpuMultires::~GpuMultires() = default;
262
263 std::expected<void, std::string> GpuMultires::EnableImplicitConduction(ID3D12Device5* device,
264 const GpuMultiresImplicitLimits& limits) {
265 auto implicit = GpuMultiresImplicit::Create(device, *this, m_capacity, limits);
266 if (!implicit)
267 return std::unexpected(implicit.error());
268
269 m_implicit = std::make_unique<GpuMultiresImplicit>(std::move(*implicit));
270
271 return {};
272 }
273
275 if (m_implicit != nullptr)
276 m_implicit->StampPhases(stamp);
277 }
278
279 std::expected<void, std::string> GpuMultires::CreatePipelines(ID3D12Device5* device,
280 const GpuMultiresOptions& options) {
281 m_rootSignature = gpu::CreateRootSignature(device, ROOT_LAYOUT);
282 if (!m_rootSignature)
283 return std::unexpected("多重解像度のルート署名を作れない");
284
285 // --- Compute(起こす段・刻む・頁に広げて刻む〔待ちの丸め。T-0121〕・木の管理の段・伝導の段)---
286 auto wake = LoadComputePipeline(device, m_rootSignature.Get(), "sim/multires_step_wake.cso");
287 auto stepWait = LoadComputePipeline(device, m_rootSignature.Get(), "sim/multires_step_wait.cso");
288 auto expandedWait = LoadComputePipeline(device, m_rootSignature.Get(), "sim/multires_step_expanded_wait.cso");
289 for (const auto* loaded : {&wake, &stepWait, &expandedWait}) {
290 if (!*loaded)
291 return std::unexpected(loaded->error());
292 }
293
294 m_wakePipeline = std::move(*wake);
295 m_stepWaitPipeline = std::move(*stepWait);
296 m_stepExpandedWaitPipeline = std::move(*expandedWait);
297 static_assert(TREE_SHADERS.size() == TREE_PASS_COUNT && CONDUCT_SHADERS.size() == CONDUCT_PASS_COUNT);
298 for (uint32_t pass = 0; pass < TREE_PASS_COUNT + CONDUCT_PASS_COUNT; ++pass) {
299 const bool tree = pass < TREE_PASS_COUNT;
300 const char* path = tree ? TREE_SHADERS[pass] : CONDUCT_SHADERS[pass - TREE_PASS_COUNT];
301 auto pipeline = LoadComputePipeline(device, m_rootSignature.Get(), path);
302 if (!pipeline)
303 return std::unexpected(pipeline.error());
304
305 (tree ? m_treePipelines[pass] : m_conductPipelines[pass - TREE_PASS_COUNT]) = std::move(*pipeline);
306 }
307
308 const auto library = gpu::LoadShader("sim/multires_graph.cso");
309 if (!library)
310 return std::unexpected(library.error());
311
312 auto graph = gpu::WorkGraph::Create(device, m_rootSignature.Get(), *library, L"Multires");
313 if (!graph)
314 return std::unexpected(graph.error());
315
316 m_graph = std::make_unique<gpu::WorkGraph>(std::move(*graph));
317 m_entries = {m_graph->EntrypointIndex(L"RefineNode"), m_graph->EntrypointIndex(L"CoarsenRequestNode"),
318 m_graph->EntrypointIndex(L"PullBackNode"), m_graph->EntrypointIndex(L"RemoveShadowNode")};
319 if (std::ranges::contains(m_entries, UINT32_MAX))
320 return std::unexpected("multires_graph.cso に入口のノードが足りない");
321
322 m_constants.graphEntries = m_entries[EntryRefine] | (m_entries[EntryCoarsenRequest] << 16);
323 if (options.conductionGraph) {
324 if (auto created = CreateConductionGraph(device); !created)
325 return created;
326 }
327
328 if (!options.activity)
329 return {};
330
331 // --- 活性のグラフ(T-0100)---
332 const auto activityLibrary = gpu::LoadShader("sim/multires_activity_graph.cso");
333 if (!activityLibrary)
334 return std::unexpected(activityLibrary.error());
335
336 auto activityGraph = gpu::WorkGraph::Create(device, m_rootSignature.Get(), *activityLibrary,
337 L"MultiresActivity");
338 if (!activityGraph)
339 return std::unexpected(activityGraph.error());
340
341 m_activityGraph = std::make_unique<gpu::WorkGraph>(std::move(*activityGraph));
342 m_activityEntries = {m_activityGraph->EntrypointIndex(L"ActivitySeedNode"),
343 m_activityGraph->EntrypointIndex(L"ObserverStepNode"),
344 m_activityGraph->EntrypointIndex(L"ExpandStepNode")};
345 if (std::ranges::contains(m_activityEntries, UINT32_MAX))
346 return std::unexpected("multires_activity_graph.cso に入口のノードが足りない");
347
348 return {};
349 }
350
351 std::expected<void, std::string> GpuMultires::CreateConductionGraph(ID3D12Device5* device) {
352 const auto library = gpu::LoadShader("sim/multires_conduct_graph.cso");
353 if (!library)
354 return std::unexpected(library.error());
355
356 auto graph = gpu::WorkGraph::Create(device, m_rootSignature.Get(), *library, L"MultiresConduct");
357 if (!graph)
358 return std::unexpected(graph.error());
359
360 m_conductGraph = std::make_unique<gpu::WorkGraph>(std::move(*graph));
361 m_conductEntries = {m_conductGraph->EntrypointIndex(L"ConductPrepareNode"),
362 m_conductGraph->EntrypointIndex(L"ConductFlowsNode"),
363 m_conductGraph->EntrypointIndex(L"ConductApplyNode"),
364 m_conductGraph->EntrypointIndex(L"ConductEndNode")};
365 if (std::ranges::contains(m_conductEntries, UINT32_MAX))
366 return std::unexpected("multires_conduct_graph.cso に入口のノードが足りない");
367
368 m_useConductGraph = true;
369
370 return {};
371 }
372
373 std::expected<void, std::string> GpuMultires::CreateBuffers(ID3D12Device5* device,
374 const BakedReactionTable& table) {
375 // 0 の大きさのバッファは作れないので、最低 16 バイト
376 const std::array<uint64_t, BufferCount> sizes = BufferSizes();
377 for (uint32_t i = 0; i < BufferCount; ++i) {
378 const uint64_t bytes = std::max<uint64_t>(sizes[i], 16);
382 if (!m_buffers[i] || !m_uploads[i] || !m_readbacks[i])
383 return std::unexpected("多重解像度のバッファを作れない");
384 }
385
386 m_requestUpload = gpu::CreateBuffer(device, REQUEST_UPLOAD_BYTES * REQUEST_UPLOAD_SLOTS,
388 if (!m_requestUpload)
389 return std::unexpected("要求の写しのバッファを作れない");
390
391 // --- 活性の一覧 2 本 ---
392 for (ComPtr<ID3D12Resource>& buffer : m_activity) {
394 if (!buffer)
395 return std::unexpected("活性の一覧のバッファを作れない");
396 }
397
398 m_activityUpload = gpu::CreateBuffer(device, ACTIVITY_FIRST_OFFSET + ActivityBytes(), gpu::BufferKind::Upload);
401 return std::unexpected("活性の一覧の写しを作れない");
402
403 const D3D12_GPU_VIRTUAL_ADDRESS graphInput = m_buffers[BufferGraphInput]->GetGPUVirtualAddress();
404 m_constants.graphInputLow = static_cast<uint32_t>(graphInput);
405 m_constants.graphInputHigh = static_cast<uint32_t>(graphInput >> 32);
406
407 m_tables = {
408 CreateFilledUpload(device, std::span(table.species)), CreateFilledUpload(device, std::span(table.rules)),
409 CreateFilledUpload(device, std::span(table.ruleIndex)), CreateFilledUpload(device, std::span(table.rates))};
410 if (!std::ranges::all_of(m_tables, [](const auto& buffer) { return buffer != nullptr; }))
411 return std::unexpected("反応の表のバッファを作れない");
412
413 const D3D12_QUERY_HEAP_DESC queryDesc{
414 .Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP, .Count = MAX_TIMESTAMPS, .NodeMask = 0};
415 if (FAILED(device->CreateQueryHeap(&queryDesc, IID_PPV_ARGS(&m_timestamps))))
416 return std::unexpected("タイムスタンプのヒープを作れない");
417
418 m_timestampReadback = gpu::CreateBuffer(device, uint64_t{MAX_TIMESTAMPS} * sizeof(uint64_t),
421 return std::unexpected("タイムスタンプの読み戻しを作れない");
422
423 return {};
424 }
425
426 std::array<uint64_t, GpuMultires::BufferCount> GpuMultires::BufferSizes() const {
427 std::array<uint64_t, BufferCount> sizes{};
428 sizes[BufferBlocks] = uint64_t{m_blockCapacity} * sizeof(MrBlock);
429 sizes[BufferCells] = (m_blockCapacity + (PageCount() * MR_BLOCK_CELLS)) * sizeof(RxCell);
430 sizes[BufferFractions] = uint64_t{m_capacity.fractions} * MR_BLOCK_CELLS * sizeof(MrFraction);
431 sizes[BufferCounters] = uint64_t{MR_COUNTER_COUNT} * sizeof(uint32_t);
432 sizes[BufferTreeWords] = ((uint64_t{m_capacity.worldBlocks} * 2) + m_capacity.indexEntries + m_capacity.pages) *
433 sizeof(uint32_t);
434 sizes[BufferFreeFractions] = uint64_t{m_capacity.fractions} * sizeof(uint32_t);
435 sizes[BufferLedger] = uint64_t{MR_LEDGER_LEVELS} * m_capacity.ledgerColumns * sizeof(uint64_t);
436 sizes[BufferRequests] = uint64_t{MR_MAX_REQUESTS} * sizeof(MrRequest);
437 sizes[BufferStates] = uint64_t{MR_MAX_REQUESTS} * sizeof(MrRequestState);
438 sizes[BufferConduction] = (uint64_t{m_blockCapacity} * CONDUCT_MARK_BYTES) +
439 (PageCount() * MR_BLOCK_CELLS * CONDUCT_DELTA_BYTES);
440 sizes[BufferGraphInput] = ConductRecordsOffset() + ((uint64_t{m_blockCapacity} + 1) * sizeof(uint32_t));
441
442 return sizes;
443 }
444
445 uint64_t GpuMultires::PageCount() const {
446 return uint64_t{m_capacity.observerBlocks} + m_capacity.pages;
447 }
448
449 // 伝導の一覧のレコードの位置(multires_bindings.hlsli の ConductRecordsOffset)
451 return GRAPH_INPUT_EXPAND_RECORDS + ((uint64_t{m_capacity.worldBlocks} + 1) * sizeof(uint32_t));
452 }
453
454 // GPU の入力の最初の中身: 頁に広げて刻む一覧と伝導の一覧の見出し(入口の番号と番地は変わらない。数は GPU が書く)とレコード 0 だけ。
455 // ほかは 0(要求の処理が毎回書いてから読む)
456 std::vector<std::byte> GpuMultires::MakeGraphInputImage() const {
457 std::vector<std::byte> image(BufferSizes()[BufferGraphInput]);
458 const D3D12_GPU_VIRTUAL_ADDRESS base = m_buffers[BufferGraphInput]->GetGPUVirtualAddress();
459 const auto writeList = [&](uint32_t headerOffset, uint64_t recordsOffset, uint32_t entry) {
460 const D3D12_NODE_GPU_INPUT header{
461 .EntrypointIndex = entry,
462 .NumRecords = 1,
463 .Records = {.StartAddress = base + recordsOffset, .StrideInBytes = sizeof(uint32_t)}};
464 std::memcpy(image.data() + headerOffset, &header, sizeof(header));
465 const uint32_t emptyRecord = MR_NO_BLOCK;
466 std::memcpy(image.data() + recordsOffset, &emptyRecord, sizeof(emptyRecord));
467 };
468
469 writeList(GRAPH_INPUT_EXPAND_HEADER, GRAPH_INPUT_EXPAND_RECORDS,
470 m_activityGraph ? m_activityEntries[ActivityEntryExpand] : 0);
471 for (uint32_t i = 0; i < GRAPH_INPUT_CONDUCT_HEADERS.size(); ++i)
472 writeList(GRAPH_INPUT_CONDUCT_HEADERS[i], ConductRecordsOffset(), m_conductGraph ? m_conductEntries[i] : 0);
473
474 return image;
475 }
476
477 // u6 の中身: [世界の枠の空き][取り合いの印][索引][世界の頁の空き](CPU の木では別々の配列)
478 std::vector<std::byte> GpuMultires::MakeTreeWordsImage(const MultiresNest& nest) const {
479 const std::span<const uint32_t> freeBlocks = std::span(nest.freeBlocks).first(m_capacity.worldBlocks);
480 const std::span<const uint32_t> freePages = std::span(nest.freeBlocks).subspan(m_capacity.worldBlocks);
481 std::vector<uint32_t> words;
482 words.reserve(BufferSizes()[BufferTreeWords] / sizeof(uint32_t));
483 words.insert(words.end(), freeBlocks.begin(), freeBlocks.end());
484 words.insert(words.end(), nest.claims.begin(), nest.claims.end());
485 words.insert(words.end(), nest.index.begin(), nest.index.end());
486 words.insert(words.end(), freePages.begin(), freePages.end());
487
488 std::vector<std::byte> image(words.size() * sizeof(uint32_t));
489 std::memcpy(image.data(), words.data(), image.size());
490
491 return image;
492 }
493
494 uint64_t GpuMultires::ActivityBytes() const {
495 return MR_ACTIVITY_RECORDS + (uint64_t{MrActivityCapacity(m_capacity.worldBlocks)} * sizeof(uint32_t));
496 }
497
498 // 一覧 list の中身: 先頭 + 空のレコード + slots(写す大きさは先頭とレコードの分だけ)
499 std::vector<std::byte> GpuMultires::MakeActivityList(uint32_t list, std::span<const uint32_t> slots) const {
500 const auto count = static_cast<uint32_t>(slots.size());
501 const ActivityHeader header{
502 .input = {.EntrypointIndex = m_activityEntries[ActivityEntrySeed],
503 .NumRecords = 1 + count,
504 .Records = {.StartAddress = m_activity[list]->GetGPUVirtualAddress() + MR_ACTIVITY_RECORDS,
505 .StrideInBytes = sizeof(uint32_t)}},
506 .reserved = 1 + count,
507 .dropped = 0};
508 std::vector<uint32_t> records = {MR_NO_BLOCK};
509 records.insert(records.end(), slots.begin(), slots.end());
510
511 std::vector<std::byte> bytes(sizeof(header) + (records.size() * sizeof(uint32_t)));
512 std::memcpy(bytes.data(), &header, sizeof(header));
513 std::memcpy(bytes.data() + sizeof(header), records.data(), records.size() * sizeof(uint32_t));
514
515 return bytes;
516 }
517
518 bool GpuMultires::RecordUpload(ID3D12GraphicsCommandList10* list, const MultiresNest& nest) {
519 if (nest.blocks.size() != m_blockCapacity || nest.seeds.size() != m_capacity.worldBlocks ||
520 nest.cells.size() * sizeof(RxCell) != BufferSizes()[BufferCells] ||
521 nest.freeBlocks.size() != size_t{m_capacity.worldBlocks} + m_capacity.pages ||
522 nest.claims.size() != m_capacity.worldBlocks ||
523 nest.fractions.size() != size_t{m_capacity.fractions} * MR_BLOCK_CELLS ||
524 nest.index.size() != m_capacity.indexEntries || nest.ledger.size() != BufferSizes()[BufferLedger] / 8)
525 return false;
526
527 // 要求の途中の値・伝導の作業場(印と変化は 0 から)・GPU の入力は写さない(毎回の処理が書いてから読む)
528 const std::vector<std::byte> zeros(std::max(BufferSizes()[BufferStates], BufferSizes()[BufferConduction]));
529 const std::vector<std::byte> graphInput = MakeGraphInputImage();
530 const std::vector<std::byte> treeWords = MakeTreeWordsImage(nest);
531 const std::array<std::span<const std::byte>, BufferCount> sources = {
532 std::as_bytes(std::span(nest.blocks)),
533 std::as_bytes(std::span(nest.cells)),
534 std::as_bytes(std::span(nest.fractions)),
535 std::as_bytes(std::span(nest.counters)),
536 std::span(treeWords),
537 std::as_bytes(std::span(nest.freeFractions)),
538 std::as_bytes(std::span(nest.ledger)),
539 std::as_bytes(std::span(nest.requests)),
540 std::span(zeros).first(BufferSizes()[BufferStates]),
541 std::span(zeros).first(BufferSizes()[BufferConduction]),
542 std::span(graphInput)};
543 std::array<D3D12_RESOURCE_BARRIER, BufferCount> barriers{};
544 for (uint32_t i = 0; i < BufferCount; ++i) {
545 if (!WriteUpload(m_uploads[i].Get(), sources[i]))
546 return false;
547
548 // COMMON から暗黙に COPY_DEST へ昇格する。写した後は UAV へ
549 list->CopyResource(m_buffers[i].Get(), m_uploads[i].Get());
550 barriers[i] = gpu::Transition(m_buffers[i].Get(), D3D12_RESOURCE_STATE_COPY_DEST,
551 D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
552 }
553
554 list->ResourceBarrier(BufferCount, barriers.data());
555
556 // --- 活性の一覧: この刻みの種 = CPU の種、もう 1 本は空 ---
557 const std::vector<uint32_t> seeds = SeedSlots(nest);
558 if (seeds.size() + 1 > MrActivityCapacity(m_capacity.worldBlocks))
559 return false;
560
562 m_activityWrite = 0;
563 const std::vector<std::byte> first = MakeActivityList(0, seeds);
564 std::vector<std::byte> staging(ACTIVITY_FIRST_OFFSET);
565 for (uint32_t i = 0; i < ACTIVITY_LISTS; ++i) {
566 const std::vector<std::byte> empty = MakeActivityList(i, {});
567 std::ranges::copy(empty, staging.begin() + static_cast<ptrdiff_t>(i * ACTIVITY_EMPTY_STRIDE));
568 }
569
570 staging.insert(staging.end(), first.begin(), first.end());
571 if (!WriteUpload(m_activityUpload.Get(), staging))
572 return false;
573
574 list->CopyBufferRegion(m_activity[0].Get(), 0, m_activityUpload.Get(), ACTIVITY_FIRST_OFFSET, first.size());
575 list->CopyBufferRegion(m_activity[1].Get(), 0, m_activityUpload.Get(), ACTIVITY_EMPTY_STRIDE,
576 ACTIVITY_EMPTY_BYTES);
577 const std::array<D3D12_RESOURCE_BARRIER, ACTIVITY_LISTS> toUav = {
578 gpu::Transition(m_activity[0].Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS),
579 gpu::Transition(m_activity[1].Get(), D3D12_RESOURCE_STATE_COPY_DEST,
580 D3D12_RESOURCE_STATE_UNORDERED_ACCESS)};
581 list->ResourceBarrier(ACTIVITY_LISTS, toUav.data());
582
583 return true;
584 }
585
586 // --- 結び付けと起動 ---
587
588 void GpuMultires::BindRoot(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing) const {
589 static_assert(sizeof(RootConstants) == ROOT_CONSTANT_COUNT * sizeof(uint32_t));
590 static_assert(BufferCount + 3 == UAV_COUNT); // u4・u5 は外のバッファ、u13 は活性の一覧
591 static_assert((UAV_COUNT * 2) + ROOT_CONSTANT_COUNT + 2 + (TABLE_COUNT * 2) <= 64); // ルート署名の語
592 for (uint32_t i = 0; i < BufferCount; ++i) {
593 const uint32_t parameter = i < EXTERNAL_VIEW_FIRST ? i : i + 2;
594 list->SetComputeRootUnorderedAccessView(parameter, m_buffers[i]->GetGPUVirtualAddress());
595 }
596
597 const D3D12_GPU_VIRTUAL_ADDRESS standIn = m_buffers[BufferCells]->GetGPUVirtualAddress();
598 for (uint32_t i = 0; i < m_externalViews.size(); ++i) {
599 const D3D12_GPU_VIRTUAL_ADDRESS view = m_externalViews[i] != 0 ? m_externalViews[i] : standIn;
600 list->SetComputeRootUnorderedAccessView(EXTERNAL_VIEW_FIRST + i, view);
601 }
602
603 list->SetComputeRootUnorderedAccessView(ACTIVITY_VIEW, m_activity[m_activityWrite]->GetGPUVirtualAddress());
604 list->SetComputeRoot32BitConstants(ROOT_LAYOUT.RootConstantIndex(), ROOT_CONSTANT_COUNT, &m_constants, 0);
605 list->SetComputeRootUnorderedAccessView(ROOT_LAYOUT.DebugRingIndex(), debugRing);
606 for (uint32_t i = 0; i < TABLE_COUNT; ++i)
607 list->SetComputeRootShaderResourceView(ROOT_LAYOUT.SrvIndex(i), m_tables[i]->GetGPUVirtualAddress());
608 }
609
610 void GpuMultires::SetGraphProgram(ID3D12GraphicsCommandList10* list) {
611 list->SetComputeRootSignature(m_rootSignature.Get());
612 m_graph->SetProgram(list, !m_graphInitialized);
613 m_graphInitialized = true;
614 }
615
616 void GpuMultires::SetActivityProgram(ID3D12GraphicsCommandList10* list) {
617 list->SetComputeRootSignature(m_rootSignature.Get());
620 }
621
622 void GpuMultires::DispatchGraph(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
623 uint32_t entry, const void* record, uint32_t recordBytes) {
624 SetGraphProgram(list);
625 BindRoot(list, debugRing);
626 gpu::WorkGraph::DispatchFromCpu(list, m_entries[entry], record, 1, recordBytes);
627 UavBarrier(list);
628 }
629
630 void GpuMultires::RecordTreePass(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
631 uint32_t pass, uint32_t groupCount) {
632 list->SetComputeRootSignature(m_rootSignature.Get());
633 list->SetPipelineState(m_treePipelines[pass].Get());
634 BindRoot(list, debugRing);
635 list->Dispatch(groupCount, 1, 1);
636 UavBarrier(list);
637 }
638
639 // --- 世界の木 ---
640
641 bool GpuMultires::RecordRequests(ID3D12GraphicsCommandList10* list, std::span<const MrRequest> requests) {
642 if (requests.size() > MR_MAX_REQUESTS)
643 return false;
644
645 // --- 写しの枠に [数][要求の一覧] を書く ---
646 const uint64_t offset = REQUEST_UPLOAD_BYTES * (m_requestUploadCursor++ % REQUEST_UPLOAD_SLOTS);
647 const auto count = static_cast<uint32_t>(requests.size());
648 void* mapped = nullptr;
649 const D3D12_RANGE noRead{.Begin = 0, .End = 0};
650 if (FAILED(m_requestUpload->Map(0, &noRead, &mapped)))
651 return false;
652
653 auto* bytes = static_cast<std::byte*>(mapped) + offset;
654 std::memcpy(bytes, &count, sizeof(count));
655 std::memcpy(bytes + 256, requests.data(), requests.size_bytes());
656 m_requestUpload->Unmap(0, nullptr);
657
658 // --- 一覧と数える欄の「要求の数」へ写す ---
659 ID3D12Resource* requestBuffer = m_buffers[BufferRequests].Get();
660 ID3D12Resource* counters = m_buffers[BufferCounters].Get();
661 const std::array<D3D12_RESOURCE_BARRIER, 2> toCopy = {
662 gpu::Transition(requestBuffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST),
663 gpu::Transition(counters, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST)};
664 list->ResourceBarrier(static_cast<UINT>(toCopy.size()), toCopy.data());
665 if (count > 0)
666 list->CopyBufferRegion(requestBuffer, 0, m_requestUpload.Get(), offset + 256, requests.size_bytes());
667
668 list->CopyBufferRegion(counters, uint64_t{MR_COUNTER_REQUESTS} * sizeof(uint32_t), m_requestUpload.Get(),
669 offset, sizeof(uint32_t));
670 const std::array<D3D12_RESOURCE_BARRIER, 2> toUav = {
671 gpu::Transition(requestBuffer, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS),
672 gpu::Transition(counters, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS)};
673 list->ResourceBarrier(static_cast<UINT>(toUav.size()), toUav.data());
674
675 return true;
676 }
677
678 void GpuMultires::RecordFoldPages(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
679 uint64_t tick) {
680 RecordFoldPages(list, debugRing, tick, MrExactFoldTolerance());
681 }
682
683 void GpuMultires::RecordFoldPages(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
684 uint64_t tick, const MrFoldTolerance& tolerance) {
685 m_constants.foldTolerance = MrPackFoldTolerance(tolerance);
686 m_constants.tickLow = static_cast<uint32_t>(tick);
687 m_constants.tickHigh = static_cast<uint32_t>(tick >> 32);
688 RecordTreePass(list, debugRing, PassFoldCheck, std::max(1u, m_capacity.worldBlocks));
689 RecordTreePass(list, debugRing, PassFold, 1);
690 }
691
692 void GpuMultires::RecordQuietRequests(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
693 uint64_t tick) {
694 RecordQuietRequests(list, debugRing, tick, MrExactFoldTolerance());
695 }
696
697 void GpuMultires::RecordQuietRequests(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
698 uint64_t tick, const MrFoldTolerance& tolerance) {
699 m_constants.foldTolerance = MrPackFoldTolerance(tolerance);
700 m_constants.tickLow = static_cast<uint32_t>(tick);
701 m_constants.tickHigh = static_cast<uint32_t>(tick >> 32);
702 RecordTreePass(list, debugRing, PassQuietCheck, std::max(1u, m_capacity.worldBlocks));
703 RecordTreePass(list, debugRing, PassQuiet, 1);
704 }
705
706 void GpuMultires::RecordProcessRequests(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing) {
707 m_activityWrite = m_activityCurrent; // 木を変えたブロックはこの刻みの種へ
708 const uint32_t requestGroups = GroupsFor(MR_MAX_REQUESTS, TREE_THREADS);
709 RecordTreePass(list, debugRing, PassResolve, requestGroups);
710 RecordTreePass(list, debugRing, PassSettle, requestGroups);
711 RecordTreePass(list, debugRing, PassAllocate, 1);
712
713 // --- 適用(Work Graph。GPU の入力は SRV の状態で読む)---
714 ID3D12Resource* graphInput = m_buffers[BufferGraphInput].Get();
715 const D3D12_RESOURCE_BARRIER toInput = gpu::Transition(graphInput, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
716 D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
717 list->ResourceBarrier(1, &toInput);
718 SetGraphProgram(list);
719 BindRoot(list, debugRing);
720 gpu::WorkGraph::DispatchFromGpu(list, graphInput->GetGPUVirtualAddress() + GRAPH_INPUT_REFINE_HEADER);
721 UavBarrier(list);
722 gpu::WorkGraph::DispatchFromGpu(list, graphInput->GetGPUVirtualAddress() + GRAPH_INPUT_COARSEN_HEADER);
723 UavBarrier(list);
724 const D3D12_RESOURCE_BARRIER toUav = gpu::Transition(graphInput, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
725 D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
726 list->ResourceBarrier(1, &toUav);
727
728 // --- 解放と索引の作り直し(印がある時だけ働く)---
729 RecordTreePass(list, debugRing, PassRelease, 1);
730 RecordTreePass(list, debugRing, PassClearIndex, GroupsFor(m_capacity.indexEntries, TREE_THREADS));
731 RecordTreePass(list, debugRing, PassFillIndex, GroupsFor(m_capacity.worldBlocks, TREE_THREADS));
732 }
733
734 // --- 観察の枠 ---
735
736 void GpuMultires::RecordRefineShadow(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
737 uint32_t parentSlot, uint32_t firstChildSlot, uint32_t levelCount,
738 const MultiresPoint& point) {
739 const std::array<int64_t, 3> coordinates = {point.x, point.y, point.z};
740 for (size_t axis = 0; axis < coordinates.size(); ++axis) {
741 const auto value = static_cast<uint64_t>(coordinates[axis]);
742 m_constants.point[axis * 2] = static_cast<uint32_t>(value);
743 m_constants.point[(axis * 2) + 1] = static_cast<uint32_t>(value >> 32);
744 }
745
746 m_constants.pointLevel = point.level;
747 const RefineRecord record{.request = MR_NO_BLOCK,
748 .parentSlot = parentSlot,
749 .childSlot = firstChildSlot,
750 .levelsLeft = levelCount,
751 .kind = MR_BLOCK_SHADOW,
752 .depth = 0};
753 DispatchGraph(list, debugRing, EntryRefine, &record, sizeof(record));
754 }
755
756 void GpuMultires::RecordRemoveShadow(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
757 uint32_t firstSlot, uint32_t levelCount) {
758 const ChainRecord record{.slot = firstSlot, .levelsLeft = levelCount};
759 DispatchGraph(list, debugRing, EntryRemoveShadow, &record, sizeof(record));
760 }
761
762 void GpuMultires::RecordPullBack(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
763 uint32_t firstShadowSlot, uint32_t levelCount) {
764 const ChainRecord record{.slot = firstShadowSlot, .levelsLeft = levelCount};
765 DispatchGraph(list, debugRing, EntryPullBack, &record, sizeof(record));
766 }
767
768 void GpuMultires::SetTick(uint64_t worldSeed, uint64_t tick) {
769 m_constants.seedLow = static_cast<uint32_t>(worldSeed);
770 m_constants.seedHigh = static_cast<uint32_t>(worldSeed >> 32);
771 m_constants.tickLow = static_cast<uint32_t>(tick);
772 m_constants.tickHigh = static_cast<uint32_t>(tick >> 32);
773 }
774
775 bool GpuMultires::RecordStepActive(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
776 uint64_t worldSeed, uint64_t tick, const MultiresStepOptions& options) {
777 if (!m_activityGraph)
778 return false;
779
780 SetTick(worldSeed, tick);
781 m_constants.stepFlags = options.conduction ? STEP_FLAG_CONDUCTION | STEP_FLAG_LISTED : 0;
782
783 // --- 起こす段がつつかれたブロックの印を直し、起こす刻みが来たブロックをこの刻みの種の一覧へ足す(CPU の StepActive の種。
784 // 次の刻みの種は書かない: 刻んだブロックは見出しの wakeTick で起こす。T-0124)---
786 RecordWake(list, debugRing, STEP_FLAG_WAKE_SEEDS);
787
788 const uint32_t next = m_activityCurrent ^ 1u;
789 ID3D12Resource* input = m_activity[m_activityCurrent].Get();
790 ID3D12Resource* output = m_activity[next].Get();
791
792 // --- 次の刻みの一覧を空にし、この刻みの一覧を GPU の入力の状態へ ---
793 const std::array<D3D12_RESOURCE_BARRIER, 2> before = {
794 gpu::Transition(output, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST),
795 gpu::Transition(input, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
796 D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE)};
797 list->ResourceBarrier(static_cast<UINT>(before.size()), before.data());
798 list->CopyBufferRegion(output, 0, m_activityUpload.Get(), uint64_t{next} * ACTIVITY_EMPTY_STRIDE,
799 ACTIVITY_EMPTY_BYTES);
800 const D3D12_RESOURCE_BARRIER outputToUav = gpu::Transition(output, D3D12_RESOURCE_STATE_COPY_DEST,
801 D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
802 list->ResourceBarrier(1, &outputToUav);
803
804 // --- 種 → 面の隣 → 刻む(Work Graph)。観察の枠は全部刻む。伝導を入れるなら、グラフは伝導の一覧に足すだけで、刻むのは伝導の段
805 // (使い終わったこの刻みの種の一覧は、小刻みの終わりに起こす一覧に使い回す。T-0109)---
806 m_activityWrite = next;
807 if (options.conduction)
808 RecordConductPass(list, debugRing, ConductPassBegin, 1);
809
810 SetActivityProgram(list);
811 BindRoot(list, debugRing);
812 gpu::WorkGraph::DispatchFromGpu(list, input->GetGPUVirtualAddress());
813 UavBarrier(list);
814 const D3D12_RESOURCE_BARRIER inputToUav = gpu::Transition(input, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
815 D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
816 list->ResourceBarrier(1, &inputToUav);
817 if (options.conduction) {
818 RecordConduction(list, debugRing, options, m_activityCurrent);
819 } else {
820 if (m_capacity.observerBlocks > 0) {
821 const ObserverRecord record{.grid = {m_capacity.observerBlocks, 1, 1},
822 .firstSlot = m_capacity.worldBlocks};
823 gpu::WorkGraph::DispatchFromCpu(list, m_activityEntries[ActivityEntryObserver], &record, 1,
824 sizeof(record));
825 UavBarrier(list);
826 }
827
828 RecordExpandPages(list, debugRing); // 一様で反応が進むブロック(T-0102)
829 }
830
831 m_activityCurrent = next;
832
833 return true;
834 }
835
836 void GpuMultires::RecordStep(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
837 uint64_t worldSeed, uint64_t tick, const MultiresStepOptions& options) {
838 SetTick(worldSeed, tick);
839 m_constants.stepFlags = options.conduction ? STEP_FLAG_CONDUCTION : 0;
840 if (options.conduction) {
841 RecordWake(list, debugRing, 0); // つつかれたブロックの印を直す
842 RecordConduction(list, debugRing, options, ACTIVITY_LISTS); // 全部を刻むので起こさない
843 return;
844 }
845
846 RecordStepWait(list, debugRing);
847 }
848
849 void GpuMultires::RecordWake(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
850 uint32_t extraFlags) {
851 const uint32_t flags = std::exchange(m_constants.stepFlags, m_constants.stepFlags | extraFlags);
852 list->SetComputeRootSignature(m_rootSignature.Get());
853 list->SetPipelineState(m_wakePipeline.Get());
854 BindRoot(list, debugRing);
855 list->Dispatch(GroupsFor(m_blockCapacity, WAKE_THREADS), 1, 1);
856 UavBarrier(list);
857 m_constants.stepFlags = flags;
858 }
859
860 void GpuMultires::RecordStepWait(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing) {
861 // --- 起こす段(つつかれたブロックの印を直す)→ 全部の枠を刻む ---
862 RecordWake(list, debugRing, 0);
863 list->SetPipelineState(m_stepWaitPipeline.Get());
864 BindRoot(list, debugRing);
865 list->Dispatch(std::max(1u, m_blockCapacity), 1, 1);
866 UavBarrier(list);
867
868 // --- 一様で反応が進むブロックを頁に広げて刻む(数は GPU が決めるので、世界の枠の数だけグループを投げる)---
869 RecordTreePass(list, debugRing, PassExpand, 1);
870 list->SetPipelineState(m_stepExpandedWaitPipeline.Get());
871 list->Dispatch(std::max(1u, m_capacity.worldBlocks), 1, 1);
872 UavBarrier(list);
873 }
874
875 void GpuMultires::RecordConductPass(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
876 uint32_t pass, uint32_t groupCount) {
877 list->SetComputeRootSignature(m_rootSignature.Get());
878 list->SetPipelineState(m_conductPipelines[pass].Get());
879 BindRoot(list, debugRing);
880 list->Dispatch(groupCount, 1, 1);
881 UavBarrier(list);
882 }
883
884 // 熱の伝導を入れた刻みの段(CPU の StepBlocks・StepConduction と同じ順。multires_conduct.hlsli の先頭)。活性(stepFlags の LISTED)なら
885 // 伝導の一覧のブロックだけ(数は GPU が決めるので、全部の枠の数だけグループを投げる。一覧は活性のグラフが作り終えている)。
886 // 細かいレベルの刻み(T-0109): 小刻みを最大回数 4^maxSubcycleGap ぶん積む(Work Graphs に全体の同期が無いので小刻みごとに段を分ける。
887 // その小刻みに始まるレベルが無ければ段は空で抜ける)。wakeList = 小刻みの終わりに起こす一覧に使う活性の一覧(全部を刻むなら ACTIVITY_LISTS)
888 void GpuMultires::RecordConduction(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
889 const MultiresStepOptions& options, uint32_t wakeList) {
890 FX_ASSERT(options.maxSubcycleGap <= MULTIRES_MAX_SUBCYCLE_GAP);
891 const uint32_t flags = m_constants.stepFlags;
892 const uint32_t substeps = 1u << (2 * options.maxSubcycleGap);
893 for (uint32_t substep = 0; substep < substeps; ++substep) {
894 m_constants.stepFlags = SubcycleFlags(flags, options, substep);
895 RecordConductSubstep(list, debugRing, options);
896 if (substep + 1 < substeps)
897 RecordSubstepEnd(list, debugRing, wakeList);
898 }
899
900 // --- 細かいレベルの熱の陰解法(T-0132。最後の小刻みの流れの後。CPU の StepConduction の AddImplicitConduction)---
901 if (options.implicitConduction)
902 RecordImplicitConduction(list, debugRing, options);
903
904 // --- 最後の小刻みの変化を足して反応 ---
905 RecordConductStage(list, debugRing, ConductPassApply);
906 m_constants.stepFlags = flags;
907 }
908
909 // 陰解法の段(sim/gpu_multires_implicit)。刻みの印と stepFlags(最後の小刻み)はこの刻みのまま
910 void GpuMultires::RecordImplicitConduction(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
911 const MultiresStepOptions& options) {
912 FX_ASSERT(m_implicit != nullptr);
913 if (m_implicit == nullptr) {
914 Log(Channel::Gpu, Level::Error, "陰解法の段が無い(EnableImplicitConduction を呼んでいない)");
915 return;
916 }
917
918 m_implicit->Record(list, debugRing, *this, options);
919 }
920
921 // 小刻み 1 回: 印 → 頁 → 端数の枠 → 埋める →(陰解法の刻みなら系に入れるブロックを選ぶ。T-0178)→ 流れ(その小刻みに始まるレベルのブロックだけ)
922 void GpuMultires::RecordConductSubstep(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
923 const MultiresStepOptions& options) {
924 RecordConductPass(list, debugRing, ConductPassMark, std::max(1u, m_blockCapacity));
925 RecordTreePass(list, debugRing, PassExpand, 1);
926 RecordTreePass(list, debugRing, PassFractions, 1);
927 RecordConductStage(list, debugRing, ConductPassPrepare);
928 if ((m_constants.stepFlags & STEP_FLAG_IMPLICIT) != 0 && m_implicit != nullptr)
929 m_implicit->RecordAdmit(list, debugRing, *this, options);
930
931 RecordConductStage(list, debugRing, ConductPassFlows);
932 }
933
934 // 小刻みの終わり(T-0109。CPU の ApplyEndingDeltas・WakeChangedBlocks): 終わるレベルに変化を足す(ConductEnd)。活性なら、変わった
935 // ブロックの面の隣を活性のグラフ(ActivitySeedNode → WakeFaceNode → ActivityStepNode)で起こし、初めて印を付けたブロックを伝導の一覧に足す。
936 // 起こす一覧はこの刻みの種の一覧(使い終わっている)を空にして u13 に結び、ConductEnd に書かせる
937 void GpuMultires::RecordSubstepEnd(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
938 uint32_t wakeList) {
939 if (wakeList >= ACTIVITY_LISTS) {
940 RecordConductStage(list, debugRing, ConductPassEnd);
941 return;
942 }
943
944 // --- 起こす一覧を空にして u13 に結び、変化を足す ---
945 ID3D12Resource* wake = m_activity[wakeList].Get();
946 const D3D12_RESOURCE_BARRIER toCopy = gpu::Transition(wake, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
947 D3D12_RESOURCE_STATE_COPY_DEST);
948 list->ResourceBarrier(1, &toCopy);
949 list->CopyBufferRegion(wake, 0, m_activityUpload.Get(), uint64_t{wakeList} * ACTIVITY_EMPTY_STRIDE,
950 ACTIVITY_EMPTY_BYTES);
951 const D3D12_RESOURCE_BARRIER toUav = gpu::Transition(wake, D3D12_RESOURCE_STATE_COPY_DEST,
952 D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
953 list->ResourceBarrier(1, &toUav);
954 const uint32_t write = std::exchange(m_activityWrite, wakeList);
955 RecordConductStage(list, debugRing, ConductPassEnd);
956 m_activityWrite = write;
957
958 // --- 面の隣を起こす(GPU の入力 = 起こす一覧)---
959 const D3D12_RESOURCE_BARRIER toInput = gpu::Transition(wake, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
960 D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
961 list->ResourceBarrier(1, &toInput);
962 const uint32_t flags = std::exchange(m_constants.stepFlags, m_constants.stepFlags | STEP_FLAG_SUBSTEP_WAKE);
963 SetActivityProgram(list);
964 BindRoot(list, debugRing);
965 gpu::WorkGraph::DispatchFromGpu(list, wake->GetGPUVirtualAddress());
966 UavBarrier(list);
967 m_constants.stepFlags = flags;
968 const D3D12_RESOURCE_BARRIER back = gpu::Transition(wake, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
969 D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
970 list->ResourceBarrier(1, &back);
971 }
972
973 // 伝導の段 1 つ(埋める・流れ・足す・小刻みの終わり)。活性で Work Graph 版を使うなら GPU の入力 = 伝導の一覧(段ごとに見出しが別)、
974 // ほかは Compute
975 void GpuMultires::RecordConductStage(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
976 uint32_t pass) {
977 // 陰解法の段(T-0132)は流れの後に伝導の一覧を足すが、Work Graph の見出しの数は TreeFractions が写した後なので、足す段は Compute で
978 const bool listed = (m_constants.stepFlags & STEP_FLAG_LISTED) != 0;
979 const bool implicitApply = pass == ConductPassApply && (m_constants.stepFlags & STEP_FLAG_IMPLICIT) != 0;
980 if (!m_useConductGraph || !listed || implicitApply) {
981 RecordConductPass(list, debugRing, pass, std::max(1u, m_blockCapacity));
982 return;
983 }
984
985 ID3D12Resource* graphInput = m_buffers[BufferGraphInput].Get();
986 const D3D12_RESOURCE_BARRIER toInput = gpu::Transition(graphInput, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
987 D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
988 list->ResourceBarrier(1, &toInput);
989 list->SetComputeRootSignature(m_rootSignature.Get());
990 m_conductGraph->SetProgram(list, !m_conductGraphInitialized);
992 BindRoot(list, debugRing);
993 const uint32_t header = GRAPH_INPUT_CONDUCT_HEADERS[pass - ConductPassPrepare];
994 gpu::WorkGraph::DispatchFromGpu(list, graphInput->GetGPUVirtualAddress() + header);
995 UavBarrier(list);
996 const D3D12_RESOURCE_BARRIER toUav = gpu::Transition(graphInput, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
997 D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
998 list->ResourceBarrier(1, &toUav);
999 }
1000
1002 if (use && !m_conductGraph)
1003 return false;
1004
1005 m_useConductGraph = use;
1006
1007 return true;
1008 }
1009
1010 void GpuMultires::RecordExpandPass(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing) {
1011 RecordTreePass(list, debugRing, PassExpand, 1);
1012 }
1013
1014 void GpuMultires::RecordExpandPages(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing) {
1015 // --- 頁を配る(枠の順)→ 埋めて刻む(Work Graph の GPU の入力)---
1016 RecordTreePass(list, debugRing, PassExpand, 1);
1017 ID3D12Resource* graphInput = m_buffers[BufferGraphInput].Get();
1018 const D3D12_RESOURCE_BARRIER toInput = gpu::Transition(graphInput, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
1019 D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
1020 list->ResourceBarrier(1, &toInput);
1021 SetActivityProgram(list);
1022 BindRoot(list, debugRing);
1023 gpu::WorkGraph::DispatchFromGpu(list, graphInput->GetGPUVirtualAddress() + GRAPH_INPUT_EXPAND_HEADER);
1024 UavBarrier(list);
1025 const D3D12_RESOURCE_BARRIER toUav = gpu::Transition(graphInput, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
1026 D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
1027 list->ResourceBarrier(1, &toUav);
1028 }
1029
1030 // --- 外のバッファとパイプライン ---
1031
1032 void GpuMultires::SetExternalViews(D3D12_GPU_VIRTUAL_ADDRESS first, D3D12_GPU_VIRTUAL_ADDRESS second) {
1033 m_externalViews = {first, second};
1034 }
1035
1036 void GpuMultires::RecordExternalDispatch(ID3D12GraphicsCommandList10* list, D3D12_GPU_VIRTUAL_ADDRESS debugRing,
1037 ID3D12PipelineState* pipeline, uint32_t groupCount,
1038 const std::array<uint32_t, 4>& external) {
1039 m_constants.external = external;
1040
1041 list->SetComputeRootSignature(m_rootSignature.Get());
1042 list->SetPipelineState(pipeline);
1043 BindRoot(list, debugRing);
1044 list->Dispatch(groupCount, 1, 1);
1045 UavBarrier(list);
1046 }
1047
1048 // --- 読み戻しと計測 ---
1049
1050 void GpuMultires::RecordReadback(ID3D12GraphicsCommandList10* list) {
1051 // 状態と索引(要求・途中の値・伝導の作業場・GPU の入力は読まない)
1052 for (uint32_t i = 0; i <= BufferLedger; ++i)
1053 gpu::RecordCopyToReadback(list, m_buffers[i].Get(), m_readbacks[i].Get());
1054
1056
1057 if (m_timestampCount > 0) {
1058 list->ResolveQueryData(m_timestamps.Get(), D3D12_QUERY_TYPE_TIMESTAMP, 0, m_timestampCount,
1059 m_timestampReadback.Get(), 0);
1060 }
1061 }
1062
1064 if (nest.blocks.size() != m_blockCapacity || nest.index.size() != m_capacity.indexEntries)
1066
1067 std::vector<uint32_t> treeWords(BufferSizes()[BufferTreeWords] / sizeof(uint32_t));
1068 const std::array<std::span<std::byte>, BufferLedger + 1> destinations = {
1069 std::as_writable_bytes(std::span(nest.blocks)), std::as_writable_bytes(std::span(nest.cells)),
1070 std::as_writable_bytes(std::span(nest.fractions)), std::as_writable_bytes(std::span(nest.counters)),
1071 std::as_writable_bytes(std::span(treeWords)), std::as_writable_bytes(std::span(nest.freeFractions)),
1072 std::as_writable_bytes(std::span(nest.ledger))};
1073 for (uint32_t i = 0; i < destinations.size(); ++i) {
1074 if (!gpu::ReadBuffer(m_readbacks[i].Get(), destinations[i]))
1075 return false;
1076 }
1077
1078 // --- u6 を CPU の配列に分ける(取り合いの印は読まない: 要求の処理の外では全部 MR_NO_CLAIM)---
1079 const uint32_t worldBlocks = m_capacity.worldBlocks;
1080 const auto words = std::span(treeWords);
1081 const auto index = words.subspan(size_t{worldBlocks} * 2, m_capacity.indexEntries);
1082 const auto freePages = words.subspan((size_t{worldBlocks} * 2) + m_capacity.indexEntries);
1083 std::ranges::copy(words.first(worldBlocks), nest.freeBlocks.begin());
1084 std::ranges::copy(freePages, nest.freeBlocks.begin() + worldBlocks);
1085 std::ranges::copy(index, nest.index.begin());
1086
1087 return true;
1088 }
1089
1090 std::optional<GpuMultires::ActivitySeeds> GpuMultires::ReadSeeds() const {
1091 std::vector<std::byte> bytes(ActivityBytes());
1092 if (!gpu::ReadBuffer(m_activityReadback.Get(), bytes))
1093 return std::nullopt;
1094
1095 ActivityHeader header{};
1096 std::memcpy(&header, bytes.data(), sizeof(header));
1097 const uint32_t capacity = MrActivityCapacity(m_capacity.worldBlocks);
1098 if (header.input.NumRecords == 0 || header.input.NumRecords > capacity)
1099 return std::nullopt;
1100
1101 ActivitySeeds seeds{.slots = std::vector<uint32_t>(header.input.NumRecords - 1), .dropped = header.dropped};
1102 std::memcpy(seeds.slots.data(), bytes.data() + sizeof(header) + sizeof(uint32_t),
1103 seeds.slots.size() * sizeof(uint32_t));
1104 std::ranges::sort(seeds.slots);
1105 const auto duplicates = std::ranges::unique(seeds.slots);
1106 seeds.slots.erase(duplicates.begin(), duplicates.end());
1107
1108 return seeds;
1109 }
1110
1111 void GpuMultires::RecordTimestamp(ID3D12GraphicsCommandList10* list, uint32_t index) {
1112 if (index >= MAX_TIMESTAMPS)
1113 return;
1114
1115 list->EndQuery(m_timestamps.Get(), D3D12_QUERY_TYPE_TIMESTAMP, index);
1116 m_timestampCount = std::max(m_timestampCount, index + 1);
1117 }
1118
1119 std::vector<uint64_t> GpuMultires::ReadTimestamps(uint32_t count) const {
1120 std::vector<uint64_t> ticks(std::min(count, m_timestampCount));
1121 if (!gpu::ReadBuffer(m_timestampReadback.Get(), std::as_writable_bytes(std::span(ticks))))
1122 ticks.clear();
1123
1124 return ticks;
1125 }
1126
1127} // 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)
static std::expected< GpuMultiresImplicit, std::string > Create(ID3D12Device5 *device, const GpuMultires &multires, const MultiresCapacity &capacity, const GpuMultiresImplicitLimits &limits)
static constexpr uint32_t CONDUCT_PASS_COUNT
std::array< uint32_t, 4 > m_entries
ComPtr< ID3D12RootSignature > m_rootSignature
GpuMultires & operator=(GpuMultires &&other) noexcept
void StampImplicitPhases(bool stamp)
void RecordQuietRequests(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, uint64_t tick)
ComPtr< ID3D12Resource > m_timestampReadback
void BindRoot(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing) const
void SetActivityProgram(ID3D12GraphicsCommandList10 *list)
ComPtr< ID3D12QueryHeap > m_timestamps
std::unique_ptr< gpu::WorkGraph > m_conductGraph
void SetTick(uint64_t worldSeed, uint64_t tick)
static constexpr uint32_t TREE_PASS_COUNT
uint64_t ConductRecordsOffset() const
void RecordExpandPages(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing)
ComPtr< ID3D12PipelineState > m_wakePipeline
static constexpr uint32_t TABLE_COUNT
std::array< ComPtr< ID3D12Resource >, BufferCount > m_readbacks
bool RecordRequests(ID3D12GraphicsCommandList10 *list, std::span< const multires::MrRequest > requests)
void RecordWake(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, uint32_t extraFlags)
std::unique_ptr< gpu::WorkGraph > m_activityGraph
void RecordFoldPages(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, uint64_t tick)
void RecordConduction(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, const MultiresStepOptions &options, uint32_t wakeList)
std::array< ComPtr< ID3D12Resource >, TABLE_COUNT > m_tables
void RecordStepWait(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing)
std::vector< uint64_t > ReadTimestamps(uint32_t count) const
void SetGraphProgram(ID3D12GraphicsCommandList10 *list)
void SetExternalViews(D3D12_GPU_VIRTUAL_ADDRESS first, D3D12_GPU_VIRTUAL_ADDRESS second)
void RecordConductStage(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, uint32_t pass)
void RecordRefineShadow(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, uint32_t parentSlot, uint32_t firstChildSlot, uint32_t levelCount, const MultiresPoint &point)
static constexpr uint32_t MAX_TIMESTAMPS
bool Read(MultiresNest &nest) const
std::vector< std::byte > MakeTreeWordsImage(const MultiresNest &nest) const
std::array< uint64_t, BufferCount > BufferSizes() const
void RecordPullBack(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, uint32_t firstShadowSlot, uint32_t levelCount)
void DispatchGraph(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, uint32_t entry, const void *record, uint32_t recordBytes)
ComPtr< ID3D12Resource > m_activityReadback
ComPtr< ID3D12PipelineState > m_stepExpandedWaitPipeline
std::vector< std::byte > MakeActivityList(uint32_t list, std::span< const uint32_t > slots) const
std::array< ComPtr< ID3D12Resource >, BufferCount > m_uploads
void RecordImplicitConduction(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, const MultiresStepOptions &options)
ComPtr< ID3D12Resource > m_requestUpload
std::array< ComPtr< ID3D12Resource >, BufferCount > m_buffers
std::array< ComPtr< ID3D12Resource >, ACTIVITY_LISTS > m_activity
void RecordConductPass(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, uint32_t pass, uint32_t groupCount)
void RecordExternalDispatch(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, ID3D12PipelineState *pipeline, uint32_t groupCount, const std::array< uint32_t, 4 > &external)
MultiresCapacity m_capacity
ComPtr< ID3D12PipelineState > m_stepWaitPipeline
static constexpr uint32_t ACTIVITY_LISTS
void RecordRemoveShadow(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, uint32_t firstSlot, uint32_t levelCount)
std::vector< std::byte > MakeGraphInputImage() const
void RecordTimestamp(ID3D12GraphicsCommandList10 *list, uint32_t index)
std::array< uint32_t, 3 > m_activityEntries
void RecordStep(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, uint64_t worldSeed, uint64_t tick, const MultiresStepOptions &options={})
std::expected< void, std::string > CreateBuffers(ID3D12Device5 *device, const BakedReactionTable &table)
std::expected< void, std::string > CreatePipelines(ID3D12Device5 *device, const GpuMultiresOptions &options)
bool RecordUpload(ID3D12GraphicsCommandList10 *list, const MultiresNest &nest)
ComPtr< ID3D12Resource > m_activityUpload
void RecordExpandPass(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing)
bool RecordStepActive(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, uint64_t worldSeed, uint64_t tick, const MultiresStepOptions &options={})
static std::expected< GpuMultires, std::string > Create(ID3D12Device5 *device, const BakedReactionTable &table, const MultiresCapacity &capacity, const GpuMultiresOptions &options={})
std::unique_ptr< GpuMultiresImplicit > m_implicit
void RecordSubstepEnd(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, uint32_t wakeList)
std::unique_ptr< gpu::WorkGraph > m_graph
void RecordConductSubstep(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, const MultiresStepOptions &options)
std::array< uint32_t, 4 > m_conductEntries
std::optional< ActivitySeeds > ReadSeeds() const
void RecordProcessRequests(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing)
void RecordReadback(ID3D12GraphicsCommandList10 *list)
std::expected< void, std::string > EnableImplicitConduction(ID3D12Device5 *device, const GpuMultiresImplicitLimits &limits)
std::expected< void, std::string > CreateConductionGraph(ID3D12Device5 *device)
std::array< ComPtr< ID3D12PipelineState >, TREE_PASS_COUNT > m_treePipelines
std::array< ComPtr< ID3D12PipelineState >, CONDUCT_PASS_COUNT > m_conductPipelines
std::array< D3D12_GPU_VIRTUAL_ADDRESS, 2 > m_externalViews
static constexpr uint32_t REQUEST_UPLOAD_SLOTS
GpuMultires(GpuMultires &&other) noexcept
void RecordTreePass(ID3D12GraphicsCommandList10 *list, D3D12_GPU_VIRTUAL_ADDRESS debugRing, uint32_t pass, uint32_t groupCount)
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
ComPtr< ID3D12Resource > CreateFilledUpload(ID3D12Device *device, std::span< const T > data)
constexpr std::array< const char *, 12 > TREE_SHADERS
bool WriteUpload(ID3D12Resource *upload, std::span< const std::byte > bytes)
uint32_t ImplicitFlags(const MultiresStepOptions &options)
void UavBarrier(ID3D12GraphicsCommandList10 *list)
uint32_t GroupsFor(uint32_t threads, uint32_t perGroup)
constexpr std::array< const char *, 6 > CONDUCT_SHADERS
constexpr std::array< uint32_t, 4 > GRAPH_INPUT_CONDUCT_HEADERS
uint32_t SubcycleFlags(uint32_t flags, const MultiresStepOptions &options, uint32_t substep)
std::expected< ComPtr< ID3D12PipelineState >, std::string > LoadComputePipeline(ID3D12Device5 *device, ID3D12RootSignature *rootSignature, const char *path)
constexpr gpu::RootSignatureLayout ROOT_LAYOUT
std::vector< uint32_t > SeedSlots(const MultiresNest &nest)
constexpr uint32_t MULTIRES_MAX_SUBCYCLE_GAP
MultiresNest MakeMultiresNest(const MultiresCapacity &capacity)
MultiresImplicitLimits GpuMultiresImplicitLimits
void Log(Channel channel, Level level, FormatWithLocation< std::type_identity_t< Args >... > pattern, Args &&... args)
Definition log.h:140
uint32_t SrvIndex(uint32_t shaderRegister) const
Definition resources.h:42
std::vector< uint32_t > ruleIndex
std::vector< reaction::RxSpecies > species
std::vector< reaction::RxRule > rules
std::vector< uint8_t > seeds
std::vector< reaction::RxCell > cells
std::vector< uint32_t > index
std::vector< multires::MrBlock > blocks
std::vector< uint32_t > claims
std::vector< multires::MrFraction > fractions
std::vector< uint32_t > freeFractions
std::vector< uint64_t > ledger
std::vector< multires::MrRequest > requests
std::vector< uint32_t > freeBlocks
std::array< uint32_t, multires::MR_COUNTER_COUNT > counters