Skip to content

dotclock panel needs 2x32KB contiguous internal DMA RAM and fails opaquely without it; and backends should expose fill_rect where they can #28

Description

@bdbarnett

Not a 1.29 regression — corrected 2026-09-09

The original report claimed the ESP32-S3-Touch-LCD-4.3 panel was broken on MicroPython v1.29.0 + ESP-IDF 5.5.4 and that anyone upgrading would hit it. That was wrong. On a hard-booted board board_config imports cleanly, the panel comes up 800x480, and a 400x240 fill_rect runs in 12.3 ms through its native path. Verified on the bench, red and blue quadrants drawn and seen.

What was real is narrower and still worth knowing.

The panel needs ~64 KB of internal DMA RAM, in two contiguous 32 KB pieces

mod_dotclockframebuffer.c:236 requests bounce_buffer_size_px = 20 * h_res. On an 800-wide panel that is 16,000 px = 32,000 bytes per bounce buffer, and ESP-IDF allocates RGB_LCD_PANEL_BOUNCE_BUF_NUM = 2 of them (esp_lcd_panel_rgb.c:75, allocated at :188 via heap_caps_aligned_calloc from internal memory, returning ESP_ERR_NO_MEM at :190).

Measured on this board:

state internal free largest contiguous
fresh hard boot 187,879 110,592
after 92 mip installs and a long run of exec sessions 57,304 38,912

Two 32,000-byte allocations need 64,000. Fresh boot has ample room. The busy board does not — the first allocation fits in the 38,912 block and the second fails, which is exactly the ESP_ERR_NO_MEM seen. A soft reset does not recover it; a hard reset does.

So constructing the panel is sensitive to internal-RAM fragmentation from prior activity, and it fails with an ESP-IDF error code that says nothing about which allocation or why. PSRAM is a red herring throughout: it had 6.7 MB contiguous free the whole time, which is what made "plenty of memory" so misleading.

Worth considering (not decided): size the bounce buffer against what is actually free — try 20 rows, fall back to fewer, and disable bounce rather than failing construction, since with fb_in_psram the panel can scan out of PSRAM without it. And whatever the driver does, the error deserves a message naming the bounce buffer rather than a bare IDF code.


Backends should expose fill_rect where they have a fast path

Unchanged from the original report, and independent of the above.

FBDisplay.fill_rect tries, in order: bitmaptools + Bitmap (the CircuitPython Qualia path), a native fill_rect on the raw buffer, and — as of pydevices 3b38c0c — a native blit, before falling back to per-row memoryview assigns.

The two boards land differently, and the question is why the S3's backend has fill_rect while the P4's does not:

board raw buffer type fill_rect blit
ESP32-S3-Touch-LCD-4.3 DotClockFramebuffer (displayif) yes
ESP32-P4 Touch-LCD-4B Display (mipidsi) no yes

The P4's object exposes only blit, deinit, height, refresh, refresh_rect, row_stride, width. Without a native fill it was doing ~14 ms per row into SPIRAM: a 100x100 fill measured 1.387 s, and paint.py's palette visibly drew block by block. Routing through the native blit took the same fill to 2,352 us, and a full 720x720 screen to 20.8 ms. For comparison the S3, which has a native fill, does 400x240 in 12.3 ms.

The ask: every displayif backend that can fill in C should expose fill_rect. Worth auditing the set — mipidsi, dotclockframebuffer, i80bus, spibus, rgbmatrix, busdisplay — for which have a fast fill available and which are silently falling back.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions