Skip to content

Conversation

@softhack007
Copy link
Collaborator

@softhack007 softhack007 commented Oct 21, 2025

  • optimization of code and supporting functions in the hot path of BusManager::setPixelColor() and BusManager::getPixelColor().
  • colorKtoRGB() bugfix: avoid math error related to log() returning -inf (corner case when kelvin < 1200)

Summary by CodeRabbit

  • Bug Fixes

    • Fixed color temperature handling to avoid incorrect wrapping and added validation for out-of-range values.
  • Breaking Changes

    • Pixel color API simplified: the explicit color-temperature parameter was removed from the public pixel-set call; color temperature is handled internally.
  • Performance

    • Faster per-pixel rendering and color correction via improved caching and hot-path optimizations.

…anager.cpp

colorBalanceFromKelvin() is only called from inside bus_manager.cpp, so we can help the compiler optimize by making it a local (static) fuction
* bug: logf(temp-10) result becomes NaN when kelvin < 1200
* bug (RISC-V only): parameter of Bus::setCCT must be signed, to avoid undefined behaviour
* minor optimization by replacing constrain() with min(max())
… hot path in sPC and gPC

* optimize loops that scan through all busses
* small speedups for  Bus::autoWhiteCalc()
* small speedups for ColorOrderMap::getPixelColorOrder()

thanks to github Copilot for giving me the right ideas for this optimization
@softhack007 softhack007 marked this pull request as draft October 21, 2025 11:46
@coderabbitai
Copy link

coderabbitai bot commented Oct 21, 2025

Walkthrough

The change moves Kelvin color-balance logic into the bus manager, removes the cct parameter from BusManager::setPixelColor, renames/repurposes a cached lastendlastlen, and applies multiple hot-path and unsigned-typing optimizations across bus and color code.

Changes

Cohort / File(s) Change Summary
API Signature & Header
wled00/bus_manager.h
BusManager::setPixelColor signature changed from (uint16_t pix, uint32_t c, int16_t cct) to (uint16_t pix, uint32_t c). Bus::setCCT parameter type changed from uint16_t to int16_t.
Bus manager implementation
wled00/bus_manager.cpp
colorBalanceFromKelvin moved in as static inline and cached per-Kelvin; lastend renamed to lastlen and used as unified length cache; setPixelColor fast-path reworked (null-check lastBus, cached-bus path); hot attributes added; unsigned-typing and loop/branch optimizations applied across pixel/bus routines.
Color utilities
wled00/colors.cpp
Kelvin-to-RGB (colorKtoRGB) gained kelvin range guard (1200–65000), explicit float cast for calculations, clamp replaced with min/max; the previous colorBalanceFromKelvin block disabled via #if 0.
Public declarations
wled00/fcn_declare.h
Public declaration of colorBalanceFromKelvin commented out/removed (function moved into bus_manager.cpp).
Minor refactors & attrs
wled00/...
Added __attribute__((hot)) to hot functions (e.g., ColorOrderMap::getPixelColorOrder, Bus::autoWhiteCalc), tighter integer typedefs (uint_fast8_t, uint_fast16_t), and branch-friendly arithmetic in several bus types (Digital, PWM, Network).

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant BusManager
  participant Bus
  participant ColorBalance

  rect rgb(240, 255, 240)
    Caller->>BusManager: setPixelColor(pix, color)
    note right of BusManager: new signature (no cct)\nuses cached lastBus/lastlen fast-path
  end

  alt cached fast-path (lastBus valid)
    BusManager->>Bus: writePixelFast(pix, color)
    Bus-->>BusManager: ack
  else slow-path or cache miss
    BusManager->>ColorBalance: (conditionally) colorBalanceFromKelvin(kelvin, color)
    ColorBalance-->>BusManager: balancedColor
    BusManager->>Bus: writePixel(pix, balancedColor)
    Bus-->>BusManager: ack
  end

  BusManager-->>Caller: return
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Poem

🐇 I hopped through bytes and Kelvin light,

