Bicameral Engine
GPU-resident engine for a magic-research open-world game
Loading...
Searching...
No Matches
multires_tree.cpp
Go to the documentation of this file.
1// multires_tree.cpp — 多重解像度の世界の木の管理の CPU リファレンス(multires_nest.h。17 §5「木の管理」。T-0018。ADR-0016)。
2// 要求の処理の段(Resolve・Settle・Allocate・Apply・Release・索引の作り直し)を、GPU(shaders/sim/multires_tree.hlsl の段と
3// multires_graph.hlsl の鎖)と同じ順・同じ関数で行う。GPU は段の中を並列に走らせるが、1 刻みに 1 つの親は 1 つの要求だけが触るので
4// 書き込みは重ならず、結果は要求の順に 1 つずつ行うこの実装と同じになる。
5//
6// 枠の番号(ADR-0016): 要求 i は空きのスタックの上から「i より前の許可した要求の数の和」だけ下から順に取る。返すのも要求の順に積む。
7// 索引は開番地法(線形探査)。入れる = 空の所(墓石は使い回さない)、消す = 墓石。表の中の並びは GPU と違ってよい。
8#include <algorithm>
9
10#include "common/multires_activity.hlsli"
11#include "sim/multires_nest.h"
13
14using namespace bicameral::multires;
15using namespace bicameral::reaction;
16
17namespace bicameral::sim {
18
19 namespace {
20
28
29 // --- 索引 ---
30
31 uint32_t IndexMask(const MultiresNest& nest) {
32 return nest.capacity.indexEntries - 1;
33 }
34
35 void IndexInsert(MultiresNest& nest, uint32_t slot) {
36 const MrBlock& block = nest.blocks[slot];
37 const uint32_t home = MrIndexHome(block.level, block.originX, block.originY, block.originZ,
39 for (uint32_t probe = 0; probe < nest.capacity.indexEntries; ++probe) {
40 uint32_t& entry = nest.index[(home + probe) & IndexMask(nest)];
41 if (entry != MR_INDEX_EMPTY)
42 continue;
43
44 entry = slot;
45 return;
46 }
47
48 nest.counters[MR_COUNTER_INDEX_FULL] += 1;
49 }
50
51 void IndexRemove(MultiresNest& nest, uint32_t slot) {
52 const MrBlock& block = nest.blocks[slot];
53 const uint32_t home = MrIndexHome(block.level, block.originX, block.originY, block.originZ,
55 for (uint32_t probe = 0; probe < nest.capacity.indexEntries; ++probe) {
56 uint32_t& entry = nest.index[(home + probe) & IndexMask(nest)];
57 if (entry == MR_INDEX_EMPTY)
58 return;
59
60 if (entry != slot)
61 continue;
62
63 entry = MR_INDEX_TOMBSTONE;
64 nest.counters[MR_COUNTER_TOMBSTONES] += 1;
65 return;
66 }
67 }
68
69 // --- 端数の枠 ---
70
71 void Release(MrRequestState& state, uint32_t fractionSlot) {
72 FX_ASSERT(state.releaseCount < MR_MAX_RELEASES);
73 state.releases[state.releaseCount] = fractionSlot;
74 state.releaseCount += 1;
75 }
76
77 uint32_t PoppedBlock(const MultiresNest& nest, const MrRequestState& state, uint32_t i) {
78 return nest.freeBlocks[state.blockBase - 1 - i];
79 }
80
81 uint32_t PoppedPage(MultiresNest& nest, const MrRequestState& state, uint32_t i) {
82 FX_ASSERT(i < state.pageNeed);
83
84 return FreePageAt(nest, state.pageBase - 1 - i);
85 }
86
87 uint32_t PoppedFraction(const MultiresNest& nest, const MrRequestState& state, uint32_t i) {
88 if (i >= state.fractionNeed)
89 return MR_NO_FRACTION;
90
91 return nest.freeFractions[state.fractionBase - 1 - i];
92 }
93
94 // --- 1. 解決 ---
95
96 void ResolveRefine(MultiresNest& nest, uint32_t i) {
97 const MrRequest& request = nest.requests[i];
98 MrRequestState& state = nest.states[i];
99 const int32_t rootLevel = nest.capacity.rootLevel;
100 const uint32_t root = LookupBlock(
101 nest, rootLevel, MrBlockOriginOf(MrCoarserCoordinate(request.x, request.level, rootLevel)),
102 MrBlockOriginOf(MrCoarserCoordinate(request.y, request.level, rootLevel)),
103 MrBlockOriginOf(MrCoarserCoordinate(request.z, request.level, rootLevel)));
104 if (root == MR_NO_BLOCK) {
105 state.status = MR_STATUS_INVALID;
106 return;
107 }
108
109 // --- 子の番号をたどって、点を含む最も深い本物のブロックへ ---
110 uint32_t deepest = root;
111 while (nest.blocks[deepest].level < request.level) {
112 const MrBlock& block = nest.blocks[deepest];
113 const uint32_t
114 child = block.children[MrOctantOfPoint(block, request.x, request.y, request.z, request.level)];
115 if (child == MR_NO_BLOCK)
116 break;
117
118 deepest = child;
119 }
120
121 const int32_t deepestLevel = nest.blocks[deepest].level;
122 if (deepestLevel == request.level) {
123 state.status = MR_STATUS_ALREADY;
124 return;
125 }
126
127 state.target = deepest;
128 state.claimSlot = deepest;
129 state.levels = std::min(static_cast<uint32_t>(request.level - deepestLevel), MR_MAX_CHAIN_LEVELS);
130 nest.claims[deepest] = std::min(nest.claims[deepest], i);
131 }
132
133 MrChildren GatherChildren(const MultiresNest& nest, uint32_t childSlot, uint32_t fractionSlot, uint32_t local) {
134 MrChildren children{};
135 for (uint32_t j = 0; j < MR_CHILDREN_PER_CELL; ++j) {
136 const uint32_t index = MrChildCell(local, j);
137 children.cells[j] = LoadNestCell(nest, childSlot, index);
138 children.fractions[j] = FractionAt(nest, fractionSlot, index);
139 }
140
141 return children;
142 }
143
144 // 子のブロック childSlot を親へ粗くしても、親の八分の一の 64 セルのどれも成分が入りきるか(T-0022。multires.hlsli の MrCoarsenFits)
145 bool CoarsenFits(const MultiresNest& nest, uint32_t childSlot) {
146 const uint32_t fractionSlot = nest.blocks[childSlot].fraction;
147 for (uint32_t local = 0; local < MR_OCTANT_CELLS; ++local) {
148 if (!MrCoarsenFits(GatherChildren(nest, childSlot, fractionSlot, local)))
149 return false;
150 }
151
152 return true;
153 }
154
155 void ResolveCoarsen(MultiresNest& nest, uint32_t i) {
156 const MrRequest& request = nest.requests[i];
157 MrRequestState& state = nest.states[i];
158 const uint32_t slot = LookupBlock(nest, request.level, MrBlockOriginOf(request.x),
159 MrBlockOriginOf(request.y), MrBlockOriginOf(request.z));
160 if (slot == MR_NO_BLOCK || nest.blocks[slot].parent == MR_NO_BLOCK || MrHasRealChild(nest.blocks[slot])) {
161 state.status = MR_STATUS_INVALID;
162 return;
163 }
164
165 // --- 成分が入りきらなければ粗くしない。静かな葉なら、今の忙しさの印では粗くできないことにする(毎刻み要求し直さない)---
166 if (!CoarsenFits(nest, slot)) {
167 MrBlock& block = nest.blocks[slot];
168 block.quietCheck = MrQuietCheckStamp(block.busyTick, false);
169 state.status = MR_STATUS_SPECIES_FULL;
170 return;
171 }
172
173 const uint32_t parent = nest.blocks[slot].parent;
174 state.target = slot;
175 state.claimSlot = parent;
176 nest.claims[parent] = std::min(nest.claims[parent], i);
177 }
178
179 void Resolve(MultiresNest& nest, uint32_t i) {
180 const MrRequest& request = nest.requests[i];
181 nest.states[i] = MrMakeRequestState();
182 const int32_t span = request.level - nest.capacity.rootLevel;
183 if (span < 0 || span > static_cast<int32_t>(MR_MAX_LEVEL_SPAN)) {
184 nest.states[i].status = MR_STATUS_INVALID;
185 return;
186 }
187
188 if (request.op == MR_REQUEST_REFINE)
189 ResolveRefine(nest, i);
190 else if (request.op == MR_REQUEST_COARSEN)
191 ResolveCoarsen(nest, i);
192 else
193 nest.states[i].status = MR_STATUS_INVALID;
194 }
195
196 // --- 2. 確定: 取り合いに負けたら後回し。要る枠の数(端数は控えめに多く取り、適用の後で 0 なら返す)---
197
198 void Settle(MultiresNest& nest, uint32_t i) {
199 MrRequestState& state = nest.states[i];
200 if (state.status != MR_STATUS_PENDING)
201 return;
202
203 const bool coarsen = nest.requests[i].op == MR_REQUEST_COARSEN;
204 const bool lost = nest.claims[state.claimSlot] != i;
205 const bool targetTaken = coarsen && nest.claims[state.target] != MR_NO_CLAIM;
206 if (lost || targetTaken) {
207 state.status = MR_STATUS_CONFLICT;
208 return;
209 }
210
211 if (coarsen) {
212 const uint32_t parent = nest.blocks[state.target].parent;
213 state.fractionNeed = nest.blocks[parent].fraction == MR_NO_FRACTION ? 1 : 0;
214 state.pageNeed = MrCoarsenPageNeed(nest.blocks[state.target], UniformAt(nest, state.target),
215 nest.blocks[parent], UniformAt(nest, parent));
216 return;
217 }
218
219 // 一様な親の鎖は全部一様(頁なし)、頁を持つ親の鎖は全部頁を持つ(T-0102)
220 state.blockNeed = state.levels;
221 state.fractionNeed = nest.blocks[state.target].fraction != MR_NO_FRACTION ? state.levels : 0;
222 state.pageNeed = MrIsUniform(nest.blocks[state.target]) ? 0 : state.levels;
223 }
224
225 // --- 3. 割り当て: 要求の順の累積和が空きの数以下なら許可(許可は一覧の前から続く)---
226
227 void Allocate(MultiresNest& nest, uint32_t count) {
228 const uint32_t freeBlocks = nest.counters[MR_COUNTER_FREE_BLOCKS];
229 const uint32_t freeFractions = nest.counters[MR_COUNTER_FREE_FRACTIONS];
230 const uint32_t freePages = nest.counters[MR_COUNTER_FREE_PAGES];
231 uint32_t blockSum = 0;
232 uint32_t fractionSum = 0;
233 uint32_t pageSum = 0;
234 uint32_t grantedBlocks = 0;
235 uint32_t grantedFractions = 0;
236 uint32_t grantedPages = 0;
237 for (uint32_t i = 0; i < count; ++i) {
238 MrRequestState& state = nest.states[i];
239 if (state.status != MR_STATUS_PENDING)
240 continue;
241
242 const uint32_t blockBase = freeBlocks - blockSum;
243 const uint32_t fractionBase = freeFractions - fractionSum;
244 const uint32_t pageBase = freePages - pageSum;
245 blockSum += state.blockNeed;
246 fractionSum += state.fractionNeed;
247 pageSum += state.pageNeed;
248 if (blockSum > freeBlocks || fractionSum > freeFractions || pageSum > freePages) {
249 state.status = MR_STATUS_NO_SPACE;
250 continue;
251 }
252
253 state.status = MR_STATUS_GRANTED;
254 state.blockBase = blockBase;
255 state.fractionBase = fractionBase;
256 state.pageBase = pageBase;
257 grantedBlocks = blockSum;
258 grantedFractions = fractionSum;
259 grantedPages = pageSum;
260 }
261
262 nest.counters[MR_COUNTER_FREE_BLOCKS] = freeBlocks - grantedBlocks;
263 nest.counters[MR_COUNTER_FREE_FRACTIONS] = freeFractions - grantedFractions;
264 nest.counters[MR_COUNTER_FREE_PAGES] = freePages - grantedPages;
265 }
266
267 // --- 4. 適用: 細かくする(1 段)---
268
269 void RefineRequestLevel(MultiresNest& nest, uint32_t i, uint32_t depth, uint32_t parentSlot) {
270 const MrRequest& request = nest.requests[i];
271 MrRequestState& state = nest.states[i];
272 const uint32_t childSlot = PoppedBlock(nest, state, depth);
273 const uint32_t candidate = PoppedFraction(nest, state, depth);
274 MrBlock& parent = nest.blocks[parentSlot];
275 const uint32_t octant = MrOctantOfPoint(parent, request.x, request.y, request.z, request.level);
276 const bool uniform = MrIsUniform(parent); // 一様な親の子は一様(T-0102)
277 const uint32_t childPage = uniform ? MR_NO_PAGE : PoppedPage(nest, state, depth);
278
279 // --- 端数: 親の八分の一に 0 でない端数があれば、取っておいた枠に写す。親の残り(覆わない所)に 0 でない端数が残るか ---
280 bool octantFraction = false;
281 bool restFraction = false;
282 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index) {
283 const bool nonzero = !MrFractionIsZero(FractionAt(nest, parent.fraction, index));
284 const bool covered = MrOctantOfCell(index) == octant;
285 octantFraction |= nonzero && covered;
286 restFraction |= nonzero && !covered;
287 }
288
289 FX_ASSERT(!octantFraction || candidate != MR_NO_FRACTION);
290 const uint32_t childFraction = octantFraction ? candidate : MR_NO_FRACTION;
291
292 // --- セルと端数: 子は親と同じ数(一様なら値 1 つ)---
293 if (uniform)
294 UniformAt(nest, childSlot) = UniformAt(nest, parentSlot);
295
296 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index) {
297 const uint32_t parentCell = MrParentCellOfChild(octant, index);
298 if (!uniform)
299 PageCellAt(nest, childPage, index) = CellAt(nest, parentSlot, parentCell);
300
301 if (childFraction != MR_NO_FRACTION)
302 SetFraction(nest, childFraction, index, FractionAt(nest, parent.fraction, parentCell));
303 }
304
305 // --- 親を覆う(覆われた親のセルと端数は空。一様な親は覆われた所を空と読むので書かない)---
306 for (uint32_t local = 0; local < MR_OCTANT_CELLS; ++local) {
307 const uint32_t index = MrOctantCell(octant, local);
308 if (!uniform)
309 CellAt(nest, parentSlot, index) = RxMakeEmptyCell(0);
310
311 if (parent.fraction != MR_NO_FRACTION)
312 SetFraction(nest, parent.fraction, index, MrMakeEmptyFraction());
313 }
314
315 // --- 見出し・返す枠・索引 ---
316 if (candidate != MR_NO_FRACTION && childFraction == MR_NO_FRACTION)
317 Release(state, candidate);
318
319 if (parent.fraction != MR_NO_FRACTION && !restFraction) {
320 Release(state, parent.fraction);
321 parent.fraction = MR_NO_FRACTION;
322 }
323
324 parent.children[octant] = childSlot;
325 nest.blocks[childSlot] = MrMakeChildBlock(parent, parentSlot, octant, MR_BLOCK_REAL, childFraction,
326 childPage);
327 IndexInsert(nest, childSlot);
328
329 // --- 木を変えたブロックをつつく(活性の種。最初の段だけ親も。T-0100)---
330 if (depth == 0)
331 PokeBlock(nest, parentSlot);
332
333 PokeBlock(nest, childSlot);
334 }
335
336 void ApplyRefine(MultiresNest& nest, uint32_t i) {
337 uint32_t parent = nest.states[i].target;
338 for (uint32_t depth = 0; depth < nest.states[i].levels; ++depth) {
339 RefineRequestLevel(nest, i, depth, parent);
340 parent = PoppedBlock(nest, nest.states[i], depth);
341 }
342 }
343
344 // --- 4. 適用: 粗くする(1 段)---
345
346 void AddToLedger(MultiresNest& nest, int32_t level, uint32_t column, uint32_t lostBits) {
347 const uint32_t address = MrLedgerAddress(level, column, nest.capacity.ledgerColumns);
348 if (address == MR_NO_BLOCK) {
349 nest.counters[MR_COUNTER_LEDGER_OUTSIDE] += 1;
350 return;
351 }
352
353 nest.ledger[address] += lostBits;
354 }
355
356 void RecordCoarsened(MultiresNest& nest, const MrCoarsened& result, int32_t childLevel) {
357 nest.counters[MR_COUNTER_LOST] += result.lostCount;
358 nest.counters[MR_COUNTER_OVERFLOW] += result.overflowCount;
359 if (result.energyLostBits != 0)
360 AddToLedger(nest, childLevel, 0, result.energyLostBits);
361
362 for (uint32_t k = 0; k < result.lostSpeciesCount; ++k)
363 AddToLedger(nest, childLevel, 1 + result.lostSpecies[k], result.lostBits[k]);
364 }
365
366 void ApplyCoarsen(MultiresNest& nest, uint32_t i) {
367 MrRequestState& state = nest.states[i];
368 const uint32_t childSlot = state.target;
369 const MrBlock child = nest.blocks[childSlot];
370 MrBlock& parent = nest.blocks[child.parent];
371
372 // --- 子 2³ を 1 つに(親の八分の一の 64 セル)---
373 std::array<MrCoarsened, MR_OCTANT_CELLS> results{};
374 bool octantFraction = false;
375 for (uint32_t local = 0; local < MR_OCTANT_CELLS; ++local) {
376 results[local] = MrCoarsenCell(GatherChildren(nest, childSlot, child.fraction, local));
377 RecordCoarsened(nest, results[local], child.level);
378 octantFraction |= !MrFractionIsZero(results[local].fraction);
379 }
380
381 // --- 一様な親: 子が同じ値に戻らなければ頁に広げる(T-0102)---
382 if (state.pageNeed != 0) {
383 const uint32_t page = PoppedPage(nest, state, 0);
384 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index)
385 PageCellAt(nest, page, index) = MrUniformCell(parent, UniformAt(nest, child.parent), index);
386
387 parent.page = page;
388 }
389
390 // --- 親の端数: 枠が無ければ取っておいた枠を全部 0 で始める。残り(八分の一の外)に 0 でない端数があるか ---
391 bool restFraction = false;
392 const uint32_t newFraction = PoppedFraction(nest, state, 0);
393 if (parent.fraction == MR_NO_FRACTION) {
394 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index)
395 SetFraction(nest, newFraction, index, MrMakeEmptyFraction());
396 parent.fraction = newFraction;
397 }
398
399 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index) {
400 if (MrOctantOfCell(index) != child.parentOctant)
401 restFraction |= !MrFractionIsZero(FractionAt(nest, parent.fraction, index));
402 }
403
404 for (uint32_t local = 0; local < MR_OCTANT_CELLS; ++local) {
405 const uint32_t index = MrOctantCell(child.parentOctant, local);
406 if (!MrIsUniform(parent))
407 CellAt(nest, child.parent, index) = results[local].cell;
408
409 SetFraction(nest, parent.fraction, index, results[local].fraction);
410 }
411
412 // --- 見出し・返す枠・索引 ---
413 if (!octantFraction && !restFraction) {
414 Release(state, parent.fraction);
415 parent.fraction = MR_NO_FRACTION;
416 }
417
418 if (child.fraction != MR_NO_FRACTION)
419 Release(state, child.fraction);
420
421 state.releaseBlock = childSlot;
422 if (!MrIsUniform(child))
423 state.releasePage = child.page;
424
425 parent.children[child.parentOctant] = MR_NO_BLOCK;
426 IndexRemove(nest, childSlot);
427 nest.blocks[childSlot] = MrMakeUnusedBlock();
428 PokeBlock(nest, child.parent);
429 }
430
431 // --- 5. 解放: 返す枠を要求の順に積む・取り合いの印を消す・数える ---
432
433 void ReleaseAll(MultiresNest& nest, uint32_t count) {
434 for (uint32_t i = 0; i < count; ++i) {
435 const MrRequestState& state = nest.states[i];
436 if (state.claimSlot != MR_NO_CLAIM)
437 nest.claims[state.claimSlot] = MR_NO_CLAIM;
438
439 nest.counters[MrStatusCounter(state.status)] += 1;
440 if (state.status != MR_STATUS_GRANTED)
441 continue;
442
443 for (uint32_t k = 0; k < state.releaseCount; ++k)
444 nest.freeFractions[nest.counters[MR_COUNTER_FREE_FRACTIONS]++] = state.releases[k];
445
446 if (state.releaseBlock != MR_NO_BLOCK)
447 nest.freeBlocks[nest.counters[MR_COUNTER_FREE_BLOCKS]++] = state.releaseBlock;
448
449 if (state.releasePage != MR_NO_PAGE)
450 FreePageAt(nest, nest.counters[MR_COUNTER_FREE_PAGES]++) = state.releasePage;
451 }
452
453 // --- 墓石が表の 1/4 を超えたら作り直す ---
454 const bool rebuild = nest.counters[MR_COUNTER_TOMBSTONES] * 4 > nest.capacity.indexEntries;
455 nest.counters[MR_COUNTER_REBUILD_INDEX] = rebuild ? 1 : 0;
456 if (rebuild)
457 nest.counters[MR_COUNTER_TOMBSTONES] = 0;
458
459 nest.counters[MR_COUNTER_REQUESTS] = 0;
460 }
461
462 } // namespace
463
464 uint32_t PlaceRootBlock(MultiresNest& nest, int64_t originX, int64_t originY, int64_t originZ,
465 std::span<const RxCell> cells) {
466 FX_ASSERT(cells.size() == MR_BLOCK_CELLS);
467 FX_ASSERT(nest.counters[MR_COUNTER_FREE_BLOCKS] > 0);
468 const uint32_t slot = nest.freeBlocks[--nest.counters[MR_COUNTER_FREE_BLOCKS]];
469
470 MrBlock block = MrMakeUnusedBlock();
471 block.originX = originX;
472 block.originY = originY;
473 block.originZ = originZ;
474 block.level = nest.capacity.rootLevel;
475 block.kind = MR_BLOCK_REAL;
476
477 // --- 全部同じなら一様(頁なし)、でなければ頁を取る(T-0102)---
478 const bool uniform = std::ranges::all_of(cells, [&](const RxCell& cell) { return MrSameCell(cell, cells[0]); });
479 if (uniform) {
480 UniformAt(nest, slot) = cells[0];
481 } else {
482 block.page = nest_detail::PopPage(nest);
483 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index)
484 PageCellAt(nest, block.page, index) = cells[index];
485 }
486
487 nest.blocks[slot] = block;
488 IndexInsert(nest, slot);
489 nest_detail::PokeBlock(nest, slot);
490
491 return slot;
492 }
493
494 void SubmitRequests(MultiresNest& nest, std::span<const MrRequest> requests) {
495 uint32_t& count = nest.counters[MR_COUNTER_REQUESTS];
496 FX_ASSERT(count + requests.size() <= MR_MAX_REQUESTS);
497 for (const MrRequest& request : requests)
498 nest.requests[count++] = request;
499 }
500
502 const uint32_t count = nest.counters[MR_COUNTER_REQUESTS];
503 for (uint32_t i = 0; i < count; ++i)
504 Resolve(nest, i);
505
506 for (uint32_t i = 0; i < count; ++i)
507 Settle(nest, i);
508
509 Allocate(nest, count);
510 for (uint32_t i = 0; i < count; ++i) {
511 if (nest.states[i].status != MR_STATUS_GRANTED)
512 continue;
513
514 if (nest.requests[i].op == MR_REQUEST_REFINE)
515 ApplyRefine(nest, i);
516 else
517 ApplyCoarsen(nest, i);
518 }
519
520 ReleaseAll(nest, count);
521 if (nest.counters[MR_COUNTER_REBUILD_INDEX] != 0)
522 RebuildIndex(nest);
523 }
524
525 uint32_t LookupBlock(const MultiresNest& nest, int32_t level, int64_t originX, int64_t originY, int64_t originZ) {
526 const uint32_t home = MrIndexHome(level, originX, originY, originZ, nest.capacity.indexEntries);
527 for (uint32_t probe = 0; probe < nest.capacity.indexEntries; ++probe) {
528 const uint32_t entry = nest.index[(home + probe) & IndexMask(nest)];
529 if (entry == MR_INDEX_EMPTY)
530 return MR_NO_BLOCK;
531
532 if (entry != MR_INDEX_TOMBSTONE && MrBlockHasKey(nest.blocks[entry], level, originX, originY, originZ))
533 return entry;
534 }
535
536 return MR_NO_BLOCK;
537 }
538
540 std::ranges::fill(nest.index, MR_INDEX_EMPTY);
541 for (uint32_t slot = 0; slot < nest.capacity.worldBlocks; ++slot) {
542 if (nest.blocks[slot].kind == MR_BLOCK_REAL)
543 IndexInsert(nest, slot);
544 }
545 }
546
547} // namespace bicameral::sim
void ResolveCoarsen(MultiresNest &nest, uint32_t i)
void RefineRequestLevel(MultiresNest &nest, uint32_t i, uint32_t depth, uint32_t parentSlot)
void ApplyCoarsen(MultiresNest &nest, uint32_t i)
uint32_t PoppedBlock(const MultiresNest &nest, const MrRequestState &state, uint32_t i)
void ResolveRefine(MultiresNest &nest, uint32_t i)
uint32_t & FreePageAt(MultiresNest &nest, uint32_t position)
void IndexInsert(MultiresNest &nest, uint32_t slot)
multires::MrFraction FractionAt(const MultiresNest &nest, uint32_t fractionSlot, uint32_t index)
void Release(MrRequestState &state, uint32_t fractionSlot)
MrChildren GatherChildren(const MultiresNest &nest, uint32_t childSlot, uint32_t fractionSlot, uint32_t local)
void Allocate(MultiresNest &nest, uint32_t count)
void IndexRemove(MultiresNest &nest, uint32_t slot)
uint32_t PoppedFraction(const MultiresNest &nest, const MrRequestState &state, uint32_t i)
bool CoarsenFits(const MultiresNest &nest, uint32_t childSlot)
void ApplyRefine(MultiresNest &nest, uint32_t i)
uint32_t PoppedPage(MultiresNest &nest, const MrRequestState &state, uint32_t i)
reaction::RxCell & UniformAt(MultiresNest &nest, uint32_t slot)
void RecordCoarsened(MultiresNest &nest, const MrCoarsened &result, int32_t childLevel)
reaction::RxCell & PageCellAt(MultiresNest &nest, uint32_t page, uint32_t index)
void SetFraction(MultiresNest &nest, uint32_t fractionSlot, uint32_t index, const multires::MrFraction &fraction)
void AddToLedger(MultiresNest &nest, int32_t level, uint32_t column, uint32_t lostBits)
void ReleaseAll(MultiresNest &nest, uint32_t count)
reaction::RxCell & CellAt(MultiresNest &nest, uint32_t slot, uint32_t index)
uint32_t & FreePageAt(MultiresNest &nest, uint32_t position)
multires::MrFraction FractionAt(const MultiresNest &nest, uint32_t fractionSlot, uint32_t index)
void PokeBlock(MultiresNest &nest, uint32_t slot)
reaction::RxCell & UniformAt(MultiresNest &nest, uint32_t slot)
reaction::RxCell & PageCellAt(MultiresNest &nest, uint32_t page, uint32_t index)
void SetFraction(MultiresNest &nest, uint32_t fractionSlot, uint32_t index, const multires::MrFraction &fraction)
uint32_t PopPage(MultiresNest &nest)
reaction::RxCell & CellAt(MultiresNest &nest, uint32_t slot, uint32_t index)
void SubmitRequests(MultiresNest &nest, std::span< const multires::MrRequest > requests)
void RebuildIndex(MultiresNest &nest)
RxCell LoadNestCell(const MultiresNest &nest, uint32_t slot, uint32_t index)
uint32_t LookupBlock(const MultiresNest &nest, int32_t level, int64_t originX, int64_t originY, int64_t originZ)
void ProcessRequests(MultiresNest &nest)
uint32_t PlaceRootBlock(MultiresNest &nest, int64_t originX, int64_t originY, int64_t originZ, std::span< const reaction::RxCell > cells)
std::vector< uint32_t > index
std::vector< multires::MrRequestState > states
std::vector< multires::MrBlock > blocks
std::vector< uint32_t > claims
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