EffectsLayer¶
Owns a double-buffered 3D pixel array that effects and modifiers write into. Renamed from
LightsProducerin 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¶
- src/modules/layers/EffectsLayer.h
- src/modules/layers/Channel.h
- tests/test_layers.cpp
- tests/test_module_manager.cpp
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.