I cached the hues to make them right,
One fewer arg, a leaner race,
Fast-paths hummed in their small space,
A rabbit's cheer for cleaner pace! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Bus level setPixelColor and getPixelColor optimizations" directly aligns with the primary objectives of this pull request. The raw_summary confirms extensive changes to BusManager::setPixelColor and getPixelColor functions with performance-oriented refactors, and the PR objectives explicitly state that optimization of these functions in the hot path is the main goal. The title is specific and concise, clearly identifying which functions are being optimized without vague language, making it immediately understandable to teammates reviewing the commit history.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bus_sPC_gPC_optimizations

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@softhack007 softhack007 changed the title Bus level setPixelColor and getPixelColor optimizations (WIP) Bus level setPixelColor and getPixelColor optimizations Oct 21, 2025
@softhack007 softhack007 marked this pull request as ready for review October 21, 2025 20:36
@softhack007
Copy link
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Oct 21, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
wled00/colors.cpp (2)

173-175: Verify performance claim for min/max over constrain().

The comment states min(max()) is faster than constrain(), but in most implementations, constrain() is a macro that expands to similar logic. This may be a micro-optimization with negligible benefit unless proven otherwise on this specific platform.

Consider benchmarking to confirm the performance improvement, or revert to constrain() for better readability if the difference is negligible.


325-341: Consider removing the disabled code block.

The colorBalanceFromKelvin function is wrapped in #if 0 since it has been moved to bus_manager.cpp. Consider removing this dead code entirely rather than leaving it disabled, as it may cause confusion for future maintainers.

If there's a reason to preserve this for reference or potential rollback, add a comment explaining why it's being kept.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 315f147 and 541b201.

