22#include "common/multires_conduction.hlsli"
26using namespace bicameral::multires;
51 bool IsFrozen(std::span<const uint8_t> frozen, uint32_t slot) {
52 return !frozen.empty() && frozen[slot] != 0;
57 std::span<const uint8_t> frozen, std::span<const uint8_t> implicitBlocks,
59 const CpuTree tree{.nest = &nest};
60 const MrBlock& block = nest.
blocks[slot];
62 const MrThermal self = thermals.
At(slot, index);
66 for (uint32_t face = 0; face < MR_FACES; ++face) {
67 const MrFaceNeighbor neighbor = MrFindFaceNeighbor(tree, slot, block, index, face,
69 if (neighbor.kind != MR_NEIGHBOR_SAME && neighbor.kind != MR_NEIGHBOR_COARSER)
76 if (neighbor.kind == MR_NEIGHBOR_SAME && joinsImplicit &&
80 const MrThermal other = thermals.
At(neighbor.slot, neighbor.index);
81 if (neighbor.kind == MR_NEIGHBOR_SAME) {
82 faces.
sameLevelOutflow += MrSubstepSameLevelFlow(self, other, block.level, shift);
87 const int64_t flow = MrSubstepCrossLevelFlow(self, other, block.level, neighbor.gap, shift,
101 return stepped[slot] != 0;
106 constexpr uint32_t LAST = MR_BLOCK_EDGE - 1;
107 const uint32_t x = MrCellX(index);
108 const uint32_t y = MrCellY(index);
109 const uint32_t z = MrCellZ(index);
111 return x == 0 || x == LAST || y == 0 || y == LAST || z == 0 || z == LAST;
117 for (uint32_t i = 0; i < faces.
crossCount; ++i) {
120 if (transfer.fineDelta == 0)
125 if (nest.
blocks[coarseSlot].page == MR_NO_PAGE)
126 nest.
blocks[coarseSlot].page = MR_PAGE_WANTED;
128 if (transfer.coarseDelta.fraction != 0)
129 wantsFraction[coarseSlot] = 1;
138 const bool uniform = MrIsUniform(nest.
blocks[slot]);
140 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index) {
149 if (flows && nest.
blocks[slot].page == MR_NO_PAGE)
150 nest.
blocks[slot].page = MR_PAGE_WANTED;
155 std::span<const uint8_t> wantsFraction) {
157 MrBlock& block = nest.
blocks[slot];
158 if (wantsFraction[slot] == 0 || frozen[slot] != 0 || block.fraction != MR_NO_FRACTION)
161 uint32_t& freeCount = nest.
counters[MR_COUNTER_FREE_FRACTIONS];
162 if (freeCount == 0) {
163 nest.
counters[MR_COUNTER_FRACTION_SHORTAGE] += 1;
168 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index)
175 std::span<const uint8_t> frozen, std::span<const uint8_t> implicitBlocks,
178 MrEnergyDelta own = MrMakeEnergyDelta();
180 for (uint32_t i = 0; i < faces.
crossCount; ++i) {
182 const MrBlock& coarseBlock = nest.
blocks[coarse.slot];
183 const MrCrossTransfer transfer = MrSplitCrossFlow(faces.
crossFlows[i], coarse.gap,
184 coarseBlock.fraction != MR_NO_FRACTION);
185 if (transfer.fineDelta == 0)
188 FX_ASSERT(!MrIsUniform(coarseBlock));
189 own.whole += transfer.fineDelta;
191 target = MrAddEnergyDelta(target, transfer.coarseDelta);
195 entry = MrAddEnergyDelta(entry, own);
201 for (uint32_t slot = 0; slot < nest.
blocks.size(); ++slot) {
203 const bool begins = MrSubstepBegins(substep, shift, options.
maxSubcycleGap);
204 active[slot] = stepped[slot] != 0 && begins ? 1 : 0;
211 std::vector<uint32_t> changed;
212 for (uint32_t slot = 0; slot < nest.
blocks.size(); ++slot) {
213 const MrBlock block = nest.
blocks[slot];
215 if (MrIsUniform(block) || !MrSubstepEnds(substep, shift, options.
maxSubcycleGap))
218 bool blockChanged =
false;
219 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index) {
221 if (!MrIsSteppedCell(block, index) || MrEnergyDeltaIsZero(delta))
225 delta = MrMakeEnergyDelta();
233 changed.push_back(slot);
241 std::span<uint8_t> stepped) {
242 for (
const uint32_t slot : changed) {
248 if (nest.
blocks[slot].activeTick == wakeMark)
260 m_values(nest.blocks.size() * (MR_BLOCK_CELLS + 1)),
261 m_known(nest.blocks.size() * (MR_BLOCK_CELLS + 1), 0) {}
264 const bool uniform = MrIsUniform(
m_nest->blocks[slot]);
265 const size_t address = (
size_t{slot} * (MR_BLOCK_CELLS + 1)) + (uniform ? MR_BLOCK_CELLS : index);
275 const auto first =
static_cast<std::ptrdiff_t
>(
size_t{slot} * (MR_BLOCK_CELLS + 1));
276 std::fill(
m_known.begin() + first,
m_known.begin() + first + MR_BLOCK_CELLS + 1, uint8_t{0});
281 std::vector<uint8_t> wantsFraction(nest.
blocks.size(), 0);
282 for (uint32_t slot = 0; slot < nest.
blocks.size(); ++slot) {
283 if (IsSteppedSlot(stepped, slot))
284 MarkBlockWants(nest, thermals, slot, wantsFraction, options);
287 return wantsFraction;
291 std::span<const uint8_t> frozen, std::span<const uint8_t> wantsFraction,
293 std::span<MrEnergyDelta> deltas) {
294 AllocateConductionFractions(nest, frozen, wantsFraction);
297 for (uint32_t slot = 0; slot < nest.
blocks.size(); ++slot) {
298 const MrBlock& block = nest.
blocks[slot];
299 if (!IsSteppedSlot(stepped, slot) || IsFrozen(frozen, slot) || MrIsUniform(block) ||
303 for (uint32_t index = 0; index < MR_BLOCK_CELLS; ++index) {
304 if (MrIsSteppedCell(block, index))
305 AddCellFlows(nest, thermals, slot, index, frozen, implicitBlocks, options, deltas);
312 uint32_t wakeMark, std::span<BlockStepResult> results) {
317 std::vector<MrEnergyDelta> deltas(nest.
cells.size(), MrMakeEnergyDelta());
318 std::vector<uint8_t> active(nest.
blocks.size(), 0);
319 for (uint32_t substep = 0;; ++substep) {
321 MarkSubstepBlocks(nest, stepped, substep, options, active);
322 const std::vector<uint8_t> wantsFraction =
MarkConductionWants(nest, thermals, active, options);
323 const std::vector<uint8_t> frozen =
ExpandForStep(nest, results);
324 const std::vector<uint8_t> implicitBlocks =
MarkImplicitBlocks(nest, thermals, frozen, options);
325 ComputeConduction(nest, thermals, active, frozen, wantsFraction, implicitBlocks, options, deltas);
326 if (substep + 1 == substeps) {
334 const std::vector<uint32_t> changed = ApplyEndingDeltas(nest, thermals, substep, options, deltas);
335 for (
const uint32_t slot : changed)
336 results[slot].changed =
true;
339 WakeChangedBlocks(nest, changed, wakeMark, stepped);
void Invalidate(uint32_t slot)
multires::MrThermal At(uint32_t slot, uint32_t index)
CellThermals(const MultiresNest &nest, const ReactionTableView &view)
const MultiresNest * m_nest
void Invalidate(uint32_t slot)
std::vector< multires::MrThermal > m_values
multires::MrThermal At(uint32_t slot, uint32_t index)
std::vector< uint8_t > m_known
bool IsSteppedSlot(std::span< const uint8_t > stepped, uint32_t slot)
bool MarkCrossSends(MultiresNest &nest, const CellFaces &faces, std::span< uint8_t > wantsFraction)
void MarkBlockWants(MultiresNest &nest, CellThermals &thermals, uint32_t slot, std::span< uint8_t > wantsFraction, const MultiresStepOptions &options)
bool IsFrozen(std::span< const uint8_t > frozen, uint32_t slot)
void AddCellFlows(const MultiresNest &nest, CellThermals &thermals, uint32_t slot, uint32_t index, std::span< const uint8_t > frozen, std::span< const uint8_t > implicitBlocks, const MultiresStepOptions &options, std::span< MrEnergyDelta > deltas)
CellFaces CollectCellFaces(const MultiresNest &nest, CellThermals &thermals, uint32_t slot, uint32_t index, std::span< const uint8_t > frozen, std::span< const uint8_t > implicitBlocks, const MultiresStepOptions &options)
void MarkSubstepBlocks(const MultiresNest &nest, std::span< const uint8_t > stepped, uint32_t substep, const MultiresStepOptions &options, std::span< uint8_t > active)
uint32_t SubcycleShift(const MultiresStepOptions &options, int32_t level)
bool OnBlockSurface(uint32_t index)
std::vector< uint32_t > ApplyEndingDeltas(MultiresNest &nest, CellThermals &thermals, uint32_t substep, const MultiresStepOptions &options, std::span< MrEnergyDelta > deltas)
void WakeChangedBlocks(MultiresNest &nest, std::span< const uint32_t > changed, uint32_t wakeMark, std::span< uint8_t > stepped)
void AllocateConductionFractions(MultiresNest &nest, std::span< const uint8_t > frozen, std::span< const uint8_t > wantsFraction)
size_t PageCellAddress(const MultiresNest &nest, uint32_t page, uint32_t index)
void ComputeConduction(MultiresNest &nest, CellThermals &thermals, std::span< const uint8_t > stepped, std::span< const uint8_t > frozen, std::span< const uint8_t > wantsFraction, std::span< const uint8_t > implicitBlocks, const MultiresStepOptions &options, std::span< MrEnergyDelta > deltas)
void WakeAround(MultiresNest &nest, uint32_t slot, uint32_t mark)
std::vector< uint8_t > ExpandForStep(MultiresNest &nest, std::span< BlockStepResult > results)
std::vector< uint8_t > MarkImplicitBlocks(MultiresNest &nest, CellThermals &thermals, std::span< const uint8_t > frozen, const MultiresStepOptions &options)
std::vector< uint8_t > MarkConductionWants(MultiresNest &nest, CellThermals &thermals, std::span< const uint8_t > stepped, const MultiresStepOptions &options)
void ApplyEnergyDelta(MultiresNest &nest, uint32_t slot, uint32_t index, RxCell &cell, const MrEnergyDelta &delta)
void SetFraction(MultiresNest &nest, uint32_t fractionSlot, uint32_t index, const multires::MrFraction &fraction)
std::vector< MrEnergyDelta > StepConduction(MultiresNest &nest, const ReactionTableView &view, std::span< uint8_t > stepped, const MultiresStepOptions &options, uint32_t wakeMark, std::span< BlockStepResult > results)
reaction::RxCell & CellAt(MultiresNest &nest, uint32_t slot, uint32_t index)
bool IsImplicitBlock(std::span< const uint8_t > implicitBlocks, uint32_t slot)
void AddImplicitConduction(MultiresNest &nest, CellThermals &thermals, std::span< const uint8_t > frozen, std::span< const uint8_t > implicitBlocks, const MultiresStepOptions &options, std::span< MrEnergyDelta > deltas)
constexpr uint32_t MULTIRES_MAX_SUBCYCLE_GAP
RxCell LoadNestCell(const MultiresNest &nest, uint32_t slot, uint32_t index)
std::vector< reaction::RxCell > cells
std::vector< uint32_t > index
MultiresCapacity capacity
std::vector< multires::MrBlock > blocks
std::vector< uint32_t > freeFractions
std::array< uint32_t, multires::MR_COUNTER_COUNT > counters
int32_t subcycleBaseLevel
std::array< MrFaceNeighbor, MR_FACES > crossNeighbors
std::array< int64_t, MR_FACES > crossFlows