Skip to content

Update pixel buffer after changing matrix dimensions#5675

Open
DedeHai wants to merge 2 commits into
wled:mainfrom
DedeHai:fix_2D_setup_crash
Open

Update pixel buffer after changing matrix dimensions#5675
DedeHai wants to merge 2 commits into
wled:mainfrom
DedeHai:fix_2D_setup_crash

Conversation

@DedeHai

@DedeHai DedeHai commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Fix for #5669

Summary by CodeRabbit

  • Bug Fixes
    • Fixed pixel-frame buffer allocation during startup so the internal pixel memory is reliably (re)allocated after initialization.
    • Ensured pixel buffer size is updated whenever LED configuration or map changes are applied, preventing mismatches after LED-map loading or parsing.

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: df417402-8f82-4393-a5f7-9b27a4a98668

📥 Commits

Reviewing files that changed from the base of the PR and between b60cf6b and dfc4a33.

📒 Files selected for processing (1)
  • wled00/FX_fcn.cpp
💤 Files with no reviewable changes (1)
  • wled00/FX_fcn.cpp

Walkthrough

The PR centralizes _pixels allocation into updatePixelBuffer(), invokes it during startup immediately after finalizeInit(), and ensures deserializeMap() calls it whenever logical LED count changes so the frame buffer size stays synchronized.

Changes

Pixel Buffer Synchronization

Layer / File(s) Summary
Buffer allocation refactoring
wled00/FX_fcn.cpp, wled00/FX.h
Extracts buffer sizing/allocation into updatePixelBuffer() (computes requiredMem once, frees/reallocates _pixels, zeroes memory, logs size). finalizeInit() calls it after strip/matrix setup and the startup sequence now invokes it immediately after finalizeInit().
LED map deserialization buffer sync
wled00/FX_fcn.cpp
deserializeMap() records lengthTotalBefore around setUpMatrix() and conditionally calls strip.updatePixelBuffer() when strip.getLengthTotal() changes both for missing ledmap.json cases and after LED map parsing completes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • wled/WLED#5073: Both PRs modify wled00/FX_fcn.cpp in the LED map parsing/validation path.
  • wled/WLED#5023: Both PRs modify wled00/FX_fcn.cpp’s WS2812FX::deserializeMap() around LED/2D map handling.
  • wled/WLED#4783: Both PRs modify _pixels frame-buffer allocation in WS2812FX::finalizeInit().

Suggested labels

optimization

Suggested reviewers

  • willmmiles
  • blazoncek
  • netmindz
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Update pixel buffer after changing matrix dimensions' directly describes the main change: ensuring the _pixels buffer is resized/reallocated when matrix dimensions change via deserializeMap() and related setup flows.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

1285-1287: ⚡ Quick win

Add error handling for buffer allocation failure.

If allocate_buffer() returns nullptr, _pixels will be null and could cause issues before show() catches it. Following the defensive pattern used in Segment::setGeometry() (lines 495-503), check allocation success immediately.

🛡️ Proposed error handling
   _pixels = static_cast<uint32_t*>(allocate_buffer(requiredMem, BFRALLOC_ENFORCE_PSRAM | BFRALLOC_NOBYTEACCESS | BFRALLOC_CLEAR));