📒 Files selected for processing (4)
  • wled00/bus_manager.cpp (6 hunks)
  • wled00/bus_manager.h (3 hunks)
  • wled00/colors.cpp (3 hunks)
  • wled00/fcn_declare.h (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
wled00/**/*.{cpp,h}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use spaces (2 per level) for C++ firmware source and headers in wled00

Files:

  • wled00/fcn_declare.h
  • wled00/bus_manager.h
  • wled00/bus_manager.cpp
  • wled00/colors.cpp
🧬 Code graph analysis (2)
wled00/bus_manager.h (3)
wled00/FX_fcn.cpp (9)
  • setCCT (566-576)
  • setCCT (566-566)
  • setCCT (2170-2176)
  • setCCT (2170-2170)
  • setPixelColor (941-941)
  • setPixelColor (1183-1213)
  • setPixelColor (1183-1183)
  • setPixelColor (1972-1977)
  • setPixelColor (1972-1972)
wled00/bus_manager.cpp (10)
  • setPixelColor (216-233)
  • setPixelColor (216-216)
  • setPixelColor (316-367)
  • setPixelColor (316-316)
  • setPixelColor (450-459)
  • setPixelColor (450-450)
  • setPixelColor (521-555)
  • setPixelColor (521-521)
  • setPixelColor (1087-1087)
  • setPixelColor (1305-1305)
wled00/bus_wrapper.h (1)
  • setPixelColor (719-838)
wled00/bus_manager.cpp (3)
wled00/colors.cpp (2)
  • colorKtoRGB (143-177)
  • colorKtoRGB (143-143)
wled00/FX_fcn.cpp (9)
  • i (702-712)
  • i (702-702)
  • i (713-719)
  • i (713-713)
  • setPixelColor (941-941)
  • setPixelColor (1183-1213)
  • setPixelColor (1183-1183)
  • setPixelColor (1972-1977)
  • setPixelColor (1972-1972)
wled00/bus_wrapper.h (1)
  • setPixelColor (719-838)
🔇 Additional comments (11)
wled00/colors.cpp (1)

145-153: Verify white channel value in fallback.

The safety guard correctly prevents logf domain errors, but rgb[3] = 0 sets the white channel to zero. For a pure white fallback (RGB 255,255,255), should rgb[3] also be 255, or is it intentionally left at 0 because this represents RGB-only white?

Consider clarifying the intent with a comment, or verify that downstream consumers expect rgb[3]=0 for this fallback case.

wled00/bus_manager.h (3)

178-180: Good fix for signed/unsigned conversion issue.

Changing the parameter type to int16_t correctly handles the sentinel value -1 (used to indicate "no CCT"). With uint16_t, -1 would wrap to 65535, causing undefined behavior.


466-466: Improved naming: lastend → lastlen.

Renaming lastend to lastlen improves clarity. The new name clearly indicates it stores the length of the cached bus segment, avoiding ambiguity about whether it represents an exclusive or inclusive end index.

Also applies to: 508-508


472-473: API change verified – all call sites correctly updated.

The setPixelColor signature change has been successfully implemented. The old signature (uint16_t pix, uint32_t c, int16_t cct) has been removed and replaced with (uint16_t pix, uint32_t c). Verification confirms:

  • No remaining calls use the removed three-parameter signature
  • CCT handling has been properly migrated to separate setCCT() methods (Segment, WS2812FX, and Bus classes)
  • All existing call sites use valid overloaded signatures: 2-arg (color), 4-arg (RGB), 5-arg (RGBA), or with anti-aliasing flag
  • References to CCT handling now flow through the dedicated setCCT() API rather than inline parameter passing
wled00/fcn_declare.h (1)

69-69: LGTM: Public declaration correctly removed.

The public declaration of colorBalanceFromKelvin has been appropriately removed since the function is now static inline in bus_manager.cpp. The comment clearly documents the relocation for better optimization.

wled00/bus_manager.cpp (6)

82-98: Good relocation for optimization, verify default correctionRGB initialization.

Moving colorBalanceFromKelvin to bus_manager.cpp as static inline allows the compiler to inline it at hot-path call sites, improving performance. The use of uint_fast16_t for calculations is also appropriate.

Note: The default correctionRGB is initialized to {255,255,255,0} (neutral white balance) instead of {0,0,0,0}. This is a better default, but verify this change is intentional and won't cause issues if colorKtoRGB is never called.


117-129: Excellent hot-path optimizations in getPixelColorOrder.

The optimizations are well-thought-out:

  • __attribute__((hot)) guides compiler optimization
  • Pre-computing swapW avoids repeated bit shifts
  • uint_fast8_t leverages native fast types
  • const auto & eliminates unnecessary copies
  • Unsigned arithmetic range check (uint16_t)(pix - m.start) < m.len is elegant and safe (underflow creates large value that fails the check)

132-153: Cleaner implementation with max/min functions.

The refactored autoWhiteCalc improves readability by using max() and min() instead of nested conditionals. The use of uint_fast8_t for channel values is appropriate for hot-path code.

Note: Modern compilers typically optimize nested conditionals and max()/min() calls to similar machine code, so the primary benefit here is improved readability and maintainability.


1305-1332: Well-implemented hot-path optimization with proper safeguards.

The fast-path caching in setPixelColor is a significant performance optimization for sequential pixel access patterns. The implementation correctly:

  • Checks lastBus for nullptr before dereferencing
  • Uses short-circuit evaluation to safely skip the fast path if cache is invalid
  • Falls back to full bus search when needed
  • Uses unsigned arithmetic for efficient range checking

The const pointer hints and uint_fast8_t loop variables should help compiler optimization.


1349-1374: Consistent optimization pattern applied to getPixelColor.

The same efficient caching and fast-path logic from setPixelColor is correctly applied here. The implementation maintains consistency across the API and provides the same performance benefits for read operations.


1376-1401: LGTM: Restored variant maintains optimization consistency.

The getPixelColorRestored implementation follows the same optimized pattern, correctly calling b->getPixelColorRestored() instead of b->getPixelColor(). This ensures consistent performance across all pixel access methods.

@softhack007 softhack007 marked this pull request as draft October 21, 2025 20:46
@softhack007 softhack007 self-assigned this Oct 21, 2025
previously gPC cached busses were disabled in slowMode (one pixel mapped to several outputs). However this should not be needed, because the first copy found should be like all other.
@softhack007
Copy link
Collaborator Author

softhack007 commented Oct 23, 2025

first test on HUB75 shows no regressions, and 2-4% speedup depending on effect.
However this is kind of a booooring testcase for my changes.

Things that should be faster now

  • Large setups with many (>4) outputs
  • White Balance correction
  • Color Order Override
  • FPS when preview is enabled (as getPixelColor can leverage on cached busses info now)

@softhack007 softhack007 changed the title (WIP) Bus level setPixelColor and getPixelColor optimizations Bus level setPixelColor and getPixelColor optimizations Oct 23, 2025
@softhack007 softhack007 marked this pull request as ready for review October 23, 2025 22:07
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
wled00/bus_manager.cpp (1)

1349-1374: Optimizations look good, but note the slowMode handling difference.

The fast-path optimization is well-implemented. However, lines 1364-1369 show commented-out slowMode checks, meaning this function always caches the last bus, unlike setPixelColor which respects slowMode. This asymmetry might be intentional (reads may benefit more from caching), but consider whether both functions should handle slowMode consistently.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 541b201 and 1f99aeb.

📒 Files selected for processing (1)
  • wled00/bus_manager.cpp (6 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
wled00/**/*.{cpp,h}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use spaces (2 per level) for C++ firmware source and headers in wled00

Files:

  • wled00/bus_manager.cpp
🧬 Code graph analysis (1)
wled00/bus_manager.cpp (4)
wled00/colors.cpp (9)
  • hot (11-43)
  • hot (83-111)
  • hot (327-340)
  • hot (437-443)
  • hot (445-449)
  • hot (469-472)
  • hot (475-487)
  • colorKtoRGB (143-177)
  • colorKtoRGB (143-143)
wled00/FX_fcn.cpp (18)
  • hot (941-1180)
  • hot (1215-1325)
  • hot (1406-1429)
  • hot (1466-1505)
  • hot (1508-1529)
  • hot (1534-1571)
  • hot (1616-1633)
  • i (702-712)
  • i (702-702)
  • i (713-719)
  • i (713-713)
  • setPixelColor (941-941)
  • setPixelColor (1183-1213)
  • setPixelColor (1183-1183)
  • setPixelColor (1972-1977)
  • setPixelColor (1972-1972)
  • getPixelColorRestored (1986-1991)
  • getPixelColorRestored (1986-1986)
wled00/FX_2Dfcn.cpp (2)
  • hot (198-204)
  • hot (221-230)
wled00/bus_manager.h (1)
  • getPixelColorRestored (318-318)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: Builds (esp8266_4MB_M)
  • GitHub Check: Builds (adafruit_matrixportal_esp32s3_tinyUF2)
  • GitHub Check: Builds (esp32s2_PSRAM_M)
  • GitHub Check: Builds (esp32c3dev_4MB_M)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_M)
  • GitHub Check: Builds (esp8266pro_16MB_M)
  • GitHub Check: Builds (esp8266_4MB_S)
  • GitHub Check: Builds (seeed_esp32c3_4MB_S)
  • GitHub Check: Builds (esp32_4MB_V4_HUB75_forum)
  • GitHub Check: Builds (esp32s2_PSRAM_S)
  • GitHub Check: Builds (esp32S3_8MB_S)
  • GitHub Check: Builds (esp32c3mini_dio_4MB_M)
  • GitHub Check: Builds (esp32S3_8MB_M)
  • GitHub Check: Builds (esp32S3_16MB_PSRAM_M_HUB75)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_S)
  • GitHub Check: Builds (esp32_16MB_V4_S)
  • GitHub Check: Builds (esp32_4MB_PSRAM_S)
  • GitHub Check: Builds (esp32_16MB_V4_M)
  • GitHub Check: Builds (esp32_4MB_V4_S)
  • GitHub Check: Builds (esp32S3_16MB_PSRAM_M_HUB75)
