Repository navigation
Speed up JPEG decoding on ESP32-P4 and ESP32-S31 - #11634
mikeysklar wants to merge 12 commits into
Conversation
tannewt
left a comment
There was a problem hiding this comment.
Do we want to be explicit about whether the user wants hardware acceleration? Or do you think it is ok to do through speed testing?
I had a couple comments about structure.
CIRCUITPY_JPEGIO_HW (default 0) means the port provides
jpegio_jpegdecoder_decode_hw(). decode() calls it first when open() was
given an in-memory buffer. If it returns false, TJpgDec decodes the image
as before. The espressif port turns it on for esp32p4 and esp32s31, the
targets with a JPEG decoder peripheral, and implements it with
esp_driver_jpeg. Other targets are unchanged.
CIRCUITPY_DISPLAYIO_BITMAP_DATA_ALIGNMENT (default 4) sets the alignment
of the pixel memory a Bitmap allocates. The two chips raise it to the
64 byte cache line, so a decode of the whole image at 0,0 into a Bitmap of
the same size goes straight into the Bitmap. The pixels stay in the GC
heap. Other decodes go through a temporary buffer that is freed before
decode() returns. Skip indexes, and input the DMA can't read, use TJpgDec.
The engine is created on the first decode and deleted on VM reset. The
color conversion is full range BT601, as JFIF specifies. This needs
yuv_range in jpeg_decode_cfg_t, added in adafruit/esp-idf.
1280x720, median of 10, hardware from bytes vs TJpgDec from io.BytesIO:
ESP32-P4 (Adafruit P4 GPIO) 4:2:0 9.2 ms (302.1), 4:2:2 12.2 (327.5),
4:4:4 18.2 (385.2)
ESP32-S31 (ESP32-S31-Korvo-1) 4:2:0 16.0 ms (374.8), 4:2:2 20.9 (409.4),
4:4:4 31.0 (477.4)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Points at adafruit/esp-idf#19, which also merges #18. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Use 64-byte GC blocks and align the PSRAM heap areas to them, as the camera change does, so Bitmap pixels start on a cache line without a Bitmap-specific alignment option. Revert the Bitmap allocation change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Each JpegDecoder now owns its JPEG engine and releases it when it is collected, instead of sharing one engine that is freed on VM reset. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
a730902 to
d69ef5f
Compare
|
Good point. Timing alone is not enough, since a decode can quietly fall back to software. Added a read-only Thanks! Changes:
I'd keep hardware decode on by default. It costs under 1% of flash and RAM on P4 and S31, and 720p decodes in 9 to 16 ms instead of 300 to 375 ms. Builds can opt out with Re-ran the hardware tests after these changes, rebased on main. All pass on P4 and S31. |
Reports whether the last decode() used the JPEG decoder peripheral, so scripts and tests can check the path without timing it. Ports without the hardware always report False. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Two merge notes:
|
tannewt
left a comment
There was a problem hiding this comment.
Don't wait to rebase on the video pipeline stuff because this is close. The allocation changes here are fine.
Match the Makefile style of one section per target. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Removed per review. A software_ok option will cover the case of expecting hardware decode and not getting it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This matches hw_deinit, which frees it from the finaliser. If the engine cannot be created, hw_decoder stays NULL and decode() uses TJpgDec. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
software_ok=True (the default) keeps falling back to TJpgDec when the JPEG decoder peripheral cannot decode an image. software_ok=False raises ValueError instead, including on chips without the peripheral, so code that expects hardware decode finds out when it does not get it. The error messages reuse existing translated strings. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
(me, not LLM):
Sorry I misread this the first time. As we settled on Discord:
The default stays Not waiting on #11614 anymore. Tested on three ESPs to verify changes worked:
Still waiting on adafruit/esp-idf #19 to point the submodule at it. |
Match the esp-idf pin in adafruit#11643 (29d36039) so the two PRs merge cleanly in either order. 29d36039 already contains the jpeg full-range change (adafruit/esp-idf#18) this PR needs, plus #19 and #21. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
What
jpegionow uses the chip's JPEG hardware on ESP32-P4 and ESP32-S31.Why
Software decode is too slow for video on these chips.
Needs adafruit/esp-idf#18 (adds
yuv_range) and #19 (S31 ROM fix).The esp-idf submodule points at #19, which includes both.
MICROPY_BYTES_PER_GC_BLOCKport_mallocchange as #11614.CIRCUITPY_JPEGIO_HWTJpgDec is timed from
io.BytesIO; the hardware frombytes.open()got bytes,scale=0, 16-bit Bitmap, baseline color JPEGdecode()returnsdecode()onlytests/circuitpython-manual/jpegio/hw_decode.pyscale=1fall back and match exactly; progressive raises as beforeAI assistance: written with Claude Code. I ran the tests above on hardware.