Files
esp-idf/components/esp_driver_ppa/test_apps/main/test_ppa.c
T
Song Ruo Jing 37f2d25897 feat(ppa): ESP32P4 ECO5 PPA related updates
PPA SRM engine added YUV422 and GRAY8 color mode support
PPA SRM engine macro block size increased to 32x32
PPA Blending engine added YUV420, YUV422 and GRAY8 color mode support
2025-10-22 20:05:59 +08:00

840 lines
31 KiB
C

/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "unity.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "driver/ppa.h"
#include "esp_heap_caps.h"
#include "esp_err.h"
#include "ccomp_timer.h"
#include "hal/color_hal.h"
#include "esp_cache.h"
#include "ppa_performance.h"
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
TEST_CASE("ppa_client_do_ppa_operation", "[PPA]")
{
const uint32_t w = 480;
const uint32_t h = 480;
const uint32_t buf_1_color_type_id = COLOR_TYPE_ID(COLOR_SPACE_ARGB, COLOR_PIXEL_ARGB8888);
const uint32_t buf_2_color_type_id = COLOR_TYPE_ID(COLOR_SPACE_ARGB, COLOR_PIXEL_ARGB8888);
color_space_pixel_format_t buf_1_cm = {
.color_type_id = buf_1_color_type_id,
};
color_space_pixel_format_t buf_2_cm = {
.color_type_id = buf_2_color_type_id,
};
uint32_t buf_1_size = ALIGN_UP(w * h * color_hal_pixel_format_get_bit_depth(buf_1_cm) / 8, 64);
uint32_t buf_2_size = ALIGN_UP(w * h * color_hal_pixel_format_get_bit_depth(buf_2_cm) / 8, 64);
uint8_t *buf_1 = heap_caps_aligned_calloc(4, buf_1_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA); // cache alignment is implicited by MALLOC_CAP_DMA
TEST_ASSERT_NOT_NULL(buf_1);
uint8_t *buf_2 = heap_caps_aligned_calloc(4, buf_2_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(buf_2);
// Register different types of PPA clients
ppa_client_handle_t ppa_client_srm_handle;
ppa_client_handle_t ppa_client_blend_handle;
ppa_client_handle_t ppa_client_fill_handle_a;
ppa_client_handle_t ppa_client_fill_handle_b;
ppa_client_config_t ppa_client_config = {
.oper_type = PPA_OPERATION_SRM,
};
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_srm_handle));
ppa_client_config.oper_type = PPA_OPERATION_BLEND;
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_blend_handle));
ppa_client_config.oper_type = PPA_OPERATION_FILL;
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_fill_handle_a));
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_fill_handle_b));
ppa_srm_oper_config_t srm_oper_config = {
.in.buffer = buf_1,
.in.pic_w = w,
.in.pic_h = h,
.in.block_w = w,
.in.block_h = h,
.in.block_offset_x = 0,
.in.block_offset_y = 0,
.in.srm_cm = buf_1_color_type_id,
.out.buffer = buf_2,
.out.buffer_size = buf_2_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = 0,
.out.block_offset_y = 0,
.out.srm_cm = buf_2_color_type_id,
.rotation_angle = PPA_SRM_ROTATION_ANGLE_0,
.scale_x = 1.0,
.scale_y = 1.0,
.mode = PPA_TRANS_MODE_BLOCKING,
};
// A SRM client can request to do a SRM operation
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_srm_handle, &srm_oper_config));
// A non-SRM client can not request to do a SRM operation
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, ppa_do_scale_rotate_mirror(ppa_client_blend_handle, &srm_oper_config));
ppa_blend_oper_config_t blend_oper_config = {
.in_bg.buffer = buf_1,
.in_bg.pic_w = w,
.in_bg.pic_h = h,
.in_bg.block_w = w,
.in_bg.block_h = h,
.in_bg.block_offset_x = 0,
.in_bg.block_offset_y = 0,
.in_bg.blend_cm = buf_1_color_type_id,
.in_fg.buffer = buf_2,
.in_fg.pic_w = w,
.in_fg.pic_h = h,
.in_fg.block_w = w,
.in_fg.block_h = h,
.in_fg.block_offset_x = 0,
.in_fg.block_offset_y = 0,
.in_fg.blend_cm = buf_2_color_type_id,
.out.buffer = buf_1,
.out.buffer_size = buf_1_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = 0,
.out.block_offset_y = 0,
.out.blend_cm = buf_1_color_type_id,
.mode = PPA_TRANS_MODE_BLOCKING,
};
// A blend client can request to do a blend operation
TEST_ESP_OK(ppa_do_blend(ppa_client_blend_handle, &blend_oper_config));
// A non-blend client can not request to do a blend operation
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, ppa_do_blend(ppa_client_fill_handle_b, &blend_oper_config));
ppa_fill_oper_config_t fill_oper_config = {
.out.buffer = buf_1,
.out.buffer_size = buf_1_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = 0,
.out.block_offset_y = 0,
.out.fill_cm = buf_1_color_type_id,
.fill_block_w = w,
.fill_block_h = h,
.fill_argb_color = {
.val = 0xFF00FF00,
},
.mode = PPA_TRANS_MODE_NON_BLOCKING,
};
// A fill client can request to do a fill operation
TEST_ESP_OK(ppa_do_fill(ppa_client_fill_handle_a, &fill_oper_config));
// Another fill client can also request another fill operation at the same time
TEST_ESP_OK(ppa_do_fill(ppa_client_fill_handle_b, &fill_oper_config));
vTaskDelay(pdMS_TO_TICKS(500));
// Unregister all PPA clients
TEST_ESP_OK(ppa_unregister_client(ppa_client_srm_handle));
TEST_ESP_OK(ppa_unregister_client(ppa_client_blend_handle));
TEST_ESP_OK(ppa_unregister_client(ppa_client_fill_handle_a));
TEST_ESP_OK(ppa_unregister_client(ppa_client_fill_handle_b));
free(buf_1);
free(buf_2);
}
static bool ppa_trans_done_cb(ppa_client_handle_t ppa_client, ppa_event_data_t *event_data, void *user_data)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
SemaphoreHandle_t sem = (SemaphoreHandle_t)user_data;
xSemaphoreGiveFromISR(sem, &xHigherPriorityTaskWoken);
return (xHigherPriorityTaskWoken == pdTRUE);
}
TEST_CASE("ppa_pending_transactions_in_queue", "[PPA]")
{
// A big picture block takes longer time to process, desired for this test case
const uint32_t w = 1920;
const uint32_t h = 1080;
const uint32_t buf_1_color_type_id = COLOR_TYPE_ID(COLOR_SPACE_ARGB, COLOR_PIXEL_ARGB8888);
const uint32_t buf_2_color_type_id = COLOR_TYPE_ID(COLOR_SPACE_YUV, COLOR_PIXEL_YUV420);
color_space_pixel_format_t buf_1_cm = {
.color_type_id = buf_1_color_type_id,
};
color_space_pixel_format_t buf_2_cm = {
.color_type_id = buf_2_color_type_id,
};
uint32_t buf_1_size = w * h * color_hal_pixel_format_get_bit_depth(buf_1_cm) / 8;
uint32_t buf_2_size = ALIGN_UP(w * h * color_hal_pixel_format_get_bit_depth(buf_2_cm) / 8, 64);
uint8_t *buf_1 = heap_caps_aligned_calloc(4, buf_1_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(buf_1);
uint8_t *buf_2 = heap_caps_aligned_calloc(4, buf_2_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(buf_2);
// Register two PPA SRM clients with different max_pending_trans_num
ppa_client_handle_t ppa_client_a_handle;
ppa_client_handle_t ppa_client_b_handle;
ppa_client_config_t ppa_client_config = {
.oper_type = PPA_OPERATION_SRM,
};
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_a_handle));
ppa_client_config.max_pending_trans_num = 3;
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_b_handle));
ppa_event_callbacks_t cbs = {
.on_trans_done = ppa_trans_done_cb,
};
ppa_client_register_event_callbacks(ppa_client_a_handle, &cbs);
SemaphoreHandle_t sem = xSemaphoreCreateBinary();
ppa_srm_oper_config_t oper_config = {
.in.buffer = buf_1,
.in.pic_w = w,
.in.pic_h = h,
.in.block_w = w,
.in.block_h = h,
.in.block_offset_x = 0,
.in.block_offset_y = 0,
.in.srm_cm = buf_1_color_type_id,
.out.buffer = buf_2,
.out.buffer_size = buf_2_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = 0,
.out.block_offset_y = 0,
.out.srm_cm = buf_2_color_type_id,
.rotation_angle = PPA_SRM_ROTATION_ANGLE_0,
.scale_x = 1.0,
.scale_y = 1.0,
.user_data = (void *)sem,
.mode = PPA_TRANS_MODE_NON_BLOCKING,
};
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_a_handle, &oper_config));
// Another transaction cannot be accept since client_a can only hold one transaction
TEST_ESP_ERR(ESP_FAIL, ppa_do_scale_rotate_mirror(ppa_client_a_handle, &oper_config));
// Wait for the last transaction finishes
xSemaphoreTake(sem, portMAX_DELAY);
// Then a new transaction can be accepted again
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_a_handle, &oper_config));
// Client can not be unregistered when there are unfinished transactions
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, ppa_unregister_client(ppa_client_a_handle));
oper_config.mode = PPA_TRANS_MODE_BLOCKING;
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_b_handle, &oper_config));
// Every PPA engine can only process one operation at a time
// Transactions are being processed with First-In-First-Out
// So, at the moment, the new transaction requested by client_b has finished, the last transaction requested by client_a for sure has finished
TEST_ASSERT(xSemaphoreTake(sem, 0) == pdTRUE);
// client_b can accept more than one transactions
oper_config.mode = PPA_TRANS_MODE_NON_BLOCKING;
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_b_handle, &oper_config));
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_b_handle, &oper_config));
oper_config.mode = PPA_TRANS_MODE_BLOCKING;
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_b_handle, &oper_config));
// The last transaction requested is with BLOCKING mode, so the last call to ppa_do_scale_rotate_mirror returned means all transactions finished
// Unregister all PPA clients
TEST_ESP_OK(ppa_unregister_client(ppa_client_a_handle));
TEST_ESP_OK(ppa_unregister_client(ppa_client_b_handle));
vSemaphoreDelete(sem);
free(buf_1);
free(buf_2);
}
TEST_CASE("ppa_srm_basic_data_correctness_check", "[PPA]")
{
const uint32_t w = 4;
const uint32_t h = 4;
const uint32_t block_w = 3;
const uint32_t block_h = 3;
const uint32_t in_block_offset_x = 1;
const uint32_t in_block_offset_y = 1;
const uint32_t out_block_offset_x = 1;
const uint32_t out_block_offset_y = 0;
const ppa_srm_color_mode_t cm = PPA_SRM_COLOR_MODE_RGB565;
const ppa_srm_rotation_angle_t rotation = PPA_SRM_ROTATION_ANGLE_90; // CCW
const float scale_x = 1.0;
const float scale_y = 1.0;
color_space_pixel_format_t buf_cm = {
.color_type_id = cm,
};
const uint32_t buf_len = w * h * color_hal_pixel_format_get_bit_depth(buf_cm) / 8; // 32
uint32_t out_buf_size = ALIGN_UP(buf_len, 64);
uint8_t *out_buf = heap_caps_aligned_calloc(4, out_buf_size, sizeof(uint8_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(out_buf);
esp_cache_msync((void *)out_buf, out_buf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
const uint16_t in_buf[16] = {
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
// /*******************************/
0xFFFF, /**/ 0x8080, 0x8080, 0x8080, /**/
0xFFFF, /**/ 0x8F80, 0x8F80, 0x8F80, /**/
0xFFFF, /**/ 0xFF80, 0xFF80, 0xFF80, /**/
// /*******************************/
};
// Expected SRM output
const uint16_t out_buf_expected[16] = {
// /*******************************/
0x0000, /**/ 0x8080, 0x8F80, 0xFF80, /**/
0x0000, /**/ 0x8080, 0x8F80, 0xFF80, /**/
0x0000, /**/ 0x8080, 0x8F80, 0xFF80, /**/
// /*******************************/
0x0000, 0x0000, 0x0000, 0x0000
};
ppa_client_handle_t ppa_client_handle;
ppa_client_config_t ppa_client_config = {
.oper_type = PPA_OPERATION_SRM,
.max_pending_trans_num = 1,
};
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle));
ppa_srm_oper_config_t oper_config = {
.in.buffer = in_buf,
.in.pic_w = w,
.in.pic_h = h,
.in.block_w = block_w,
.in.block_h = block_h,
.in.block_offset_x = in_block_offset_x,
.in.block_offset_y = in_block_offset_y,
.in.srm_cm = cm,
.out.buffer = out_buf,
.out.buffer_size = out_buf_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = out_block_offset_x,
.out.block_offset_y = out_block_offset_y,
.out.srm_cm = cm,
.rotation_angle = rotation,
.scale_x = scale_x,
.scale_y = scale_y,
.rgb_swap = 0,
.byte_swap = 0,
.mode = PPA_TRANS_MODE_BLOCKING,
};
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_handle, &oper_config));
// Check result
for (int i = 0; i < buf_len; i++) {
if (i % 8 == 0) {
printf("\n");
}
printf("0x%02X ", out_buf[i]);
}
printf("\n");
TEST_ASSERT_EQUAL_UINT8_ARRAY((void *)out_buf_expected, (void *)out_buf, buf_len);
#if !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3)
// Test a rgb2gray color conversion
memset(out_buf, 0, out_buf_size);
esp_cache_msync((void *)out_buf, out_buf_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
const uint8_t r_weight = 100;
const uint8_t g_weight = 56;
const uint8_t b_weight = 100;
TEST_ESP_OK(ppa_set_rgb2gray_formula(r_weight, g_weight, b_weight));
oper_config.out.srm_cm = PPA_SRM_COLOR_MODE_GRAY8;
oper_config.rotation_angle = PPA_SRM_ROTATION_ANGLE_0;
uint8_t out_buf_expected_gray[16] = {};
for (int i = 0; i < block_w * block_h; i++) {
const uint16_t pix = in_buf[(i / block_w + in_block_offset_y) * w + (i % block_w + in_block_offset_x)];
uint8_t _r = ((pix >> 8) & 0xF8);
uint8_t _g = ((pix >> 3) & 0xFC);
uint8_t _b = ((pix << 3) & 0xF8);
out_buf_expected_gray[(i / block_w + out_block_offset_y) * w + (i % block_w + out_block_offset_x)] = (_r * r_weight + _g * g_weight + _b * b_weight) >> 8;
}
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_handle, &oper_config));
// Check result
for (int i = 0; i < w * h; i++) {
if (i % 4 == 0) {
printf("\n");
}
printf("0x%02X ", out_buf[i]);
}
printf("\n");
TEST_ASSERT_EQUAL_UINT8_ARRAY((void *)out_buf_expected_gray, (void *)out_buf, w * h);
#endif // !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3)
TEST_ESP_OK(ppa_unregister_client(ppa_client_handle));
free(out_buf);
}
TEST_CASE("ppa_blend_basic_data_correctness_check", "[PPA]")
{
const uint32_t w = 2;
const uint32_t h = 2;
const uint32_t block_w = 1;
const uint32_t block_h = 1;
const uint32_t block_offset_x = 1;
const uint32_t block_offset_y = 1;
const ppa_blend_color_mode_t in_bg_cm = PPA_BLEND_COLOR_MODE_RGB888;
const ppa_blend_color_mode_t in_fg_cm = PPA_BLEND_COLOR_MODE_ARGB8888;
const ppa_blend_color_mode_t out_cm = PPA_BLEND_COLOR_MODE_ARGB8888;
color_space_pixel_format_t bg_buf_cm = {
.color_type_id = in_bg_cm,
};
const uint32_t bg_buf_len __attribute__((unused)) = w * h * color_hal_pixel_format_get_bit_depth(bg_buf_cm) / 8; // 12
color_space_pixel_format_t fg_buf_cm = {
.color_type_id = in_fg_cm,
};
const uint32_t fg_buf_len __attribute__((unused)) = w * h * color_hal_pixel_format_get_bit_depth(fg_buf_cm) / 8; // 16
const uint32_t out_buf_len = fg_buf_len; // 16
// We will make the output write to the in_fg_buffer, therefore, it has cache line alignment requirement
const uint32_t out_buf_size = 64;
// Alpha of BG will be scaled by 0.8
// Alpha of FG will be inverted
const float bg_alpha_scale_ratio = 0.8;
const uint8_t in_bg_buf[12] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// /*************************/
0xFF, 0xFF, 0xFF, /**/ 0x80, 0x40, 0xA0, /**/
// /*************************/
};
uint8_t in_fg_buf[64] __attribute__((aligned(64))) = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// /*******************************/
0xFF, 0xFF, 0xFF, 0xFF, /**/ 0x00, 0x80, 0x80, 0xC0, /**/
// /* (B) (G) (R) (A) */
// /*******************************/
[16 ... 63] = 0,
};
uint8_t *out_buf = in_fg_buf;
// Expected blend output
// Alpha Blending calculation:
// A_bg' = (255 * 0.8) / 255 = 0.8, A_fg' = (255 - 0xC0) / 255 = 0.247
// A_out = 0.8 + 0.247 - 0.8 * 0.247 = 0.849 (216 -> 0xD8)
// C_out_b = (0x80 * 0.8 * (1 - 0.247) + 0x00 * 0.247) / 0.849 = 91 -> 0x5B
// C_out_g = (0x40 * 0.8 * (1 - 0.247) + 0x80 * 0.247) / 0.849 = 83 -> 0x53
// C_out_g = (0xA0 * 0.8 * (1 - 0.247) + 0x80 * 0.247) / 0.849 = 150 -> 0x96
const uint8_t out_buf_expected[16] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// /*******************************/
0xFF, 0xFF, 0xFF, 0xFF, /**/ 0x5B, 0x53, 0x96, 0xD8, /**/
// /*******************************/
};
ppa_client_handle_t ppa_client_handle;
ppa_client_config_t ppa_client_config = {
.oper_type = PPA_OPERATION_BLEND,
.max_pending_trans_num = 1,
};
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle));
ppa_blend_oper_config_t oper_config = {
.in_bg.buffer = in_bg_buf,
.in_bg.pic_w = w,
.in_bg.pic_h = h,
.in_bg.block_w = block_w,
.in_bg.block_h = block_h,
.in_bg.block_offset_x = block_offset_x,
.in_bg.block_offset_y = block_offset_y,
.in_bg.blend_cm = in_bg_cm,
.in_fg.buffer = in_fg_buf,
.in_fg.pic_w = w,
.in_fg.pic_h = h,
.in_fg.block_w = block_w,
.in_fg.block_h = block_h,
.in_fg.block_offset_x = block_offset_x,
.in_fg.block_offset_y = block_offset_y,
.in_fg.blend_cm = in_fg_cm,
.out.buffer = out_buf,
.out.buffer_size = out_buf_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = block_offset_x,
.out.block_offset_y = block_offset_y,
.out.blend_cm = out_cm,
.bg_alpha_update_mode = PPA_ALPHA_SCALE,
.bg_alpha_scale_ratio = bg_alpha_scale_ratio,
.fg_alpha_update_mode = PPA_ALPHA_INVERT,
.bg_ck_en = false,
.fg_ck_en = false,
.mode = PPA_TRANS_MODE_BLOCKING,
};
TEST_ESP_OK(ppa_do_blend(ppa_client_handle, &oper_config));
// Check result
for (int i = 0; i < out_buf_len; i++) {
if (i % 8 == 0) {
printf("\n");
}
printf("0x%02X ", out_buf[i]);
}
printf("\n");
TEST_ASSERT_EQUAL_UINT8_ARRAY((void *)out_buf_expected, (void *)out_buf, out_buf_len);
TEST_ESP_OK(ppa_unregister_client(ppa_client_handle));
}
TEST_CASE("ppa_fill_basic_data_correctness_check", "[PPA]")
{
const uint32_t w = 80;
const uint32_t h = 120;
const uint32_t block_w = 80;
const uint32_t block_h = 80;
const uint32_t block_offset_x = 0;
const uint32_t block_offset_y = 40;
const ppa_fill_color_mode_t out_cm = PPA_FILL_COLOR_MODE_RGB565;
const color_pixel_argb8888_data_t fill_color = {.a = 0x80, .r = 0xFF, .g = 0x55, .b = 0xAA};
color_space_pixel_format_t out_pixel_format = {
.color_type_id = out_cm,
};
uint32_t out_pixel_depth = color_hal_pixel_format_get_bit_depth(out_pixel_format); // bits
uint32_t out_buf_len = w * h * out_pixel_depth / 8;
uint32_t out_buf_size = ALIGN_UP(out_buf_len, 64);
uint8_t *out_buf = heap_caps_aligned_calloc(4, out_buf_size, sizeof(uint8_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(out_buf);
memset(out_buf, 0xFF, out_buf_len);
ppa_client_handle_t ppa_client_handle;
ppa_client_config_t ppa_client_config = {
.oper_type = PPA_OPERATION_FILL,
.max_pending_trans_num = 1,
};
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle));
ppa_fill_oper_config_t oper_config = {
.out.buffer = out_buf,
.out.buffer_size = out_buf_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = block_offset_x,
.out.block_offset_y = block_offset_y,
.out.fill_cm = out_cm,
.fill_block_w = block_w,
.fill_block_h = block_h,
.fill_argb_color = fill_color,
.mode = PPA_TRANS_MODE_BLOCKING,
};
TEST_ESP_OK(ppa_do_fill(ppa_client_handle, &oper_config));
// Check result
const color_pixel_rgb565_data_t fill_pixel_expected = {.r = fill_color.r >> 3,
.g = fill_color.g >> 2,
.b = fill_color.b >> 3,
};
TEST_ASSERT_EACH_EQUAL_UINT16(fill_pixel_expected.val, (void *)((uint32_t)out_buf + w * block_offset_y * out_pixel_depth / 8), block_w * block_h);
#if !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3)
// Test a yuv color fill
oper_config.out.fill_cm = PPA_FILL_COLOR_MODE_YUV422; // output YUV422 is with YVYU packed order
const color_macroblock_yuv_data_t fill_yuv_color = {.y = 0xFF, .u = 0x55, .v = 0xAA};
oper_config.fill_yuv_color = fill_yuv_color;
out_pixel_format.color_type_id = PPA_FILL_COLOR_MODE_YUV422;
out_pixel_depth = color_hal_pixel_format_get_bit_depth(out_pixel_format); // bits
TEST_ESP_OK(ppa_do_fill(ppa_client_handle, &oper_config));
// Check result (2 pixels per macro pixel)
const uint32_t fill_pixel_expected_yuv422 = ((fill_yuv_color.y << 24) | (fill_yuv_color.v << 16) | (fill_yuv_color.y << 8) | (fill_yuv_color.u));
TEST_ASSERT_EACH_EQUAL_UINT32(fill_pixel_expected_yuv422, (void *)((uint32_t)out_buf + w * block_offset_y * out_pixel_depth / 8), block_w * block_h / 2);
#endif // !(CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_SELECTS_REV_LESS_V3)
TEST_ESP_OK(ppa_unregister_client(ppa_client_handle));
free(out_buf);
}
/* All performance tests are tested under the following situations:
* - Testing PPA speed where in_buffer(s) and out_buffer all located in PSRAM
* - Only 2D-DMA is using PSRAM
* - 2D-DMA burst length is at maximum 128B
* - Input and output color mode is ARGB8888 (maximizing 2D-DMA data transafer amount)
*
* The time spend (T) to complete a PPA transaction is proportional to the amount of pixels (x) need to be processed.
* T = k * x + b
* k = (T - b) / x
*/
TEST_CASE("ppa_srm_performance", "[PPA]")
{
// Configurable parameters
const uint32_t w = 1920; // 1920 / 1280 / 800 / 640
const uint32_t h = 1080; // 1080 / 720 / 480
const uint32_t block_w = w;
const uint32_t block_h = h;
const ppa_srm_color_mode_t in_cm = PPA_SRM_COLOR_MODE_ARGB8888;
const ppa_srm_color_mode_t out_cm = PPA_SRM_COLOR_MODE_ARGB8888;
const ppa_srm_rotation_angle_t rotation = PPA_SRM_ROTATION_ANGLE_0;
const float scale_x = 1.0;
const float scale_y = 1.0;
color_space_pixel_format_t in_pixel_format = {
.color_type_id = in_cm,
};
color_space_pixel_format_t out_pixel_format = {
.color_type_id = out_cm,
};
uint32_t in_buf_size = w * h * color_hal_pixel_format_get_bit_depth(in_pixel_format) / 8;
uint32_t out_buf_size = ALIGN_UP(w * h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8, 64);
uint8_t *out_buf = heap_caps_aligned_calloc(4, out_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(out_buf);
uint8_t *in_buf = heap_caps_aligned_calloc(4, in_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(in_buf);
uint8_t *ptr = in_buf;
for (int x = 0; x < in_buf_size; x++) {
ptr[x] = x;
}
ppa_client_handle_t ppa_client_handle;
ppa_client_config_t ppa_client_config = {
.oper_type = PPA_OPERATION_SRM,
.max_pending_trans_num = 1,
};
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle));
uint32_t out_pic_w = (rotation == PPA_SRM_ROTATION_ANGLE_0 || rotation == PPA_SRM_ROTATION_ANGLE_180) ? w : h;
uint32_t out_pic_h = (rotation == PPA_SRM_ROTATION_ANGLE_0 || rotation == PPA_SRM_ROTATION_ANGLE_180) ? h : w;
ppa_srm_oper_config_t oper_config = {
.in.buffer = in_buf,
.in.pic_w = w,
.in.pic_h = h,
.in.block_w = block_w,
.in.block_h = block_h,
.in.block_offset_x = 0,
.in.block_offset_y = 0,
.in.srm_cm = in_cm,
.out.buffer = out_buf,
.out.buffer_size = out_buf_size,
.out.pic_w = out_pic_w,
.out.pic_h = out_pic_h,
.out.block_offset_x = 0,
.out.block_offset_y = 0,
.out.srm_cm = out_cm,
.rotation_angle = rotation,
.scale_x = scale_x,
.scale_y = scale_y,
.rgb_swap = 0,
.byte_swap = 0,
.mode = PPA_TRANS_MODE_BLOCKING,
};
ccomp_timer_start();
TEST_ESP_OK(ppa_do_scale_rotate_mirror(ppa_client_handle, &oper_config));
int64_t oper_time = ccomp_timer_stop(); // us
printf("PPA SRM - Process Time: %lld us\n", oper_time);
// Check performance
uint64_t num_pixels_processed = block_w * block_h;
uint64_t px_per_second = (num_pixels_processed - PPA_SRM_TIME_OFFSET) * 1000 * 1000 / oper_time;
printf("PPA SRM performance = %lld pixels/sec\n", px_per_second);
TEST_ASSERT_GREATER_THAN(PPA_SRM_MIN_PERFORMANCE_PX_PER_SEC, px_per_second);
TEST_ESP_OK(ppa_unregister_client(ppa_client_handle));
free(in_buf);
free(out_buf);
}
TEST_CASE("ppa_blend_performance", "[PPA]")
{
// Configurable parameters
const uint32_t w = 1280;
const uint32_t h = 720;
const uint32_t block_w = w;
const uint32_t block_h = h;
const ppa_blend_color_mode_t in_bg_cm = PPA_BLEND_COLOR_MODE_ARGB8888;
const ppa_blend_color_mode_t in_fg_cm = PPA_BLEND_COLOR_MODE_ARGB8888;
const ppa_blend_color_mode_t out_cm = PPA_BLEND_COLOR_MODE_ARGB8888;
color_space_pixel_format_t in_bg_pixel_format = {
.color_type_id = in_bg_cm,
};
color_space_pixel_format_t in_fg_pixel_format = {
.color_type_id = in_fg_cm,
};
color_space_pixel_format_t out_pixel_format = {
.color_type_id = out_cm,
};
uint32_t in_bg_buf_size = w * h * color_hal_pixel_format_get_bit_depth(in_bg_pixel_format) / 8;
uint32_t in_fg_buf_size = w * h * color_hal_pixel_format_get_bit_depth(in_fg_pixel_format) / 8;
uint32_t out_buf_size = ALIGN_UP(w * h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8, 64);
uint8_t *out_buf = heap_caps_aligned_calloc(4, out_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(out_buf);
uint8_t *in_bg_buf = heap_caps_aligned_calloc(4, in_bg_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(in_bg_buf);
uint8_t *in_fg_buf = heap_caps_aligned_calloc(4, in_fg_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(in_fg_buf);
uint8_t *ptr = in_bg_buf;
for (int x = 0; x < in_bg_buf_size; x++) {
ptr[x] = x & 0x55;
}
ptr = in_fg_buf;
for (int x = 0; x < in_fg_buf_size; x++) {
ptr[x] = x & 0xAA;
}
ppa_client_handle_t ppa_client_handle;
ppa_client_config_t ppa_client_config = {
.oper_type = PPA_OPERATION_BLEND,
.max_pending_trans_num = 1,
};
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle));
ppa_blend_oper_config_t oper_config = {
.in_bg.buffer = in_bg_buf,
.in_bg.pic_w = w,
.in_bg.pic_h = h,
.in_bg.block_w = block_w,
.in_bg.block_h = block_h,
.in_bg.block_offset_x = 0,
.in_bg.block_offset_y = 0,
.in_bg.blend_cm = in_bg_cm,
.in_fg.buffer = in_fg_buf,
.in_fg.pic_w = w,
.in_fg.pic_h = h,
.in_fg.block_w = block_w,
.in_fg.block_h = block_h,
.in_fg.block_offset_x = 0,
.in_fg.block_offset_y = 0,
.in_fg.blend_cm = in_fg_cm,
.out.buffer = out_buf,
.out.buffer_size = out_buf_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = 0,
.out.block_offset_y = 0,
.out.blend_cm = out_cm,
.bg_ck_en = false,
.fg_ck_en = false,
.mode = PPA_TRANS_MODE_BLOCKING,
};
ccomp_timer_start();
TEST_ESP_OK(ppa_do_blend(ppa_client_handle, &oper_config));
int64_t oper_time = ccomp_timer_stop();
printf("PPA Blend - Process Time: %lld us\n", oper_time);
// Check performance
uint64_t num_pixels_processed = block_w * block_h;
uint64_t px_per_second = (num_pixels_processed - PPA_BLEND_TIME_OFFSET) * 1000 * 1000 / oper_time;
printf("PPA Blend performance = %lld pixels/sec\n", px_per_second);
TEST_ASSERT_GREATER_THAN(PPA_BLEND_MIN_PERFORMANCE_PX_PER_SEC, px_per_second);
TEST_ESP_OK(ppa_unregister_client(ppa_client_handle));
free(in_bg_buf);
free(in_fg_buf);
free(out_buf);
}
TEST_CASE("ppa_fill_performance", "[PPA]")
{
// Configurable parameters
const uint32_t w = 1280;
const uint32_t h = 720;
const uint32_t block_w = 800;
const uint32_t block_h = 480;
const ppa_fill_color_mode_t out_cm = PPA_FILL_COLOR_MODE_RGB565;
color_space_pixel_format_t out_pixel_format = {
.color_type_id = out_cm,
};
uint32_t out_buf_size = ALIGN_UP(w * h * color_hal_pixel_format_get_bit_depth(out_pixel_format) / 8, 64);
uint8_t *out_buf = heap_caps_aligned_calloc(4, out_buf_size, sizeof(uint8_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(out_buf);
ppa_client_handle_t ppa_client_handle;
ppa_client_config_t ppa_client_config = {
.oper_type = PPA_OPERATION_FILL,
.max_pending_trans_num = 1,
};
TEST_ESP_OK(ppa_register_client(&ppa_client_config, &ppa_client_handle));
ppa_fill_oper_config_t oper_config = {
.out.buffer = out_buf,
.out.buffer_size = out_buf_size,
.out.pic_w = w,
.out.pic_h = h,
.out.block_offset_x = 0,
.out.block_offset_y = 0,
.out.fill_cm = out_cm,
.fill_block_w = block_w,
.fill_block_h = block_h,
.fill_argb_color = {
.val = 0xFF00FFFF,
},
.mode = PPA_TRANS_MODE_BLOCKING,
};
ccomp_timer_start();
TEST_ESP_OK(ppa_do_fill(ppa_client_handle, &oper_config));
int64_t oper_time = ccomp_timer_stop();
printf("PPA Fill - Process Time: %lld us\n", oper_time);
// Check performance
uint64_t num_pixels_processed = block_w * block_h;
uint64_t px_per_second = (num_pixels_processed - PPA_FILL_TIME_OFFSET) * 1000 * 1000 / oper_time;
printf("PPA Blend performance = %lld pixels/sec\n", px_per_second);
TEST_ASSERT_GREATER_THAN(PPA_FILL_MIN_PERFORMANCE_PX_PER_SEC, px_per_second);
TEST_ESP_OK(ppa_unregister_client(ppa_client_handle));
free(out_buf);
}