🔇 Additional comments (6)
wled00/bus_manager.cpp (6)

82-98: LGTM with a note on the initialization improvement.

The move to static inline and the change of correctionRGB initialization from {0,0,0,0} to {255,255,255,0} is correct. The new default represents neutral correction (no color change), whereas the old default would have incorrectly produced black output on the first call when kelvin is 0.


117-129: Well-optimized hot path implementation.

The unsigned arithmetic trick at line 124 efficiently checks whether pix is in the range [m.start, m.start + m.len) with a single comparison. Extracting the W swap information once before the loop and using const auto& for the mapping reference are good micro-optimizations.


132-153: LGTM! Cleaner and more efficient.

The simplified logic using max() and min() functions is more readable than nested conditionals while maintaining correctness. The use of uint_fast8_t is appropriate for these byte-sized color values.


1305-1332: Excellent hot-path optimizations with proper safety checks.

The fast-path cache with explicit null check on lastBus (line 1308) prevents potential null-pointer dereferences. The unsigned arithmetic trick at lines 1308 and 1321 efficiently performs range checks. The rename from lastend to lastlen better reflects what's being cached.

Note: The removal of the cct parameter is handled by moving CCT color correction into the individual bus implementations (e.g., lines 218, 320, 524).


1376-1401: Optimizations are correct and consistent with getPixelColor.

The implementation follows the same pattern as getPixelColor with proper fast-path caching and unsigned arithmetic range checks. The same slowMode handling consideration from getPixelColor applies here (lines 1391-1396 have commented-out checks).


1243-1243: LGTM - consistent with the cache variable rename.

The initialization of lastlen to 0 is correct and consistent with the rename from lastend to lastlen. This properly invalidates the cache when buses are added or removed.

Also applies to: 1284-1284

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants