fix(ppa): fix hang if blend operation on a YUV format input background image

Closes https://github.com/espressif/esp-idf/issues/18687
This commit is contained in:
Song Ruo Jing
2026-06-16 19:32:25 +08:00
parent 839352f836
commit 9a5fed0f14
4 changed files with 94 additions and 6 deletions
@@ -152,6 +152,8 @@ bool ppa_blend_transaction_on_picked(uint32_t num_chans, const dma2d_trans_chann
ppa_ll_blend_set_tx_yuv_range(platform->hal.dev, blend_trans_desc->out.yuv_range);
ppa_ll_blend_set_tx_rgb2yuv_std(platform->hal.dev, blend_trans_desc->out.yuv_std);
}
// For YUV420/YUV422 background input and output, blend_tx_size.blend_hb/vb must be set to make the Blending engine and 2D-DMA work properly
ppa_ll_blend_set_block_size(platform->hal.dev, blend_trans_desc->in_bg.block_w, blend_trans_desc->in_bg.block_h); // in_bg.block_w == in_fg.block_w && in_bg.block_h == in_fg.block_h
// Color keying
color_pixel_rgb888_data_t rgb888_min = {.b = 0x00, .g = 0x00, .r = 0x00};
+2 -1
View File
@@ -74,8 +74,9 @@ bool ppa_fill_transaction_on_picked(uint32_t num_chans, const dma2d_trans_channe
dma2d_start(dma2d_rx_chan);
// Configure PPA Blending engine
ppa_ll_blend_configure_filling_block(platform->hal.dev, fill_trans_desc->out.fill_cm, (void *)&fill_trans_desc->fill_color_val, fill_trans_desc->fill_block_w, fill_trans_desc->fill_block_h);
ppa_ll_blend_configure_filling_block_color(platform->hal.dev, fill_trans_desc->out.fill_cm, (void *)&fill_trans_desc->fill_color_val);
ppa_ll_blend_set_tx_color_mode(platform->hal.dev, fill_trans_desc->out.fill_cm);
ppa_ll_blend_set_block_size(platform->hal.dev, fill_trans_desc->fill_block_w, fill_trans_desc->fill_block_h);
ppa_ll_blend_start(platform->hal.dev, PPA_LL_BLEND_TRANS_MODE_FILL);
@@ -488,6 +488,82 @@ TEST_CASE("ppa_blend_basic_data_correctness_check", "[PPA]")
printf("\n");
TEST_ASSERT_EQUAL_UINT8_ARRAY((void *)out_buf_expected, (void *)out_buf, out_buf_len);
#if !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3)
// Test YUV422/YUV420 blend
// A mid-grey image is achromatic (U = V = 128), so a correct RGB<->YUV path must round-trip
// it back to grey with R == G == B. For each format we run RGB->YUV (exercises YUV as the
// blend output) then YUV->RGB (exercises YUV as the blend background input); both must
// complete without hanging and yield achromatic grey
// 2 x 2 is the smallest valid YUV block: even w/h satisfies YUV422 (2x1) & YUV420 (2x2)
const uint8_t grey = 0x80; // mid-grey: Y arbitrary, U = V = 128 -> R == G == B after decode
// RGB888 mid-grey background (12B) and ARGB8888 foreground (16B). The foreground alpha is
// inverted to 0, so it contributes nothing and the blend output equals the background color.
const uint8_t rgb_grey[12] = {[0 ... 11] = grey};
const uint8_t fg_buf[16] = {[0 ... 15] = 0xFF};
// DMA outputs require cache-line alignment; yuv_mid is also reused as input for the YUV->RGB pass.
uint8_t yuv_mid[64] __attribute__((aligned(64))) = {[0 ... 63] = 0};
uint8_t rgb_back[64] __attribute__((aligned(64))) = {[0 ... 63] = 0xCC};
const uint32_t yuv_buf_size = sizeof(yuv_mid);
const ppa_blend_color_mode_t yuv_cms[] = {
PPA_BLEND_COLOR_MODE_YUV422_UYVY,
PPA_BLEND_COLOR_MODE_YUV420,
};
for (int c = 0; c < 2; c++) {
const ppa_blend_color_mode_t yuv_cm = yuv_cms[c];
// Foreground contributes nothing: alpha 0xFF inverted to 0, so output == background.
ppa_blend_oper_config_t yuv_oper_config = {
.in_fg.buffer = fg_buf,
.in_fg.pic_w = w,
.in_fg.pic_h = h,
.in_fg.block_w = w,
.in_fg.block_h = h,
.in_fg.blend_cm = PPA_BLEND_COLOR_MODE_ARGB8888,
.bg_alpha_update_mode = PPA_ALPHA_NO_CHANGE,
.fg_alpha_update_mode = PPA_ALPHA_INVERT,
.mode = PPA_TRANS_MODE_BLOCKING,
};
// 1) RGB888 grey -> YUV (exercises YUV as blend output; must not hang)
yuv_oper_config.in_bg.buffer = rgb_grey;
yuv_oper_config.in_bg.pic_w = w;
yuv_oper_config.in_bg.pic_h = h;
yuv_oper_config.in_bg.block_w = w;
yuv_oper_config.in_bg.block_h = h;
yuv_oper_config.in_bg.blend_cm = PPA_BLEND_COLOR_MODE_RGB888;
yuv_oper_config.out.buffer = yuv_mid;
yuv_oper_config.out.buffer_size = yuv_buf_size;
yuv_oper_config.out.pic_w = w;
yuv_oper_config.out.pic_h = h;
yuv_oper_config.out.blend_cm = yuv_cm;
TEST_ESP_OK(ppa_do_blend(ppa_client_handle, &yuv_oper_config));
// 2) YUV grey -> RGB888 (exercises YUV as blend background input; must not hang)
yuv_oper_config.in_bg.buffer = yuv_mid;
yuv_oper_config.in_bg.blend_cm = yuv_cm;
yuv_oper_config.out.buffer = rgb_back;
yuv_oper_config.out.blend_cm = PPA_BLEND_COLOR_MODE_RGB888;
TEST_ESP_OK(ppa_do_blend(ppa_client_handle, &yuv_oper_config));
// Check result
// Mid-grey is achromatic: every RGB888 pixel must stay near-grey, i.e. R ~= G ~= B
// (chroma preserved) and the level must remain mid-grey (luma not driven to
// black/white). A small tolerance covers the +-1 LSB rounding of two YUV conversions.
for (int p = 0; p < w * h; p++) {
const int b = rgb_back[p * 3 + 0];
const int g = rgb_back[p * 3 + 1];
const int r = rgb_back[p * 3 + 2];
TEST_ASSERT_INT_WITHIN(2, b, g); // achromatic: channels stay together
TEST_ASSERT_INT_WITHIN(2, g, r);
TEST_ASSERT_INT_WITHIN(16, grey, b); // round-trips back to ~mid-grey
}
}
#endif // !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3)
TEST_ESP_OK(ppa_unregister_client(ppa_client_handle));
}
@@ -1083,7 +1083,7 @@ static inline void ppa_ll_blend_configure_rx_fg_alpha(ppa_dev_t *dev, ppa_alpha_
}
/**
* @brief Configure PPA blending pixel filling image block
* @brief Configure PPA blending pixel filling image block color
*
* The color to be filled is directly relying on the blend_tx_fix_pixel register field value.
* For fill operation, the data does not go through any color space conversion in the blending engine.
@@ -1091,12 +1091,9 @@ static inline void ppa_ll_blend_configure_rx_fg_alpha(ppa_dev_t *dev, ppa_alpha_
* @param dev Peripheral instance address
* @param color_mode One of the values in ppa_fill_color_mode_t
* @param data The point of the fix data to be filled to the image block pixels
* @param hb The horizontal width of image block that would be filled in fix pixel filling mode. The unit is pixel.
* @param vb The vertical height of image block that would be filled in fix pixel filling mode. The unit is pixel.
*/
static inline void ppa_ll_blend_configure_filling_block(ppa_dev_t *dev, ppa_fill_color_mode_t color_mode, void *data, uint32_t hb, uint32_t vb)
static inline void ppa_ll_blend_configure_filling_block_color(ppa_dev_t *dev, ppa_fill_color_mode_t color_mode, void *data)
{
HAL_ASSERT(hb <= PPA_BLEND_HB_V && vb <= PPA_BLEND_VB_V);
uint32_t fill_color_data = 0;
switch (color_mode) {
case PPA_FILL_COLOR_MODE_ARGB8888:
@@ -1123,6 +1120,18 @@ static inline void ppa_ll_blend_configure_filling_block(ppa_dev_t *dev, ppa_fill
abort();
}
dev->blend_fix_pixel.blend_tx_fix_pixel = fill_color_data;
}
/**
* @brief Set PPA blending block size
*
* @param dev Peripheral instance address
* @param hb The horizontal width of image block that would be filled in fix pixel filling mode or blend mode. The unit is pixel.
* @param vb The vertical height of image block that would be filled in fix pixel filling mode or blend mode. The unit is pixel.
*/
static inline void ppa_ll_blend_set_block_size(ppa_dev_t *dev, uint32_t hb, uint32_t vb)
{
HAL_ASSERT(hb <= PPA_BLEND_HB_V && vb <= PPA_BLEND_VB_V);
dev->blend_tx_size.blend_hb = hb;
dev->blend_tx_size.blend_vb = vb;
}