Skip to content

feat(m5stack-tab5): Update tab5 video and example to have larger display buffers and use the PPU for faster refresh and display rotation#675

Merged
finger563 merged 3 commits into
mainfrom
feat/tab5-fast-rotation
Jul 24, 2026
Merged

feat(m5stack-tab5): Update tab5 video and example to have larger display buffers and use the PPU for faster refresh and display rotation#675
finger563 merged 3 commits into
mainfrom
feat/tab5-fast-rotation

Conversation

@finger563

@finger563 finger563 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Description

Improves the M5Stack Tab5 display/video path in two related ways:

  • Full-frame LVGL draw buffers in PSRAM. The LVGL double draw buffers are
    allocated in PSRAM and the example sizes them to the full panel
    (1280×720) instead of a 10-line strip. A full-screen redraw (e.g. a tab
    switch or a rotation) now flushes in a single pass instead of ~72 ten-line
    strips, so the screen repaints at once rather than wiping progressively.
    Partial updates still only render/flush their dirty area, so the larger
    buffer costs nothing there (just PSRAM).
  • Hardware display rotation via the ESP32-P4 PPA (Pixel Processing
    Accelerator).
    flush() rotates the frame with the PPA into a
    cache-line-aligned PSRAM scratch buffer, replacing the CPU rotation
    (lv_draw_sw_rotate). A software-rotation fallback is retained for when the
    PPA client can't be registered or a PPA op fails.

Also folds in the PR review feedback: reuse/free the rotation scratch buffer
across initialize_display() calls (no leak), fail init if it can't be
allocated (rather than coming up with rotation silently broken), check the PPA
op's return and fall back to software rotation on failure, correct the
rotation-direction comment to match the hardware-verified mapping, and drop the
stale IRAM_ATTR/ISR annotation from flush() (it runs in the LVGL task
context, not an ISR — the DPI transfer-done ISR is handled separately).

Motivation and Context

Full-screen updates previously flushed as many small strips, producing a
visible progressive "wipe" on redraws. And CPU rotation of a full-frame PSRAM
buffer is slow and, because the transpose strides PSRAM, asymmetric between
90° and 270° (one direction noticeably slower than the other). Moving the draw
buffers to PSRAM + using the PPA makes full-screen refreshes repaint in one pass
and makes rotation fast and symmetric across all four orientations, offloading
the work from the CPU to dedicated hardware.

How has this been tested?

  • Built the m5stack-tab5 example with ESP-IDF for the ESP32-P4 — clean build,
    no warnings.
  • Flashed to M5Stack Tab5 hardware:
    • Full-screen redraws (tab switches, rotation changes) repaint in a single
      pass instead of wiping in strips.
    • Display rotation through all four orientations (0 / 90 / 180 / 270) is fast
      and correctly oriented; the previous CPU path was slow and asymmetric
      (90° fast, 270° slow).
    • Touch input remains correctly mapped in every rotation.
    • Verified the software-rotation fallback path still compiles and is used when
      the PPA client is unavailable.

Screenshots (if appropriate, e.g. schematic, board, console logs, lab pictures):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation Update
  • Hardware (schematic, board, system design) change
  • Software change

Checklist:

  • My change requires a change to the documentation.
  • I have added / updated the documentation related to this change via either README or WIKI

Software

  • I have added tests to cover my changes.
  • I have updated the .github/workflows/build.yml file to add my new test to the automated cloud build
  • All new and existing tests passed.
  • My code follows the code style of this project.

…lay buffers and use the PPU for faster refresh and display rotation
Copilot AI review requested due to automatic review settings July 23, 2026 19:42
@github-actions

Copy link
Copy Markdown

✅Static analysis result - no issues found! ✅

Copilot AI 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.

Pull request overview

This PR updates the M5Stack Tab5 display/video path to improve refresh performance and enable faster display rotation by moving LVGL draw buffers into PSRAM and using the ESP32-P4 PPA (Pixel Processing Accelerator) for hardware rotation.

Changes:

  • Allocate LVGL draw buffers in PSRAM and add a PSRAM-aligned scratch buffer for rotation output.
  • Add PPA-based hardware rotation in the LVGL flush path with a software-rotation fallback.
  • Update the Tab5 example to use a full-frame LVGL pixel buffer for single-pass full-screen flushes.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
components/m5stack-tab5/src/video.cpp Moves draw buffers to PSRAM, adds PPA client + aligned rotation scratch buffer, and performs rotation in flush() via PPA with SW fallback.
components/m5stack-tab5/example/main/m5stack_tab5_example.cpp Switches the example to a full-frame LVGL pixel buffer to avoid progressive “striped” redraws on full-screen updates.
components/m5stack-tab5/CMakeLists.txt Adds esp_driver_ppa dependency required for PPA rotation.