+  if (!_pixels) {
+    DEBUGFX_PRINTLN(F("!!! Not enough RAM for pixel buffer !!!"));
+    errorFlag = ERR_NORAM_PX;
+  }
   DEBUG_PRINTF_P(PSTR("strip buffer size: %uB\n"), requiredMem);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/FX_fcn.cpp` around lines 1285 - 1287, The allocation result from
allocate_buffer(requiredMem, BFRALLOC_ENFORCE_PSRAM | BFRALLOC_NOBYTEACCESS |
BFRALLOC_CLEAR) assigned to _pixels must be checked for nullptr immediately
(similar to Segment::setGeometry()); if it returns nullptr, log an error
including requiredMem and a clear message via DEBUG_PRINTF_P or process logger,
set a safe fallback state (e.g., leave _pixels null and set a flag or
return/error out from the calling init function) to avoid dereferencing before
show() is called, and ensure any subsequent code that assumes a valid buffer
respects that flag or early-returns; update the allocation site where _pixels is
assigned to perform this check and follow the same error-handling pattern used
in Segment::setGeometry.

1281-1281: 💤 Low value

Remove commented dead code.

This commented-out line serves no purpose and should be deleted.

🧹 Proposed cleanup
 void WS2812FX::updatePixelBuffer() {
-  //uint32_t currentMem = 0;
   uint32_t requiredMem = getLengthTotal() * sizeof(uint32_t);

As per coding guidelines: "Remove dead/unused code — justify or delete it" and "Unused or dead code must be justified or removed to keep the codebase clean, maintainable, and readable".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/FX_fcn.cpp` at line 1281, Remove the dead commented declaration
"//uint32_t currentMem = 0;" from FX_fcn.cpp; delete the commented line
referencing currentMem so the file no longer contains unused commented-out code
(no other changes needed).

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@wled00/FX_fcn.cpp`:
- Around line 1284-1285: The comment is incorrect about enforcing PSRAM when
using BFRALLOC_NOBYTEACCESS; on classic ESP32 (CONFIG_IDF_TARGET_ESP32)
allocate_buffer() will take the heap_caps_malloc_prefer(... MALLOC_CAP_32BIT,
MALLOC_CAP_SPIRAM, MALLOC_CAP_8BIT ...) path when BFRALLOC_NOBYTEACCESS is set,
meaning PSRAM is only a fallback rather than guaranteed; update the inline
comment on the _pixels allocation (near allocate_buffer usage) to state that
32-bit/IRAM is preferred and PSRAM is used only if the preferred 32-bit
allocation isn’t available, or remove BFRALLOC_ENFORCE_PSRAM when also passing
BFRALLOC_NOBYTEACCESS for classic ESP32; reference allocate_buffer,
BFRALLOC_NOBYTEACCESS, BFRALLOC_ENFORCE_PSRAM, heap_caps_malloc_prefer and
p_malloc when making the change.

---

Nitpick comments:
In `@wled00/FX_fcn.cpp`:
- Around line 1285-1287: The allocation result from allocate_buffer(requiredMem,
BFRALLOC_ENFORCE_PSRAM | BFRALLOC_NOBYTEACCESS | BFRALLOC_CLEAR) assigned to
_pixels must be checked for nullptr immediately (similar to
Segment::setGeometry()); if it returns nullptr, log an error including
requiredMem and a clear message via DEBUG_PRINTF_P or process logger, set a safe
fallback state (e.g., leave _pixels null and set a flag or return/error out from
the calling init function) to avoid dereferencing before show() is called, and
ensure any subsequent code that assumes a valid buffer respects that flag or
early-returns; update the allocation site where _pixels is assigned to perform
this check and follow the same error-handling pattern used in
Segment::setGeometry.
- Line 1281: Remove the dead commented declaration "//uint32_t currentMem = 0;"
from FX_fcn.cpp; delete the commented line referencing currentMem so the file no
longer contains unused commented-out code (no other changes needed).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4a92b04a-4f6a-4abc-b8f5-2dcb908101f7

📥 Commits

Reviewing files that changed from the base of the PR and between 30abc01 and b60cf6b.

📒 Files selected for processing (2)
  • wled00/FX.h
  • wled00/FX_fcn.cpp

Comment thread wled00/FX_fcn.cpp

@willmmiles willmmiles left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good catch! I don't love the number of code paths that lead up to this, but refactoring that is a problem for another cycle.

@softhack007

Copy link
Copy Markdown
Member

@coderabbitai please check if this bugfix is compatible with useGlobalLedBuffer = false (8266 default).

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find and display updatePixelBuffer implementation
echo "=== updatePixelBuffer implementation ==="
rg -n "updatePixelBuffer" --type cpp -C 5

echo ""
echo "=== useGlobalLedBuffer references ==="
rg -n "useGlobalLedBuffer" --type cpp -C 5

echo ""
echo "=== _pixels references ==="
rg -n "_pixels" --type cpp -C 3

echo ""
echo "=== getLengthTotal ==="
rg -n "getLengthTotal" --type cpp -C 3

Length of output: 47190


🏁 Script executed:

#!/bin/bash
# Get the PR diff
gh pr diff 5675 --repo wled/WLED

Length of output: 3223


🏁 Script executed:

#!/bin/bash
echo "=== allocate_buffer implementation ==="
rg -n "allocate_buffer" --type cpp -C 5

echo ""
echo "=== BFRALLOC flags definitions ==="
rg -n "BFRALLOC_" --type cpp -C 3
rg -n "BFRALLOC_" --type h -C 3

Length of output: 23834



@softhack007: The fix is fully compatible with useGlobalLedBuffer = false. Here's the breakdown:

_pixels and useGlobalLedBuffer are completely independent

  • useGlobalLedBuffer controls per-bus hardware double buffering (_ledBuffer in BusNeopixel, bus_manager.cpp line 1007). When false on ESP8266, that per-bus buffer is simply not allocated — saving memory.
  • _pixels is the WS2812FX segment-blending frame buffer — a separate structure always required for rendering, regardless of the double-buffer setting.

updatePixelBuffer() only touches _pixels; it has no interaction with useGlobalLedBuffer at all.

allocate_buffer on ESP8266 behaves correctly

The BFRALLOC_ENFORCE_PSRAM | BFRALLOC_NOBYTEACCESS flags used in updatePixelBuffer() are harmless on ESP8266:

util.cpp:
  `#ifdef` CONFIG_IDF_TARGET_ESP32
    if (type & BFRALLOC_NOBYTEACCESS) { ... }   // ← skipped on ESP8266
  `#endif`
  `#if` !defined(BOARD_HAS_PSRAM)
    buffer = d_malloc(size);                     // ← ESP8266 lands here (plain heap alloc)
  `#else`
    ... PSRAM logic ...                          // ← skipped on ESP8266
  `#endif`

On ESP8266 (no BOARD_HAS_PSRAM), allocate_buffer ignores all PSRAM/NOBYTEACCESS flags and simply calls d_malloc(). The BFRALLOC_ENFORCE_PSRAM definition itself also documents this: "use PSRAM if available, falls back to DRAM if PSRAM fails".

The bug fix itself is equally needed on ESP8266

The crash in #5669 is caused by _pixels retaining a stale (too-small) size after setUpMatrix() changes getLengthTotal() inside deserializeMap(). This out-of-bounds write is platform-agnostic — the fix correctly re-allocates _pixels to the new size on all platforms. The guard if (strip.getLengthTotal() != lengthTotalBefore) ensures the reallocation only triggers when an actual change occurred.

Summary: No issues found with useGlobalLedBuffer = false. The fix is safe for ESP8266.

@softhack007 softhack007 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

looks good to me - did a short test on esp32 (no real leds, just peek preview) and it seems to work.

@softhack007

Copy link
Copy Markdown
Member

I think this PR should go into the upcoming 16.0.1 bugfix release.

@DedeHai

DedeHai commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator Author

Its edge case, medium importance

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants