Skip to content

EffectsLayer

Owns a double-buffered 3D pixel array that effects and modifiers write into. Renamed from LightsProducer in Release 3, Sprint 3b as part of the MoonLight-vocabulary alignment — the name now reflects the role (an effects layer that holds effects and modifiers) rather than the data direction.


What it does

EffectsLayer is a StatefulModule that owns two Channel buffers (the double buffer). An effect calls acquireWriteBuffer() to get the current write slot, fills it with pixels, then calls publish() to make it visible atomically. A consumer (typically a DriverLayer) calls readyChannel() to get the latest completed frame without a lock.

EffectsLayer::loop() itself is a no-op. Because child modules (e.g. SineEffectModule) are registered as children via parent_id, the Scheduler's parent-child dispatch calls each child's loop() immediately after the parent. The net effect: the parent's loop costs nothing, and children run in parent-first order within the same tick.


Effect interface

Channel* acquireWriteBuffer();  // get the write slot for this tick
void     publish();             // publish the just-written buffer and advance the write index

Call order per tick: acquireWriteBuffer() → fill pixels → publish(). Do not hold the pointer across ticks.

Consumer interface

Channel* readyChannel() const;  // atomic read — safe to call from any thread/core

Returns the latest completed frame, or nullptr before the first publish().


Props

Parameter Source Default Description
width setProps 10 Buffer width in pixels
height setProps 10 Buffer height in pixels
depth setProps 10 Buffer depth in pixels (1 = flat 2D)

Props are injected by the ModuleManager from the "props" field in state/modulemanager.json.


Lifecycle

Phase Action
setProps() Sets width, height, depth
setup() Allocates both pixel buffers
loop() No-op
teardown() Frees both pixel buffers; clears readyChannel() to nullptr

Platform notes

  • PSRAM allocation on ESP32-S3 via ps_malloc() when PSRAM is present; malloc() otherwise.
  • The atomic hand-off is lock-free on both ESP32 (Xtensa) and PC (x86/ARM).

Source

Test coverage

Effect and Driver Layers — zero residue, sizing via props and GridLayout child, start/end fractions, resize on driver resize, serpentine mapping.

Coord3D and Pixel Addressing — pixel layout matches EffectsLayer coordinate convention.

Live pipeline: test1 — EffectsLayer + DriverLayer pipeline on real hardware.