11using namespace bicameral::multires;
32 const MrBlock parent = nest.
blocks[parentSlot];
33 const uint32_t octant = MrOctantOfPoint(parent, point.
x, point.
y, point.
z, point.
level);
37 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index)
40 nest.
blocks[childSlot] = MrMakeChildBlock(parent, parentSlot, octant, MR_BLOCK_SHADOW, MR_NO_FRACTION,
45 const MrBlock shadow = nest.
blocks[shadowSlot];
46 FX_ASSERT(shadow.kind == MR_BLOCK_SHADOW);
47 for (uint32_t local = 0; local < MR_OCTANT_CELLS; ++local) {
48 const RxCell parentCell =
LoadNestCell(nest, shadow.parent, MrOctantCell(shadow.parentOctant, local));
49 MrShadowFamily family{};
50 for (uint32_t j = 0; j < MR_CHILDREN_PER_CELL; ++j)
51 family.cells[j] =
CellAt(nest, shadowSlot, MrChildCell(local, j));
53 const MrShadowFamily pulled = MrPullBackShadow(table, parentCell, family);
54 nest.
counters[MR_COUNTER_SHADOW_CLAMPED] += pulled.clamped;
55 for (uint32_t j = 0; j < MR_CHILDREN_PER_CELL; ++j)
56 CellAt(nest, shadowSlot, MrChildCell(local, j)) = pulled.cells[j];
61 nest.
blocks[shadowSlot].busyTick = MR_BUSY_POKED;
68 const uint64_t extension = negative ? ~uint64_t{0} : 0;
70 return {low, high, extension, extension};
75 FX_ASSERT(bits < 256);
76 const uint32_t words = bits / 64;
77 const uint32_t rest = bits % 64;
79 for (
size_t i = value.size(); i-- > words;) {
80 const size_t source = i - words;
81 result[i] = value[source] << rest;
82 if (rest != 0 && source > 0)
83 result[i] |= value[source - 1] >> (64 - rest);
93 for (
size_t i = 0; i < value.size(); ++i) {
94 const FxU128 product = FxMulU64Full(value[i], factor);
95 result[i] = product.lo + carry;
96 carry = product.hi + (result[i] < carry ? 1u : 0u);
104 for (
size_t i = 0; i < sum.size(); ++i) {
105 const uint64_t partial = sum[i] + value[i];
106 const uint64_t carryPartial = partial < sum[i] ? 1u : 0u;
107 sum[i] = partial + carry;
108 carry = carryPartial + (sum[i] < carry ? 1u : 0u);
113 const MrFraction& fraction, uint32_t shiftBits) {
118 const size_t elementCount = totals.
elements.size();
119 for (uint32_t species = MrNextSpecies(cell, fraction, 0); species != MR_NO_SPECIES;
120 species = MrNextSpecies(cell, fraction, species)) {
121 const Wide256 amount =
MakeWide256(MrAmountOf(cell, species), MrFractionOf(fraction, species),
false);
122 for (
size_t element = 0; element < elementCount; ++element) {
123 const uint32_t atoms = table.
speciesElements[(species * elementCount) + element];
132 FX_ASSERT(level <= finestLevel);
134 return ShiftLeft(
Wide256{value, 0, 0, 0},
static_cast<uint32_t
>(finestLevel - level) * MR_LEVEL_SHIFT);
145 const uint32_t species = column - 1;
146 const size_t elementCount = totals.
elements.size();
147 for (
size_t element = 0; element < elementCount; ++element) {
148 const uint32_t atoms = table.
speciesElements[(species * elementCount) + element];
155 int32_t finestLevel) {
157 for (uint32_t row = 0; row < MR_LEDGER_LEVELS; ++row) {
158 const int32_t level = MR_LEDGER_LEVEL_MIN +
static_cast<int32_t
>(row);
159 for (uint32_t column = 0; column < columns; ++column) {
160 const uint64_t value = nest.
ledger[(
size_t{row} * columns) + column];
168 uint64_t hash = FxHashCombine(0, fraction.energy);
169 hash = FxHashCombine(hash, fraction.speciesCount);
170 for (uint32_t i = 0; i < fraction.speciesCount; ++i) {
171 hash = FxHashCombine(hash, fraction.species[i]);
172 hash = FxHashCombine(hash, fraction.amounts[i]);
179 uint64_t hash = FxHashCombine(0,
static_cast<uint64_t
>(block.originX));
180 hash = FxHashCombine(hash,
static_cast<uint64_t
>(block.originY));
181 hash = FxHashCombine(hash,
static_cast<uint64_t
>(block.originZ));
182 hash = FxHashCombine(hash,
static_cast<uint64_t
>(
static_cast<int64_t
>(block.level)));
183 hash = FxHashCombine(hash, block.kind);
184 hash = FxHashCombine(hash, block.parent);
185 hash = FxHashCombine(hash, block.parentOctant);
186 hash = FxHashCombine(hash, block.fraction);
187 for (
const uint32_t child : block.children)
188 hash = FxHashCombine(hash, child);
196 return FractionAt(nest, fractionSlot, index).energy;
202 uint64_t worldSeed, uint64_t tick,
bool react,
203 std::span<const MrEnergyDelta> deltas) {
204 const MrBlock block = nest.
blocks[slot];
207 MrLimitTally tally = MrMakeLimitTally();
208 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index) {
209 if (!MrIsSteppedCell(block, index))
212 RxCell& cell =
CellAt(nest, slot, index);
213 const RxCell before = cell;
214 const uint64_t fractionBefore =
FractionEnergy(nest, block.fraction, index);
217 if (!deltas.empty()) {
219 if (!MrEnergyDeltaIsZero(delta))
225 const RxWaitStep step = MrStepCellWait(view, cell, worldSeed, tick, block, index);
228 tally = MrAddLimits(tally, step.limits);
236 nest.
counters[MR_COUNTER_LIMIT_PRODUCTS] += tally.productsHeld;
237 nest.
counters[MR_COUNTER_LIMIT_CANDIDATES] += tally.candidatesLimited;
244 namespace nest_detail {
247 const MrEnergyDelta& delta) {
248 const uint32_t fractionSlot = nest.
blocks[slot].fraction;
249 MrEnergyDelta value = MrMakeEnergyDelta();
250 value.whole = cell.energy;
251 value.fraction = FractionEnergy(nest, fractionSlot, index);
252 value = MrAddEnergyDelta(value, delta);
254 cell.energy = value.whole;
255 if (fractionSlot == MR_NO_FRACTION) {
256 FX_ASSERT(value.fraction == 0);
260 MrFraction fraction =
FractionAt(nest, fractionSlot, index);
261 fraction.energy = value.fraction;
266 const auto blockCount =
static_cast<uint32_t
>(nest.
blocks.size());
267 std::vector<uint8_t> frozen(blockCount, 0);
268 for (uint32_t slot = 0; slot < blockCount; ++slot)
269 frozen[slot] = nest.
blocks[slot].page == MR_PAGE_WANTED ? 1 : 0;
273 results[slot].expanded =
true;
280 std::span<uint8_t> stepped, uint64_t worldSeed, uint64_t tick,
282 const auto blockCount =
static_cast<uint32_t
>(nest.
blocks.size());
283 std::vector<BlockStepResult> results(blockCount);
288 for (uint32_t slot = 0; slot < blockCount; ++slot) {
289 MrBlock& block = nest.
blocks[slot];
290 if (stepped[slot] == 0 || !MrIsUniform(block))
294 if (MrChangeMark(tick) < block.wakeTick)
297 const MrUniformWait wait = MrUniformWaitOf(view,
UniformAt(nest, slot), worldSeed, tick, block);
298 if (wait.changed != 0) {
299 block.page = MR_PAGE_WANTED;
303 results[slot].evaluated =
true;
304 results[slot].wakeTick = wait.wakeTick;
309 std::vector<MrEnergyDelta> deltas;
311 deltas =
StepConduction(nest, view, stepped, options, wakeMark, results);
316 for (uint32_t slot = 0; slot < blockCount; ++slot) {
317 if (MrIsUniform(nest.
blocks[slot]))
321 results[slot] = StepPagedBlock(nest, view, slot, worldSeed, tick, stepped[slot] != 0, deltas);
322 results[slot].expanded = earlier.
expanded;
323 results[slot].changed = results[slot].changed || earlier.
changed;
330 for (MrBlock& block : nest.
blocks) {
331 if (block.busyTick != MR_BUSY_POKED)
335 block.busyTick = MrChangeMark(tick);
341 const uint64_t mark = MrChangeMark(tick);
342 for (uint32_t slot = 0; slot < nest.
blocks.size(); ++slot) {
344 MrBlock& block = nest.
blocks[slot];
347 block.busyTick = mark;
348 block.wakeTick = mark + 1;
356 std::vector<uint32_t> expanded;
358 MrBlock& block = nest.
blocks[slot];
359 if (block.page != MR_PAGE_WANTED)
363 if (nest.
counters[MR_COUNTER_FREE_PAGES] == 0) {
364 block.page = MR_NO_PAGE;
365 nest.
counters[MR_COUNTER_PAGE_SHORTAGE] += 1;
366 nest.
seeds[slot] = 1;
371 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index)
374 nest.
counters[MR_COUNTER_EXPANDED] += 1;
375 expanded.push_back(slot);
384 const MrBlock& block = nest.
blocks[slot];
385 if (MrIsUniform(block))
386 return MrUniformCell(block, UniformAt(nest, slot), index);
388 return nest.
cells[nest.
blocks.size() + (
size_t{block.page} * MR_BLOCK_CELLS) + index];
403 nest.
blocks.assign(blockCount, MrMakeUnusedBlock());
404 nest.
cells.assign(blockCount + (pageCount * MR_BLOCK_CELLS), RxMakeEmptyCell(0));
405 nest.
fractions.assign(
size_t{capacity.
fractions} * MR_BLOCK_CELLS, MrMakeEmptyFraction());
408 nest.
requests.assign(MR_MAX_REQUESTS, MrMakeRequest(0, 0, 0, 0, 0));
409 nest.
states.assign(MR_MAX_REQUESTS, MrMakeRequestState());
415 for (uint32_t i = 0; i < capacity.
worldBlocks; ++i)
418 for (uint32_t i = 0; i < capacity.
pages; ++i)
422 for (uint32_t i = 0; i < capacity.
fractions; ++i)
433 int64_t originZ, std::span<const RxCell> cells) {
434 FX_ASSERT(cells.size() == MR_BLOCK_CELLS);
435 FX_ASSERT(IsObserverSlot(nest, slot));
437 nest.
blocks[slot] = MrMakeMirrorBlock(level, originX, originY, originZ, page);
438 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index)
439 PageCellAt(nest, page, index) = cells[index];
444 uint32_t parent = parentSlot;
445 for (uint32_t i = 0; i < levelCount; ++i) {
446 RefineShadowLevel(nest, parent, firstChildSlot + i, point);
447 parent = firstChildSlot + i;
452 for (uint32_t i = 0; i < levelCount; ++i) {
453 FX_ASSERT(nest.
blocks[firstSlot + i].kind == MR_BLOCK_SHADOW);
454 nest.
blocks[firstSlot + i] = MrMakeUnusedBlock();
460 std::vector<uint8_t> stepped(nest.
blocks.size(), 0);
461 for (uint32_t slot = 0; slot < nest.
blocks.size(); ++slot) {
462 const uint32_t kind = nest.
blocks[slot].kind;
463 stepped[slot] = kind != MR_BLOCK_UNUSED && kind != MR_BLOCK_MIRROR ? 1 : 0;
467 worldSeed, tick, options, 0);
472 uint32_t levelCount) {
474 for (uint32_t i = 0; i < levelCount; ++i)
475 PullBackLevel(nest, view, firstShadowSlot + i);
480 for (uint32_t slot = 0; slot < nest.
blocks.size(); ++slot) {
481 const MrBlock& block = nest.
blocks[slot];
482 if (block.kind != MR_BLOCK_REAL)
485 hash = FxHashCombine(hash, HashBlock(block));
486 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index) {
487 if (!MrIsSteppedCell(block, index))
491 hash = FxHashCombine(hash, HashFraction(FractionAt(nest, block.fraction, index)));
500 for (
const MrBlock& block : nest.
blocks) {
501 hash = FxHashCombine(hash, HashBlock(block));
502 hash = FxHashCombine(hash, block.activeTick);
503 hash = FxHashCombine(hash, block.busyTick);
504 hash = FxHashCombine(hash, block.wakeTick);
505 hash = FxHashCombine(hash, block.page);
506 hash = FxHashCombine(hash, block.quietCheck);
509 for (
const RxCell& cell : nest.
cells)
512 for (
const MrFraction& fraction : nest.
fractions)
513 hash = FxHashCombine(hash, HashFraction(fraction));
516 hash = FxHashCombine(hash, slot);
519 hash = FxHashCombine(hash, slot);
521 for (
const uint64_t value : nest.
ledger)
522 hash = FxHashCombine(hash, value);
524 for (
const uint32_t counter : nest.
counters)
525 hash = FxHashCombine(hash, counter);
531 int32_t finestLevel) {
534 for (uint32_t slot = 0; slot < nest.
blocks.size(); ++slot) {
535 const MrBlock& block = nest.
blocks[slot];
536 if (block.kind != MR_BLOCK_REAL)
539 FX_ASSERT(block.level <= finestLevel);
540 const auto shiftBits =
static_cast<uint32_t
>(finestLevel - block.level) * MR_LEVEL_SHIFT;
541 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index) {
542 if (!MrIsSteppedCell(block, index))
545 const MrFraction fraction = FractionAt(nest, block.fraction, index);
546 AddCellTotals(totals, table,
LoadNestCell(nest, slot, index), fraction, shiftBits);
550 AddLedgerTotals(totals, nest, table, finestLevel);
void PullBackLevel(MultiresNest &nest, const ReactionTableView &table, uint32_t shadowSlot)
void AddLedgerTotals(ConservedTotals &totals, const MultiresNest &nest, const BakedReactionTable &table, int32_t finestLevel)
void AddLedgerColumn(ConservedTotals &totals, const BakedReactionTable &table, uint32_t column, const Wide256 &amount)
multires::MrFraction FractionAt(const MultiresNest &nest, uint32_t fractionSlot, uint32_t index)
nest_detail::BlockStepResult StepPagedBlock(MultiresNest &nest, const ReactionTableView &view, uint32_t slot, uint64_t worldSeed, uint64_t tick, bool react, std::span< const MrEnergyDelta > deltas)
Wide256 LedgerValue(uint64_t value, int32_t level, int32_t finestLevel)
Wide256 MakeWide256(uint64_t high, uint64_t low, bool negative)
void AddTo(Wide256 &sum, const Wide256 &value)
uint64_t HashBlock(const MrBlock &block)
reaction::RxCell & PageCellAt(MultiresNest &nest, uint32_t page, uint32_t index)
Wide256 ShiftLeft(const Wide256 &value, uint32_t bits)
void AddCellTotals(ConservedTotals &totals, const BakedReactionTable &table, const RxCell &cell, const MrFraction &fraction, uint32_t shiftBits)
uint64_t HashFraction(const MrFraction &fraction)
Wide256 MultiplySmall(Wide256 value, uint32_t factor)
reaction::RxCell & CellAt(MultiresNest &nest, uint32_t slot, uint32_t index)
uint64_t FractionEnergy(const MultiresNest &nest, uint32_t fractionSlot, uint32_t index)
bool IsObserverSlot(const MultiresNest &nest, uint32_t slot)
void RefineShadowLevel(MultiresNest &nest, uint32_t parentSlot, uint32_t childSlot, const MultiresPoint &point)
size_t PageCellAddress(const MultiresNest &nest, uint32_t page, uint32_t index)
uint32_t & FreePageAt(MultiresNest &nest, uint32_t position)
std::vector< BlockStepResult > StepBlocks(MultiresNest &nest, const ReactionTableView &view, std::span< uint8_t > stepped, uint64_t worldSeed, uint64_t tick, const MultiresStepOptions &options, uint32_t wakeMark)
multires::MrFraction FractionAt(const MultiresNest &nest, uint32_t fractionSlot, uint32_t index)
std::vector< uint8_t > ExpandForStep(MultiresNest &nest, std::span< BlockStepResult > results)
std::vector< uint32_t > ExpandWantedPages(MultiresNest &nest)
void RecordWaitResults(MultiresNest &nest, std::span< const BlockStepResult > results, uint64_t tick)
reaction::RxCell & UniformAt(MultiresNest &nest, uint32_t slot)
void ApplyEnergyDelta(MultiresNest &nest, uint32_t slot, uint32_t index, RxCell &cell, const MrEnergyDelta &delta)
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)
std::vector< MrEnergyDelta > StepConduction(MultiresNest &nest, const ReactionTableView &view, std::span< uint8_t > stepped, const MultiresStepOptions &options, uint32_t wakeMark, std::span< BlockStepResult > results)
void ResolvePokes(MultiresNest &nest, uint64_t tick)
reaction::RxCell & CellAt(MultiresNest &nest, uint32_t slot, uint32_t index)
void PlaceMirrorBlock(MultiresNest &nest, uint32_t slot, int32_t level, int64_t originX, int64_t originY, int64_t originZ, std::span< const RxCell > cells)
std::array< uint64_t, 4 > Wide256
void PullBackShadowChain(MultiresNest &nest, const BakedReactionTable &table, uint32_t firstShadowSlot, uint32_t levelCount)
uint64_t HashReactionCell(const RxCell &cell)
void RemoveShadowChain(MultiresNest &nest, uint32_t firstSlot, uint32_t levelCount)
ConservedTotals ComputeConservedTotals(const MultiresNest &nest, const BakedReactionTable &table, int32_t finestLevel)
MultiresNest MakeMultiresNest(const MultiresCapacity &capacity)
uint32_t UsedWorldPages(const MultiresNest &nest)
uint64_t HashWholeNest(const MultiresNest &nest)
uint64_t HashRealLeaves(const MultiresNest &nest)
void RefineShadowChain(MultiresNest &nest, uint32_t parentSlot, uint32_t firstChildSlot, uint32_t levelCount, const MultiresPoint &point)
RxCell LoadNestCell(const MultiresNest &nest, uint32_t slot, uint32_t index)
void StepNest(MultiresNest &nest, const BakedReactionTable &table, uint64_t worldSeed, uint64_t tick, const MultiresStepOptions &options)
std::vector< uint32_t > speciesElements
std::vector< std::string > elementNames
ReactionTableView View() const
std::vector< Wide256 > elements
std::vector< uint8_t > seeds
std::vector< reaction::RxCell > cells
std::vector< uint32_t > index
MultiresCapacity capacity
std::vector< multires::MrRequestState > states
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