Layer
Source:
Layer.h
Layer¶
src/light/layers/Layer.h:35Inherits:
MoonModule
A [Layer]MoonModule (role ModuleRole::Layer, child of the Layers container) owns a buffer, a mapping LUT, an ordered effect list, and an ordered modifier list, and references the shared Layouts that describes the physical topology.
Ownership: a Buffer (logical light data, sized to the logical box); a MappingLUT (logical lights to physical positions); effects (write lights into the buffer, dynamic heap-grown list, no fixed max); modifiers (transform the LUT or light values, same dynamic list).
Composition: two controls, blendMode and opacity, govern how this [Layer] composites onto the layers below it. They are inert on the [Layer] — it never reads them; a [Layer] can't know its position in the stack or what's beneath it. The Drivers container reads each enabled [Layer]'s two values plus the container child order and does the compositing (bottom layer overwrites, each layer above blends per its mode and opacity). The value lives here so it travels with the [Layer] through add / delete / reorder — no separate sync-prone blend list on Drivers. The blend math itself lives in BlendMap.
Buffer persistence: the buffer persists frame-to-frame — the [Layer] does NOT clear it. This is the FastLED / WLED / MoonLight convention: the buffer holds the previous frame so an effect can fade it for trails (fadeToBlackBy) or read prior pixels (a scroll, Game-of-Life). Each effect owns its background. rebuildLUT clears once on the cold path so a freshly added effect starts black.
rebuildLUT (cold path): called when a layout or modifier control changes. Reads physical dimensions from Layouts, folds the box through each enabled static modifier to compute the logical box, allocates the buffer and LUT, and for the common case (no modifier, dense grid in natural order) skips the table entirely with an identity mapping. The general path folds each physical light through the static chain to its logical cell via a textbook counting-sort CSR build.
render (hot path): runs each enabled effect in order (all write the same buffer), calls extrude after each effect to duplicate its written slice across the axes it doesn't iterate, then ticks each enabled modifier. A live (animated) modifier triggers the per-frame applyLivePass backward gather; a beat-driven one asks for a single coalesced rebuild.
extrude: lets a low-dimensional effect work on a higher-dimensional layer without per-effect changes — a D2 effect on a 3D layer has its z=0 slice copied across z, a D1 effect its x=0 column copied across x, then that row across the rest. Cost is zero for D3 effects (the default, an early return) and zero when the layer's unused axes are size 1 (a D2 effect on a 2D layer). Real memcpy work only happens when the layer has more dimensions than the effect writes.
Status: the status slot shows the LOGICAL box the effects render into (<w>×<h>×<d>), which can differ from the physical box shown on Layouts (a Mirror-XY modifier folds a 128×128 physical layout into a 64×64 logical box). The same slot carries memory-degradation warnings when a build can't fit (modifier mapping skipped, buffer reduced, buffer allocation failed, all — not enough memory), and a warning wins over the neutral box line.
Prior art: MoonLight's VirtualLayer — oneToOneMapping fast-path flag, virtualChannels per-layer buffer, effectDimension, a nodes vector for effects/modifiers, and forEachLight per-logical-light iteration that asks the modifier for physical destinations (https://github.com/ewowi/MoonLight/blob/main/src/MoonLight/Layers/VirtualLayer.h).

Public Attributes¶
uint8_t blendMode = 0
uint8_t opacity = 255
Public Methods¶
virtual inline ModuleRole role() const override
: Role for type identification (no RTTI needed).
virtual inline constchar * acceptsChildRoles() const override
: Comma-separated role names this module accepts as user-added children ("effect,modifier").
virtual inline void defineControls() override
: defineControls MUST be idempotent and pure: only controls_.clear() + controls_.addX().
inline BlendOp blendOp() const
: How this [Layer] composites when stacked above another (read by Drivers).
inline void setLayouts(Layouts * lg)
inline Layouts * layouts() const
inline void setChannelsPerLight(uint8_t cpl)
virtual inline void prepare() override
: Tier-3 of the control-change split (see onControlChanged above): the module (re)allocates / recomputes whatever derived state it owns — an effect's heap, a [Layer]'s mapping LUT, the Drivers output buffer.
virtual inline void tick() override
inline void ensureLiveScratch()
inline void applyLivePass()
inline void extrude(Dim effectDim)
: Copy the effect's written slice to fill the unused axes.
inline Buffer & buffer()
inline constBuffer & buffer() const
inline constMappingLUT & lut() const
inline lengthType width() const
inline lengthType height() const
inline lengthType depth() const
inline uint8_t channelsPerLight() const
inline uint32_t elapsed() const
inline void fadeToBlackBy(uint8_t amt)
inline nrOfLightsType physicalLightCount() const
inline lengthType physicalWidth() const
inline lengthType physicalHeight() const
inline lengthType physicalDepth() const
inline bool lutSkipped() const
inline void rebuildLUT()
: Cold path, called from prepare after physical dimensions are known.
inline bool isNaturalOrder() const
inline bool buildFoldedLUT(const Coord3D & logical, nrOfLightsType logicalCount, nrOfLightsType driverCount)
inline void clampLogical(Coord3D & logical) const
Public Static Attributes¶
constexpr nrOfLightsType kNoDriver = <nrOfLightsType>(-1)
Public Static Methods¶
static inline nrOfLightsType cellCount(const Coord3D & box)