Comment thread components/m5stack-tab5/src/video.cpp
Comment thread components/m5stack-tab5/src/video.cpp Outdated
Comment thread components/m5stack-tab5/src/video.cpp Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 23, 2026 20:52

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

components/m5stack-tab5/src/video.cpp:449

  • The comment says the PPA rotation direction is counter-clockwise vs LVGL’s clockwise rotation (LVGL 90 -> PPA 270 and LVGL 270 -> PPA 90), but the current mapping uses the same angles (90->90, 270->270). This mismatch will either rotate the image the wrong way or leave misleading documentation; align the mapping with the documented behavior.
      // Hardware rotation via the PPA. NOTE: the PPA rotates COUNTER-clockwise
      // while LVGL's rotation is clockwise, so LVGL 90 -> PPA 270 and LVGL 270
      // -> PPA 90 (180 is the same). If the rotated image comes out turned the
      // wrong way, swap the 90/270 mapping here. For 90/270 the output picture
      // width/height are swapped.
      ppa_srm_rotation_angle_t angle = PPA_SRM_ROTATION_ANGLE_0;
      uint32_t out_w = ww, out_h = hh;
      if (rotation == LV_DISPLAY_ROTATION_90) {
        angle = PPA_SRM_ROTATION_ANGLE_90;
        out_w = hh;
        out_h = ww;
      } else if (rotation == LV_DISPLAY_ROTATION_180) {
        angle = PPA_SRM_ROTATION_ANGLE_180;
      } else if (rotation == LV_DISPLAY_ROTATION_270) {
        angle = PPA_SRM_ROTATION_ANGLE_270;

Comment thread components/m5stack-tab5/src/video.cpp
Comment thread components/m5stack-tab5/src/video.cpp Outdated
Review feedback on the PPA rotation in video.cpp:

- initialize_display() reused/overwrote the rotation scratch buffer on every
  call, leaking it on re-init. Reuse it when the size matches, otherwise free it
  before reallocating.
- If the scratch-buffer allocation failed, initialize_display() still returned
  true, leaving rotation silently broken (flush would skip rotation). The buffer
  is required for both the PPA and the software rotation, so fail init instead.
- The PPA-rotation comment claimed LVGL 90 -> PPA 270 / 270 -> PPA 90 while the
  code (verified on hardware) maps 90 -> 90 and 270 -> 270. Correct the comment.
- ppa_do_scale_rotate_mirror()'s return value was ignored, so a failed rotation
  would flush stale/partial scratch-buffer data to the panel. Check it and fall
  back to the software rotation (which fully overwrites the buffer) on failure.
- flush() was marked IRAM_ATTR and commented as possibly ISR-invoked, which
  conflicts with its blocking PPA op. It is actually LVGL's flush callback and
  runs in the LVGL task context (the DPI transfer-done ISR is the separate
  notify_lvgl_flush_ready), so drop IRAM_ATTR and correct the comment; blocking
  work is safe there.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 24, 2026 14:18
@finger563 finger563 self-assigned this Jul 24, 2026

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

components/m5stack-tab5/src/video.cpp:288

  • initialize_display() accepts pixel_buffer_size as a public API, but a value of 0 will currently produce required_bytes==0 and can lead to a 0-byte aligned allocation (implementation-defined) and unsafe downstream use in flush/rotation. Reject 0 early (or map it to a sensible default) before any allocations.
bool M5StackTab5::initialize_display(size_t pixel_buffer_size) {
  logger_.info("Initializing LVGL display with pixel buffer size: {} pixels", pixel_buffer_size);
  if (!display_) {

components/m5stack-tab5/src/video.cpp:315

  • initialize_display() can now fail (rotation scratch buffer allocation) after the LVGL Display has already been constructed and its large draw buffers allocated. This makes it hard/impossible to recover by retrying with a smaller pixel_buffer_size (since display_ is already initialized), and it also means a failing call has significant side effects. Consider allocating/validating the scratch buffer (and optionally registering the PPA client) before constructing display_, so the function is effectively transactional on failure.
  // Register a PPA client for hardware rotation, and allocate the rotation
  // scratch buffer that the PPA writes into. The PPA output buffer (external /
  // PSRAM memory) must be aligned to the data cache line size, and both the
  // pointer and the size must be a multiple of it.
  if (g_ppa_client == nullptr) {

@finger563
finger563 merged commit 4f65770 into main Jul 24, 2026
130 checks passed
@finger563
finger563 deleted the feat/tab5-fast-rotation branch July 24, 2026 14:28
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