feat(lcd): add boundary performance test

This commit is contained in:
chenjintao
2026-08-26 14:49:41 +08:00
parent 549f495800
commit f92915057a
43 changed files with 707 additions and 9 deletions
+127 -6
View File
@@ -27,6 +27,7 @@
#include "esp_lcd_panel_vendor.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
@@ -49,6 +50,7 @@
#define LCD_DIRECT_TRANSFER_TIMEOUT_MS 100U
#define LCD_DIRECT_PREHEAT_WAIT_TIMEOUT_MS 5000U
#define LCD_DIRECT_FRAME_CACHE_MAX 6U
#define LCD_DIRECT_BOUNDARY_TEST_CACHE_HIT_FRAMES LCD_DIRECT_FRAME_CACHE_MAX
#define LCD_DIRECT_PATH_MAX 96U
#define LCD_DIRECT_CHARGING_ICON_WIDTH 50U
#define LCD_DIRECT_CHARGING_ICON_X (LCD_DIRECT_WIDTH - LCD_DIRECT_CHARGING_ICON_WIDTH - 8U)
@@ -116,6 +118,13 @@ typedef struct lcd_direct_decoded_image_t {
bool pinned;
} lcd_direct_decoded_image_t;
typedef struct {
uint32_t completed_frames;
uint64_t total_draw_us;
uint32_t min_draw_us;
uint32_t max_draw_us;
} lcd_direct_sequence_perf_t;
typedef struct {
lcd_direct_icon_draw_t draw;
const lcd_direct_icon_t *icon;
@@ -778,7 +787,8 @@ static size_t lcd_direct_append_status_icons(const lcd_direct_image_t *image,
size_t icon_capacity)
{
if (image == NULL || image->path == NULL || icons == NULL ||
icon_count >= icon_capacity || strstr(image->path, "/qrcode.png") != NULL) {
icon_count >= icon_capacity || strstr(image->path, "/qrcode.png") != NULL ||
strstr(image->path, "/lcdtest") != NULL) {
return icon_count;
}
@@ -1011,7 +1021,8 @@ static esp_err_t lcd_direct_draw_png_image(const lcd_direct_image_t *image,
combined_icon_count,
prepared_icons,
sizeof(prepared_icons) / sizeof(prepared_icons[0]));
const bool clock_visible = strstr(image->path, "/qrcode.png") == NULL;
const bool clock_visible = strstr(image->path, "/qrcode.png") == NULL &&
strstr(image->path, "/lcdtest") == NULL;
char clock_text[LCD_DIRECT_CLOCK_TEXT_LENGTH + 1U] = {0};
if (clock_visible) {
lcd_direct_clock_text(clock_text);
@@ -1193,6 +1204,9 @@ static uint32_t lcd_direct_asset_sequence_hold_ms(const lcd_direct_request_t *re
LCD_DIRECT_BASIC_FACE_LAST_FRAME_HOLD_MS :
LCD_DIRECT_BASIC_FACE_FRAME_HOLD_MS;
}
if (request != NULL && strcmp(request->asset_dir, "lcd_boundary_test") == 0) {
return frame_ms;
}
if (frame_index + 1U != frame_count) {
return frame_ms;
}
@@ -1204,6 +1218,9 @@ static uint32_t lcd_direct_asset_sequence_duration_ms(const lcd_direct_request_t
if (request == NULL || request->duration_ms == 0U) {
return 0U;
}
if (strcmp(request->asset_dir, "lcd_boundary_test") == 0) {
return request->duration_ms;
}
const size_t frame_count = request->frame_order_count > 0U ? request->frame_order_count : 1U;
const uint32_t frame_ms = lcd_direct_asset_sequence_frame_ms(request);
@@ -1284,7 +1301,8 @@ static esp_err_t lcd_direct_draw_asset_sequence_once(const lcd_direct_request_t
static esp_err_t lcd_direct_draw_asset_sequence_for_duration(const lcd_direct_request_t *request,
TickType_t start_tick,
TickType_t duration_ticks)
TickType_t duration_ticks,
lcd_direct_sequence_perf_t *perf)
{
ESP_RETURN_ON_FALSE(request != NULL,
ESP_ERR_INVALID_ARG,
@@ -1312,9 +1330,21 @@ static esp_err_t lcd_direct_draw_asset_sequence_for_duration(const lcd_direct_re
request->asset_dir,
(unsigned)frame_index);
const int64_t draw_started_us = esp_timer_get_time();
ESP_RETURN_ON_ERROR(lcd_direct_draw_png_image(frame, NULL, 0),
TAG,
"draw timed asset frame failed");
const uint32_t draw_us = (uint32_t)(esp_timer_get_time() - draw_started_us);
if (perf != NULL) {
perf->completed_frames++;
perf->total_draw_us += draw_us;
if (draw_us < perf->min_draw_us) {
perf->min_draw_us = draw_us;
}
if (draw_us > perf->max_draw_us) {
perf->max_draw_us = draw_us;
}
}
const TickType_t now = xTaskGetTickCount();
const TickType_t elapsed_ticks = now - start_tick;
@@ -1332,6 +1362,91 @@ static esp_err_t lcd_direct_draw_asset_sequence_for_duration(const lcd_direct_re
return ESP_OK;
}
static void lcd_direct_log_sequence_perf(const lcd_direct_request_t *request,
const char *mode,
const lcd_direct_sequence_perf_t *perf,
uint64_t elapsed_us)
{
if (request == NULL || perf == NULL || perf->completed_frames == 0U || elapsed_us == 0U) {
return;
}
const uint64_t fps_x100 =
((uint64_t)perf->completed_frames * 100000000ULL) / elapsed_us;
const uint32_t average_draw_us =
(uint32_t)(perf->total_draw_us / perf->completed_frames);
ESP_LOGI(TAG,
"LCD sequence perf asset=%s mode=%s working_set=%u cache_capacity=%u "
"frames=%u elapsed_ms=%llu fps=%llu.%02llu draw_us_avg=%u "
"draw_us_min=%u draw_us_max=%u hold_ms=%u",
request->asset_dir,
mode,
(unsigned)request->frame_order_count,
(unsigned)LCD_DIRECT_FRAME_CACHE_MAX,
(unsigned)perf->completed_frames,
(unsigned long long)(elapsed_us / 1000ULL),
(unsigned long long)(fps_x100 / 100ULL),
(unsigned long long)(fps_x100 % 100ULL),
(unsigned)average_draw_us,
(unsigned)perf->min_draw_us,
(unsigned)perf->max_draw_us,
(unsigned)lcd_direct_asset_sequence_frame_ms(request));
}
static esp_err_t lcd_direct_run_sequence_perf_stage(const lcd_direct_request_t *request,
uint32_t duration_ms,
const char *mode)
{
const TickType_t start_tick = xTaskGetTickCount();
const TickType_t duration_ticks = pdMS_TO_TICKS(duration_ms);
const int64_t started_us = esp_timer_get_time();
lcd_direct_sequence_perf_t perf = {
.min_draw_us = UINT32_MAX,
};
esp_err_t err = lcd_direct_draw_asset_sequence_for_duration(
request, start_tick, duration_ticks, &perf);
const uint64_t elapsed_us = (uint64_t)(esp_timer_get_time() - started_us);
lcd_direct_log_sequence_perf(request, mode, &perf, elapsed_us);
return err;
}
static esp_err_t lcd_direct_run_boundary_test(const lcd_direct_request_t *request)
{
ESP_RETURN_ON_FALSE(request != NULL &&
request->frame_order_count > LCD_DIRECT_BOUNDARY_TEST_CACHE_HIT_FRAMES,
ESP_ERR_INVALID_ARG,
TAG,
"invalid LCD boundary test request");
lcd_direct_request_t cache_hit_request = *request;
cache_hit_request.frame_order_count = LCD_DIRECT_BOUNDARY_TEST_CACHE_HIT_FRAMES;
ESP_RETURN_ON_ERROR(lcd_direct_preheat_asset_sequence(&cache_hit_request),
TAG,
"preheat cache-hit frames failed");
const uint32_t cache_hit_duration_ms = request->duration_ms / 2U;
const uint32_t cache_thrash_duration_ms = request->duration_ms - cache_hit_duration_ms;
ESP_RETURN_ON_ERROR(lcd_direct_run_sequence_perf_stage(
&cache_hit_request, cache_hit_duration_ms, "cache_hit"),
TAG,
"cache-hit performance stage failed");
lcd_direct_request_t cache_thrash_preheat = *request;
cache_thrash_preheat.frame_order_count = LCD_DIRECT_BOUNDARY_TEST_CACHE_HIT_FRAMES;
const size_t first_preheat_frame =
request->frame_order_count - LCD_DIRECT_BOUNDARY_TEST_CACHE_HIT_FRAMES;
memcpy(cache_thrash_preheat.frame_order,
&request->frame_order[first_preheat_frame],
LCD_DIRECT_BOUNDARY_TEST_CACHE_HIT_FRAMES * sizeof(cache_thrash_preheat.frame_order[0]));
ESP_RETURN_ON_ERROR(lcd_direct_preheat_asset_sequence(&cache_thrash_preheat),
TAG,
"preheat cache-thrash frames failed");
return lcd_direct_run_sequence_perf_stage(
request, cache_thrash_duration_ms, "cache_thrash");
}
static esp_err_t lcd_direct_play_asset_sequence(const lcd_direct_request_t *request)
{
ESP_RETURN_ON_FALSE(request != NULL,
@@ -1359,9 +1474,15 @@ static esp_err_t lcd_direct_play_asset_sequence(const lcd_direct_request_t *requ
}
if (request->duration_ms > 0U) {
const TickType_t start_tick = xTaskGetTickCount();
const TickType_t duration_ticks = pdMS_TO_TICKS(lcd_direct_asset_sequence_duration_ms(request));
esp_err_t err = lcd_direct_draw_asset_sequence_for_duration(request, start_tick, duration_ticks);
esp_err_t err;
if (strcmp(request->asset_dir, "lcd_boundary_test") == 0) {
err = lcd_direct_run_boundary_test(request);
} else {
err = lcd_direct_run_sequence_perf_stage(
request,
lcd_direct_asset_sequence_duration_ms(request),
"normal");
}
if (err != ESP_OK) {
return err;
}
@@ -32,6 +32,25 @@ static const lcd_direct_image_t s_ota_frames[] = {
{.path = LCD_ASSET_BASE_PATH "ota3.png"},
};
static const lcd_direct_image_t s_lcd_boundary_test_frames[] = {
{.path = LCD_ASSET_BASE_PATH "lcdtest00.png"},
{.path = LCD_ASSET_BASE_PATH "lcdtest01.png"},
{.path = LCD_ASSET_BASE_PATH "lcdtest02.png"},
{.path = LCD_ASSET_BASE_PATH "lcdtest03.png"},
{.path = LCD_ASSET_BASE_PATH "lcdtest04.png"},
{.path = LCD_ASSET_BASE_PATH "lcdtest05.png"},
{.path = LCD_ASSET_BASE_PATH "lcdtest06.png"},
{.path = LCD_ASSET_BASE_PATH "lcdtest07.png"},
{.path = LCD_ASSET_BASE_PATH "lcdtest08.png"},
{.path = LCD_ASSET_BASE_PATH "lcdtest09.png"},
{.path = LCD_ASSET_BASE_PATH "lcdtest10.png"},
{.path = LCD_ASSET_BASE_PATH "lcdtest11.png"},
{.path = LCD_ASSET_BASE_PATH "lcdtest12.png"},
{.path = LCD_ASSET_BASE_PATH "lcdtest13.png"},
{.path = LCD_ASSET_BASE_PATH "lcdtest14.png"},
{.path = LCD_ASSET_BASE_PATH "lcdtest15.png"},
};
static const lcd_direct_image_t s_wifi_connecting_qr_frames[] = {
{.path = LCD_ASSET_BASE_PATH "qrcode.png"},
};
@@ -270,6 +289,14 @@ const lcd_direct_image_t *lcd_direct_asset_frame(const char *asset_dir, uint8_t
frame_index);
}
if (strcmp(asset_dir, "lcd_boundary_test") == 0) {
return lcd_direct_frame_from_array(
s_lcd_boundary_test_frames,
sizeof(s_lcd_boundary_test_frames) /
sizeof(s_lcd_boundary_test_frames[0]),
frame_index);
}
if (strcmp(asset_dir, "wifi_connecting_qr") == 0) {
return lcd_direct_frame_from_array(
s_wifi_connecting_qr_frames,