FastLED 3.9.15
Loading...
Searching...
No Matches

◆ showPixels()

void fl::Channel::showPixels ( PixelController< RGB, 1, 0xFFFFFFFF > & pixels)
overrideprotected

Definition at line 449 of file channel.cpp.hpp.

449 {
451
452 // Safety check: don't modify buffer if driver is currently transmitting it
453 if (mChannelData->isInUse()) {
454 FL_WARN("Channel '" << mName << "': showPixels() called while mChannelData is in use by driver, attempting to wait");
455 auto driver = mDriver.lock();
456 if (!driver) {
457 FL_ERROR("Channel '" << mName << "': No driver bound yet the mChannelData is in use - cannot transmit");
458 return;
459 }
460 // wait until the driver is in a READY state.
461 bool ok = driver->waitForReady();
462 if (!ok) {
463 FL_ERROR("Channel '" << mName << "': Timeout occurred while waiting for driver to become READY");
464 return;
465 }
466 FL_WARN("Channel '" << mName << "': Engine became READY after waiting");
467 }
468
469 // Phase 5b of #2428: if the driver was pre-bound via setDriver() (legacy
470 // addLeds<>-style controllers naming BusTraits<Bus::X>::instancePtr() in
471 // their constructor), bypass ChannelManager entirely. Channels created via
472 // the manager-based API (Channel::create(cfg) without affinity) keep their
473 // existing per-frame re-selection so users can swap drivers at runtime.
474 //
475 // **Fast path (#2773 item 2.1):** the legacy `addLeds<NEOPIXEL>` flow is
476 // by far the hot per-frame path on stock Blink. It needs no busKey
477 // construction, no dynamic driver lookup, no busKey-miss diagnostics,
478 // and no fallback `FL_ERROR` reporting — the driver was already pre-bound
479 // in the controller's constructor. Pulling all of that boilerplate out
480 // of `showPixels` lets the compiler keep the hot path compact and lets
481 // the slow path's `fl::string` ops / `ChannelManager::selectDriverForChannel`
482 // / diagnostic literals tree-shake on the slow-path branch's coldness.
483 fl::shared_ptr<IChannelDriver> driver;
484 if (mDriverPreBound) {
485 driver = mDriver.lock();
486 if (!driver) {
487 // Pre-bound driver got destroyed (singleton shutdown, etc.). Silent
488 // bail — this is unrecoverable from showPixels.
489 return;
490 }
491 } else {
492#if !defined(FASTLED_DISABLE_DYNAMIC_DRIVER) || !FASTLED_DISABLE_DYNAMIC_DRIVER
493 driver = resolveDynamicDriver();
494 if (!driver) {
495 return;
496 }
497#else
498 // Dynamic-driver lookup gated out via FASTLED_DISABLE_DYNAMIC_DRIVER
499 // (#2926). The else branch is dead at runtime for every legacy
500 // `addLeds<>` flavor — those pre-bind their driver in the ctor. The
501 // gate lets `--gc-sections` drop the resolveDynamicDriver body plus
502 // the ChannelManager::findDriverByName / selectDriverForChannel
503 // chain (~400-900 B). Channels created via `Channel::create(cfg)`
504 // without a pre-bound driver silently emit nothing under this flag —
505 // user accepts the constraint.
506 return;
507#endif
508 }
509
510 // Build pixel iterator with optional addressing transformation
511 // (#2558) Pass both Rgbw and Rgbww from the channel options; the iterator
512 // carries both, and the encoder dispatch below picks the right path based
513 // on which variant alternative ChannelOptions::mWhiteCfg holds.
514 ReorderingPixelIteratorAny iterator(pixels, mScreenMap.getXYMap(), mRgbOrder,
516 mName);
517 PixelIterator& pixelIterator = iterator.get();
518
519 // Encode pixels based on chipset type
520 auto& data = mChannelData->getData();
521 data.clear();
522
523 if (mChipset.is<ClocklessChipset>()) {
524 // Clockless chipsets: dispatch based on encoder type
525 const ClocklessChipset* clockless = mChipset.ptr<ClocklessChipset>();
526 switch (clockless->encoder) {
528 pixelIterator.writeWS2812(&data);
529 break;
530#if !defined(FASTLED_DISABLE_UCS7604) || !FASTLED_DISABLE_UCS7604
531 // Gated by FASTLED_DISABLE_UCS7604 (#2920). For WS2812-only
532 // sketches the UCS7604 case is dead at runtime, but each
533 // `writeUCS7604(...)` reference is statically reachable,
534 // keeping the encoder bodies linked. Setting
535 // `-DFASTLED_DISABLE_UCS7604=1` drops the case + the
536 // `encodeUCS7604_16bit_RGB` / `encodeUCS7604_16bit_RGBW`
537 // template instantiations (~400-600 B). When the gate is
538 // enabled, calling showPixels() on a UCS7604 channel
539 // silently emits nothing.
543 writeUCS7604(&data, pixelIterator, clockless->encoder,
545 break;
546#endif // !FASTLED_DISABLE_UCS7604
547 }
548#if !defined(FASTLED_DISABLE_SPI_CHIPSETS) || !FASTLED_DISABLE_SPI_CHIPSETS
549 } else if (mChipset.is<SpiChipsetConfig>()) {
550 // SPI chipsets: dispatch based on chipset type (zero allocation).
551 //
552 // Gated by FASTLED_DISABLE_SPI_CHIPSETS (#2913). For NEOPIXEL-only
553 // sketches on ESP32-S3 the SPI branch is dead at runtime, but the
554 // compiler cannot prove that — each pixelIterator.writeXXX(...)
555 // reference below is statically reachable, keeping ~720 B of
556 // encoder bodies (writeAPA102, writeSK9822, writeLPD8806,
557 // writeSM16716) plus the 11-case switch table linked. Setting
558 // `-DFASTLED_DISABLE_SPI_CHIPSETS=1` in build_flags drops the
559 // whole branch and recovers ~1.0-1.2 KB on a NEOPIXEL Blink.
560 //
561 // When the gate is enabled, calling showPixels() on an
562 // SpiChipsetConfig channel silently emits nothing — the user
563 // accepts that constraint by setting the flag.
564 const SpiChipsetConfig* spi = mChipset.ptr<SpiChipsetConfig>();
565 const SpiEncoder& config = spi->timing;
566
567 // Switch on enum WITHOUT default case - compiler will warn if new enum values are added
568 // TODO: Consolidate these PixelIterator methods with template controllers in src/fl/chipsets/
569 switch (config.chipset) {
573 pixelIterator.writeAPA102(&data, false);
574 break;
575
579 pixelIterator.writeAPA102(&data, true);
580 break;
581
583 pixelIterator.writeSK9822(&data, false);
584 break;
585
587 pixelIterator.writeSK9822(&data, true);
588 break;
589
591 pixelIterator.writeWS2801(&data);
592 break;
593
595 pixelIterator.writeWS2803(&data);
596 break;
597
599 pixelIterator.writeP9813(&data);
600 break;
601
603 pixelIterator.writeLPD8806(&data);
604 break;
605
607 pixelIterator.writeLPD6803(&data);
608 break;
609
611 pixelIterator.writeSM16716(&data);
612 break;
613
615 pixelIterator.writeHD108(&data);
616 break;
617 }
618 // No default case - compiler will error if any enum value is missing
619 }
620#endif // !FASTLED_DISABLE_SPI_CHIPSETS
621
622 // Fire event after encoding completes
623 {
624 auto& events = ChannelEvents::instance();
625 events.onChannelDataEncoded(*this, *mChannelData);
626 }
627
628
629
630 // #2517: detect the silent-drop scenario before enqueuing — if the
631 // resolved driver is registered with ChannelManager but currently
632 // disabled (typically by `FastLED.setExclusiveDriver<OtherBus>()`),
633 // `ChannelManager::onEndFrame()` will skip its `show()` call and the
634 // frame is silently dropped on the floor. Emit an actionable
635 // FL_ERROR so users can diagnose the missing output.
636 //
637 // One-shot via `mDisabledDriverWarned` to avoid per-frame spam.
638 // Reset the latch whenever the driver returns to ENABLED so a later
639 // disable re-emits the diagnostic.
640 fl::string driverName = driver->getName();
641 auto status = ChannelManager::instance().driverStatus(driverName);
643#if FASTLED_LOG_RUNTIME_ENABLED
646 mName, driverName,
647 ChannelManager::instance().exclusiveDriverName());
649 }
650#endif
651 // Skip the enqueue — the data wouldn't be sent anyway, and dropping
652 // it here matches the existing behaviour (rather than leaving stale
653 // bytes in the driver's pending queue across an enable/disable flip).
654 // The drop happens in both debug and release; only the diagnostic
655 // and the one-shot latch are gated. In release, the empty
656 // emitDisabledDriverError + the exclusiveDriverName() call + the
657 // 3-arg fl::string chain dead-strip (see #2950).
658 return;
660#if FASTLED_LOG_RUNTIME_ENABLED
661 // Reset the one-shot guard so a future disable re-emits the diagnostic.
662 mDisabledDriverWarned = false;
663#endif
664 }
665 // NOT_REGISTERED: driver is not managed by ChannelManager (e.g. a custom
666 // test driver, or one that was removed). Fall through to enqueue and let
667 // the driver decide what to do — this is the historic behaviour.
668
669 // Enqueue for transmission (will be sent when driver->show() is called)
670 driver->enqueue(mChannelData);
671 auto& events = ChannelEvents::instance();
672 events.onChannelEnqueued(*this, driver->getName());
673}
FL_NO_INLINE fl::shared_ptr< IChannelDriver > resolveDynamicDriver()
Cold slow-path helper for showPixels() when the driver was NOT pre-bound via setDriver().
EOrder mRgbOrder
Definition channel.h:255
ChipsetVariant mChipset
Definition channel.h:254
bool mDriverPreBound
Definition channel.h:257
fl::ScreenMap mScreenMap
Definition channel.h:280
ChannelDataPtr mChannelData
Definition channel.h:279
bool mDisabledDriverWarned
Definition channel.h:270
fl::weak_ptr< IChannelDriver > mDriver
Definition channel.h:256
fl::string mName
Definition channel.h:277
ChannelOptions mSettings
Definition channel.h:278
@ STATUS_ENABLED
Driver is registered and enabled.
Definition manager.h:173
@ STATUS_DISABLED
Driver is registered but disabled.
Definition manager.h:172
static ChannelManager & instance() FL_NOEXCEPT
Get the global singleton instance.
DriverStatus driverStatus(const fl::string &name) const FL_NOEXCEPT
Look up a driver's registration status without logging on miss.
void writeHD108(CONTAINER_UIN8_T *out) FL_NOEXCEPT
Encode pixels in HD108 format (zero allocation)
void writeAPA102(CONTAINER_UIN8_T *out, bool hd_gamma=false) FL_NOEXCEPT
Encode pixels in APA102/DOTSTAR format (zero allocation)
void writeP9813(CONTAINER_UIN8_T *out) FL_NOEXCEPT
Encode pixels in P9813 format (zero allocation)
void writeWS2801(CONTAINER_UIN8_T *out) FL_NOEXCEPT
Encode pixels in WS2801 format (zero allocation)
void writeSM16716(CONTAINER_UIN8_T *out) FL_NOEXCEPT
Encode pixels in SM16716 format (zero allocation)
void writeLPD6803(CONTAINER_UIN8_T *out) FL_NOEXCEPT
Encode pixels in LPD6803 format (zero allocation)
void writeWS2812(CONTAINER_UIN8_T *out) FL_NOEXCEPT
void writeSK9822(CONTAINER_UIN8_T *out, bool hd_gamma=false) FL_NOEXCEPT
Encode pixels in SK9822 format (zero allocation)
void writeWS2803(CONTAINER_UIN8_T *out) FL_NOEXCEPT
Encode pixels in WS2803 format (zero allocation)
void writeLPD8806(CONTAINER_UIN8_T *out) FL_NOEXCEPT
Encode pixels in LPD8806 format (zero allocation)
const XYMap * getXYMap() const FL_NOEXCEPT
Get the source XYMap as a raw const pointer.
#define FL_WARN(X)
Definition log.h:276
#define FL_ERROR(X)
Definition log.h:219
static FL_NO_INLINE void emitDisabledDriverError(const fl::string &channelName, const fl::string &driverName, const fl::string &exclusive) FL_NOEXCEPT
Out-of-line cold-path emitter for the #2517 silent-drop DISABLED-driver diagnostic in Channel::showPi...
void writeUCS7604(fl::vector_psram< u8 > *data, PixelIterator &pixelIterator, ClocklessEncoder encoder, const ChannelOptions &settings, EOrder rgbOrder)
Encode UCS7604 pixel data into the channel data buffer.
@ CLOCKLESS_ENCODER_UCS7604_8BIT
UCS7604 8-bit 800KHz.
@ CLOCKLESS_ENCODER_UCS7604_16BIT
UCS7604 16-bit 800KHz.
@ CLOCKLESS_ENCODER_UCS7604_16BIT_1600
UCS7604 16-bit 1600KHz.
@ CLOCKLESS_ENCODER_WS2812
Default, no preamble (WS2812 and compatible)
@ SM16716
SM16716 LED chipset.
@ LPD6803
LPD6803 LED chipset.
@ APA102HD
APA102 LED chipset with 5-bit gamma correction.
@ HD107
Same as APA102, but in turbo 40-mhz mode.
@ DOTSTARHD
APA102HD LED chipset alias.
@ SK9822HD
SK9822 LED chipset with 5-bit gamma correction.
@ SK9822
SK9822 LED chipset.
@ DOTSTAR
APA102 LED chipset alias.
@ P9813
P9813 LED chipset.
@ APA102
APA102 LED chipset.
@ HD108
16-bit variant of HD107, always gamma corrected. No SD (standard definition) option available - all H...
@ LPD8806
LPD8806 LED chipset.
@ WS2803
WS2803 LED chipset.
@ WS2801
WS2801 LED chipset.
@ HD107HD
Same as APA102HD, but in turbo 40-mhz mode.
fl::PixelIterator PixelIterator
static ChannelEvents & instance()
Rgbww rgbww() const FL_NOEXCEPT
Definition options.h:61
Rgbw rgbw() const FL_NOEXCEPT
Definition options.h:55
#define FL_SCOPED_TRACE
Definition trace.h:128

