MappingLUT
Source:
MappingLUT.h
MappingLUT¶
src/light/layers/MappingLUT.h:19Lookup table mapping logical light indices to physical light indices.
Four mapping types describe how logical lights relate to physical lights: 1:1 identical (logical index == physical index — a grid with no serpentine and no modifiers, no table needed), 1:1 shuffled (each logical → one physical, reordered — a serpentine grid), 1:0 unmapped (logical has no physical output — a sparse layout like a wheel), and 1:N multimap (logical → multiple physical — a mirror/clone modifier). The last three all need a table.
API: the code API answers one question — does this LUT have a mapping table? hasLUT returns true when a table is allocated (1:1 shuffled, 1:0, 1:N). setIdentity(count) sets the table-free identity mode (hasLUT false; forEachDestination(i, cb) calls cb(i)). build(logicalCount, maxDest) allocates the CSR arrays. Callers don't need to know which mapping type is used — Drivers checks hasLUT to decide whether to allocate an output buffer, BlendMap checks it to choose between memcpy (identity) and LUT-based mapping. Naming: setIdentity / hasLUT rather than a "one-to-one" flag, because "one-to-one" reads as covering all 1:1 mappings, but the table-free fast path applies only to the sequential identity case.
Storage (CSR): two arrays — offsets_[li]..offsets_[li+1] index a run of physical destinations for logical light li, and the destinations array holds the flat list of physical indices. nrOfLightsType is uint16_t on no-PSRAM, uint32_t on PSRAM. totalDestinations is provided by the Layouts container, so destinations are always within valid bounds.
Paged destinations (no-PSRAM fragmentation fallback): the destinations array can be large for a many-to-one modifier on a big grid (a 128×128 XY mirror → 32768 entries × 2 B ≈ 64 KB). On a no-PSRAM ESP32 the largest contiguous free block can be smaller than that even when total free heap is fine — a fragmentation cliff, not exhaustion. So when a single block won't allocate but total heap allows it, destinations are split into fixed-size power-of-two PAGES that each fit a fragmented heap. Paging is the exception, not the rule: PSRAM boards (alloc is PSRAM-first → one huge block) and every no-PSRAM case where the single block fits keep the flat single array and the flat hot-path walk, byte-identical to a non-paged build. Only the one failing config (no-PSRAM + large grid + fragmented heap) pages, where the alternative is the modifier silently degrading to 1:1. offsets_ is always a single small allocation; output is identical either way, so paging is purely an allocation detail.
Prior art: MoonLight's PhysMap — a memory-optimal union (2 B no-PSRAM / 4 B PSRAM) with the map type stored in each entry, oneToOneMapping / allOneLight fast-path flags, and forEachLightIndex for 1:N iteration (https://github.com/ewowi/MoonLight/blob/main/src/MoonLight/Layers/PhysMap.h). projectMM renames oneToOneMapping → setIdentity / !hasLUT for the reason above.
Public Methods¶
MappingLUT() = default
: Defaulted constructor.
MappingLUT(constMappingLUT &) = delete
: Deleted constructor.
inline void setIdentity(nrOfLightsType count)
: Fast path: logical == physical, no table needed. hasLUT returns false.
inline bool build(nrOfLightsType logicalCount, nrOfLightsType maxDestinations)
: Allocate CSR arrays for 1:N mapping.
inline void setMapping(nrOfLightsType logicalIdx, const nrOfLightsType * physicals, nrOfLightsType count)
: Fill one logical entry's destinations (call sequentially, idx 0..logicalCount-1)
inline void finalize()
: Call after all setMapping calls to close the last offset.
inline void free()
inline bool hasLUT() const
inline bool isPaged() const
inline nrOfLightsType logicalCount() const
inline nrOfLightsType destinationCount() const
inline bool overwrites() const
: Whether each physical destination is written by at most one logical light.
inline void setOverwrites(bool v)
inline size_t memoryUsed() const
: Memory accounting — actual bytes used, not capacity (destinations may be over-allocated), 0 for identity.
template<typenameF> inline void forEachDestination(nrOfLightsType logicalIdx, F && callback) const
: Hot-path: iterate physical destinations for a logical index.
Public Static Attributes¶
constexpr nrOfLightsType kPageEntries = 4096
: Destinations page size.
constexpr nrOfLightsType kPageShift = 12
constexpr nrOfLightsType kPageMask = - 1
constexprint kMaxPages = 64
Public Static Methods¶
static inline size_t estimateBytes(nrOfLightsType logicalCount, nrOfLightsType maxDest)