FastLED 3.10.9
Loading...
Searching...
No Matches
slim_bridge_controller.h
Go to the documentation of this file.
1
12
13#pragma once
14
16#include "fl/stl/noexcept.h"
17#include "fl/stl/vector.h"
18
20#include "fl/channels/data.h"
21#include "fl/channels/driver.h"
22#include "fl/channels/manager.h"
23#include "fl/channels/bus_traits.h" // IWYU pragma: keep (DriverTraits<Bus::X> satisfies the DriverTraits contract)
24#include "fl/channels/config.h"
26#include "fl/log/log.h"
28
29namespace fl {
30
33template <bool PLATFORM_DEFAULT = !FL_PLATFORM_HAS_LARGE_MEMORY>
35#ifdef FL_SLIM_BRIDGE_DIRECT_SHOW
36 static constexpr bool value = (FL_SLIM_BRIDGE_DIRECT_SHOW != 0);
37#else
38 static constexpr bool value = PLATFORM_DEFAULT;
39#endif
40};
41
42namespace detail {
43template <typename Traits>
44auto reject_legacy_frame(int) FL_NO_EXCEPT -> decltype(Traits::rejectLegacyFrame(), void()) {
45 Traits::rejectLegacyFrame();
46}
47
48template <typename Traits>
50
51template <typename Traits>
52auto show_batched_legacy(int) FL_NO_EXCEPT -> decltype(Traits::showLegacy(), void()) {
53 Traits::showLegacy();
54}
55
56template <typename Traits>
58 IChannelDriver& driver = Traits::instance();
59 driver.show();
60 driver.waitForReady();
61}
62} // namespace detail
63
66template <bool DIRECT_SHOW, bool BATCHED = false>
68
71template <bool BATCHED>
72struct SlimBridgeShowPolicy<false, BATCHED> {
73 template <typename DriverTraits>
74 static void attach() FL_NO_EXCEPT { DriverTraits::registerWithManager(); }
75 static bool enabled(IChannelDriver& driver) FL_NO_EXCEPT {
77 }
79 template <typename DriverTraits>
80 static void endShow() FL_NO_EXCEPT {}
81};
82
85template <>
86struct SlimBridgeShowPolicy<true, false> {
87 template <typename DriverTraits>
88 static void attach() FL_NO_EXCEPT {}
89 static bool enabled(IChannelDriver&) FL_NO_EXCEPT { return true; }
90 static void afterEnqueue(IChannelDriver& driver) FL_NO_EXCEPT { driver.show(); }
91 template <typename DriverTraits>
92 static void endShow() FL_NO_EXCEPT {}
93};
94
97template <>
98struct SlimBridgeShowPolicy<true, true> : SlimBridgeShowPolicy<true, false> {
100 template <typename DriverTraits>
101 static void endShow() FL_NO_EXCEPT {
102 // RMT4 supplies autonomous completion. Other batched drivers retain
103 // the bounded synchronous fallback until they supply that capability.
105 }
106};
107
145template <int DATA_PIN, typename TIMING, EOrder RGB_ORDER, int WAIT_TIME, typename DriverTraits,
146 int XTRA0 = 0, bool DIRECT_SHOW = SlimBridgeDirectShow<>::value,
147 bool BATCHED_DIRECT_SHOW = false>
148class SlimBridgeController : public CPixelLEDController<RGB_ORDER> {
150
151public:
154 // (A?) Fold WAIT_TIME (microseconds) into reset_us, mirroring the
155 // historical ClocklessIdf4 behavior: the driver must honor whichever
156 // is longer -- the chipset's own latch/reset requirement, or the
157 // caller-requested minimum inter-frame wait.
158 if (WAIT_TIME > 0 && static_cast<u32>(WAIT_TIME) > timing.reset_us) {
159 timing.reset_us = static_cast<u32>(WAIT_TIME);
160 }
161 // A clockless controller bound to a unified parallel-IO bus needs
162 // that engine's clockless pipeline linked (#4793).
166 FL_STATIC_ASSERT(XTRA0 >= 0 && XTRA0 <= 32, "XTRA0 out of range");
167 mData->setExtraZeroBitsPerByte(static_cast<u8>(XTRA0));
168 // Idempotent by contract -- safe to call from every instantiation's
169 // constructor (mirrors ClocklessIdf5's registerWithManager() call).
170 // A no-op in direct-show mode.
171 Policy::template attach<DriverTraits>();
172 }
173
174 void init() FL_NO_EXCEPT override {}
175
176 void endShowLeds(void* data) FL_NO_EXCEPT override {
178 // FastLED enqueues every controller before calling any endShowLeds,
179 // so the first direct controller flushes the complete parallel batch.
180 Policy::template endShow<DriverTraits>();
181 }
182
184 const ChannelDataPtr& channelData() const FL_NO_EXCEPT { return mData; }
185
186protected:
188 IChannelDriver& driver = DriverTraits::instance();
189
190 // (A1) Respect runtime driver enable/disable: ChannelManager is the
191 // single source of truth. A disabled driver means "drop this frame",
192 // not "enqueue and hope" -- silently enqueuing to a disabled driver
193 // is the #2517 silent-drop failure mode.
194 if (!Policy::enabled(driver)) {
195 FL_WARN_ONCE("SlimBridgeController: driver '" << driver.getName().c_str() << "' is disabled - dropping frame");
196 return;
197 }
198
199 // (A8) If the previous frame's buffer is still in flight, block
200 // (bounded) until the driver reports it can accept more work rather
201 // than racing the driver's in-progress DMA/RMT transfer.
202 if (mData->isInUse()) {
203 if (!driver.waitForReady()) {
204 FL_WARN_ONCE("SlimBridgeController: driver '" << driver.getName().c_str() << "' did not become ready in time - dropping frame");
205 return;
206 }
207 }
208
209 // (A5) Publish the pixel byte layout so the driver (and any
210 // downstream padding/diagnostic code) knows the encoded stride.
211 if (this->getRgbww().active()) {
212 mData->setPixelFormat(ChannelPixelFormat::RGBWW);
213 } else if (this->getRgbw().active()) {
214 mData->setPixelFormat(ChannelPixelFormat::RGBW);
215 } else {
216 mData->setPixelFormat(ChannelPixelFormat::RGB);
217 }
218
219 const int pixelCount = pixels.size();
220 const fl::size bytesPerPixel = channelPixelFormatBytesPerPixel(mData->getPixelFormat());
221 if (pixelCount < 0 || bytesPerPixel == 0 ||
222 static_cast<fl::size>(pixelCount) > static_cast<fl::size>(-1) / bytesPerPixel) {
224 FL_WARN_ONCE("SlimBridgeController: invalid encoded frame size - dropping frame");
225 return;
226 }
227 const fl::size expectedSize = static_cast<fl::size>(pixelCount) * bytesPerPixel;
228 auto &encoded = mData->getData();
229 // Reserve the complete snapshot before clearing the old one. Byte-wise
230 // geometric growth could fail partway through encoding and enqueue a
231 // silently truncated frame; exact reservation also bounds peak heap.
232 encoded.reserve(expectedSize);
233 if (encoded.capacity() < expectedSize) {
235 FL_WARN_ONCE("SlimBridgeController: cannot allocate encoded frame - dropping frame");
236 return;
237 }
238
239 // Observe accepted frames only after allocation succeeds (e.g. feeding
240 // ActiveStripTracker for stub/WASM channels).
241 onBeforeEncode(pixels);
242
243 // (A3) Re-encode into the same buffer every frame -- clear() keeps
244 // capacity, so steady-state operation is allocation-free.
245 encoded.clear();
246 fl::PixelIterator iterator(&pixels, this->getRgbw(), this->getRgbww());
247 iterator.writeWS2812(&encoded);
248 if (encoded.size() != expectedSize) {
249 encoded.clear();
251 FL_WARN_ONCE("SlimBridgeController: incomplete encoded frame - dropping frame");
252 return;
253 }
254
255 driver.enqueue(mData);
256 Policy::afterEnqueue(driver);
257 }
258
261 virtual void onBeforeEncode(PixelController<RGB_ORDER>& pixels) FL_NO_EXCEPT { (void)pixels; }
262
263private:
264 ChannelDataPtr mData;
265};
266
267} // namespace fl
#define DATA_PIN
Definition ClientReal.h:82
#define RGB_ORDER
Per-driver traits keyed on fl::Bus.
CPixelLEDController(RegistrationMode mode) FL_NO_EXCEPT
Protected constructor with registration mode.
Rgbww getRgbww() const FL_NO_EXCEPT
VIRTUAL_IF_NOT_AVR void endShowLeds(void *data) FL_NO_EXCEPT
Rgbw getRgbw() const FL_NO_EXCEPT
static ChannelDataPtr create(const ChipsetVariant &chipset, fl::vector_psram< u8 > &&encodedData=fl::vector_psram< u8 >(), ChannelPixelFormat pixelFormat=ChannelPixelFormat::RGB) FL_NO_EXCEPT
Create channel transmission data (modern variant-based API)
Definition data.cpp.hpp:12
static ChannelManager & registry() FL_NO_EXCEPT
Get the singleton WITHOUT registering platform default drivers.
bool isDriverEnabled(const char *name) const FL_NO_EXCEPT
Check if a driver is enabled by name.
virtual fl::string getName() const FL_NO_EXCEPT
Get the driver name for affinity binding.
Definition driver.h:217
virtual void enqueue(ChannelDataPtr channelData) FL_NO_EXCEPT=0
Enqueue channel data for transmission.
virtual void show() FL_NO_EXCEPT=0
Trigger transmission of enqueued data.
bool waitForReady(u32 timeoutMs=1000) FL_NO_EXCEPT
Wait for driver to become READY.
Minimal interface for LED channel transmission drivers.
Definition driver.h:52
void writeWS2812(CONTAINER_UIN8_T *out) FL_NO_EXCEPT
void endShowLeds(void *data) FL_NO_EXCEPT override
const ChannelDataPtr & channelData() const FL_NO_EXCEPT
Expose the underlying ChannelDataPtr for conformance tests.
SlimBridgeShowPolicy< DIRECT_SHOW, BATCHED_DIRECT_SHOW > Policy
virtual void onBeforeEncode(PixelController< RGB_ORDER > &pixels) FL_NO_EXCEPT
Called once per accepted frame, after the enabled/ready gates pass and BEFORE the frame is encoded an...
void showPixels(PixelController< RGB_ORDER > &pixels) FL_NO_EXCEPT override
void init() FL_NO_EXCEPT override
Initialize the LED controller.
const char * c_str() const FL_NO_EXCEPT
defines the templated version of the CLEDController class
Channel transmission data - lightweight DTO for driver transmission.
fl::EOrder EOrder
Definition eorder.h:12
Non-templated low level pixel data writing class.
#define FL_WARN_ONCE(...)
Definition log.h:439
Centralized logging categories for FastLED hardware interfaces and subsystems.
Unified manager for channel drivers with priority-based fallback.
auto reject_legacy_frame(int) FL_NO_EXCEPT -> decltype(Traits::rejectLegacyFrame(), void())
auto show_batched_legacy(int) FL_NO_EXCEPT -> decltype(Traits::showLegacy(), void())
void enable_clockless() FL_NO_EXCEPT
Definition bus_traits.h:64
Compile-time linker keep-alive hook for a single fl::Bus.
Definition bus_info.h:57
unsigned char u8
Definition stdint.h:131
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
constexpr ChipsetTimingConfig makeTimingConfig() FL_NO_EXCEPT
Convert compile-time CHIPSET type to runtime timing config.
@ PLATFORM_DEFAULT
Use the recommended backend for the active platform (RMT on ESP32; FlexPWM on Teensy 4....
Definition types.h:10
u8 channelPixelFormatBytesPerPixel(ChannelPixelFormat format) FL_NO_EXCEPT
Definition data.h:33
Base definition for an LED controller.
How a SlimBridgeController reaches its driver.
#define FL_STATIC_ASSERT(...)
Portable compile-time assertion wrapper.
Pixel controller class.
u32 reset_us
Reset/latch time (microseconds)
Runtime bit-period timing for a clockless chipset.
Preserve explicit build overrides while allowing a backend to choose its legacy default without chang...
static void afterEnqueue(IChannelDriver &) FL_NO_EXCEPT
static bool enabled(IChannelDriver &driver) FL_NO_EXCEPT
static bool enabled(IChannelDriver &) FL_NO_EXCEPT
static void afterEnqueue(IChannelDriver &driver) FL_NO_EXCEPT
static void afterEnqueue(IChannelDriver &) FL_NO_EXCEPT