Skip to content

Release 0.9.4 dev.10

piggei edited this page Oct 3, 2026 · 1 revision

Release 0.9.4-dev.10

Release: 0.9.4
Build: 0.9.4-dev.10
Status: development / persistent GIF source-reuse qualification
Stable behavioral baseline: 0.9.3
Qualified optimization baseline: 0.9.4-dev.9

0.9.4-dev.10 is the second measured PSRAM optimization step for the Waveshare ESP32-S3-RGB-Matrix / WLED 17.0.0-devV5 target. It keeps the qualified dev.7-dev.9 direct AnimatedGIF path and adds bounded persistent reuse of whole-file GIF source images for durable Carousel slots.

Qualified baseline

Dev.8 completed the longer PSRAM staging/telemetry gate with 266/266 successful stages and zero fallback. Dev.9 then passed the refactor regression smoke with 22/22 successful source stages and zero fallback. Dev.10 builds on that code path without changing AnimatedGIF callbacks, frame timing, BLE protocol or Carousel state/dwell semantics.

Source-reuse policy

On the Waveshare profile:

  • normal source-stage maximum: 2 MiB per GIF;
  • pre-allocation PSRAM reserve: 4 MiB;
  • persistent resident-byte budget: 1 MiB;
  • maximum admitted resident source: 512 KiB;
  • metadata capacity: 12 entries, matching the Carousel slot count;
  • replacement policy: LRU.

The first play of an eligible stored Carousel GIF uses the existing whole-file PSRAM stage. If the source is within the persistent-admission policy, the same allocation becomes a resident source-cache entry without a second copy. Later plays can reuse that PSRAM image and avoid another whole-file LittleFS copy.

GIFs too large for persistent admission may still use the normal 2 MiB one-play stage. App-upload/transient /idot_play.gif is not retained across playback boundaries.

Correctness and invalidation

The filesystem remains authoritative.

  • Carousel reset/reconfiguration clears resident source entries.
  • A committed slot replacement invalidates that slot explicitly, including same-size replacements.
  • An invalidated entry still in use by AnimatedGIF is retired only after the decoder closes.
  • The cache is expendable under PSRAM pressure: LRU entries are retired before the normal source-stage path is allowed to fail its reserve/allocation guard.
  • If persistent admission is unavailable, playback continues with transient staging.
  • If staging itself cannot be used, the unchanged LittleFS callback fallback remains available.

Telemetry

Existing staging telemetry remains:

gifStage=state:<psram|fs> bytes:<current> peak:<peak> attempts:<n> ok:<n> fallback:<n> max:<bytes> reserve:<bytes>

In dev.10, a source-cache hit does not increment attempts or ok, because no whole-file staging copy occurs.

New telemetry:

gifSourceCache=state:<off|idle|active> entries:<n> bytes:<n> hits:<n> misses:<n> stores:<n> evict:<n> invalid:<n> maxEntries:<n> maxBytes:<bytes> entryMax:<bytes>

Expected repeated-Carousel behavior is: initial misses/stores and staging attempts, followed by increasing hits while staging attempts flatten for resident slots.

Qualification

After flashing dev.10:

  1. verify release=0.9.4 and build=0.9.4-dev.10;
  2. run at least two complete Carousel traversals containing GIF slots;
  3. verify first-pass misses/stores and later cache hits;
  4. verify entries <= 12, bytes <= 1048576, and no unexpected staging fallback;
  5. re-check minFree <= free, minLargest <= largest, and peakUsed = total - minFree;
  6. replace/reconfigure at least one Carousel GIF and verify invalidation plus fresh playback;
  7. confirm AudioReactive and Matrix Auto Rotation remain healthy.

Absolute psram=total depends on the complete linked WLED/Usermod firmware composition, so A/B qualification should keep that composition constant.

16x16 -> 64x64 and 32x32 -> 64x64 Waveshare scaling remain separate pending checks.

Clone this wiki locally