References fl::APA102, fl::APA102HD, fl::CLOCKLESS_ENCODER_UCS7604_16BIT, fl::CLOCKLESS_ENCODER_UCS7604_16BIT_1600, fl::CLOCKLESS_ENCODER_UCS7604_8BIT, fl::CLOCKLESS_ENCODER_WS2812, fl::DOTSTAR, fl::DOTSTARHD, fl::ChannelManager::driverStatus(), fl::ClocklessChipset::encoder, FL_ERROR, FL_SCOPED_TRACE, FL_WARN, fl::HD107, fl::HD107HD, fl::HD108, fl::ChannelEvents::instance(), fl::ChannelManager::instance(), fl::LPD6803, fl::LPD8806, mChannelData, mChipset, mDisabledDriverWarned, mDriver, mDriverPreBound, mName, mRgbOrder, mScreenMap, mSettings, fl::P9813, resolveDynamicDriver(), fl::SK9822, fl::SK9822HD, fl::SM16716, fl::ChannelManager::STATUS_DISABLED, fl::ChannelManager::STATUS_ENABLED, fl::PixelIterator::writeAPA102(), fl::PixelIterator::writeHD108(), fl::PixelIterator::writeLPD6803(), fl::PixelIterator::writeLPD8806(), fl::PixelIterator::writeP9813(), fl::PixelIterator::writeSK9822(), fl::PixelIterator::writeSM16716(), fl::PixelIterator::writeWS2801(), fl::PixelIterator::writeWS2803(), fl::PixelIterator::writeWS2812(), fl::WS2801, and fl::WS2803.

+ Here is the call graph for this function: