From a7adce9d1ad608902cd6832318d5d24628fdb08c Mon Sep 17 00:00:00 2001 From: Attila Kiss Date: Mon, 16 Dec 2024 08:51:01 +0100 Subject: [PATCH] feat(draw): add layer memory allocation config support (#7038) Co-authored-by: Gabor Kiss-Vamosi Co-authored-by: liamHowatt Co-authored-by: Liam <30486941+liamHowatt@users.noreply.github.com> --- Kconfig | 9 +++++++++ lv_conf_template.h | 6 ++++++ src/draw/lv_draw.c | 25 ++++++++++++++++++++----- src/draw/lv_draw_private.h | 2 +- src/lv_conf_internal.h | 12 ++++++++++++ 5 files changed, 48 insertions(+), 6 deletions(-) diff --git a/Kconfig b/Kconfig index 1ef59e1bb..6d34e896e 100644 --- a/Kconfig +++ b/Kconfig @@ -202,6 +202,15 @@ menu "LVGL configuration" it is buffered into a "simple" layer before rendering. The widget can be buffered in smaller chunks. "Transformed layers" (if `transform_angle/zoom` are set) use larger buffers and can't be drawn in chunks. + config LV_DRAW_LAYER_MAX_MEMORY + int "The maximum amount of memory that can be used for layers" + default 0 + help + Limit the max allocated memory for simple and transformed layers. + It should be at least `LV_DRAW_LAYER_SIMPLE_BUF_SIZE` sized but if transformed layers are also used + it should be enough to store the largest widget too (width x height x 4 area). + Set it to 0 to have no limit. + config LV_DRAW_THREAD_STACK_SIZE int "Stack size of draw thread in bytes" default 8192 diff --git a/lv_conf_template.h b/lv_conf_template.h index eb555a42c..fde48b192 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -144,6 +144,12 @@ /** The target buffer size for simple layer chunks. */ #define LV_DRAW_LAYER_SIMPLE_BUF_SIZE (24 * 1024) /**< [bytes]*/ +/* Limit the max allocated memory for simple and transformed layers. + * It should be at least `LV_DRAW_LAYER_SIMPLE_BUF_SIZE` sized but if transformed layers are also used + * it should be enough to store the largest widget too (width x height x 4 area). + * Set it to 0 to have no limit. */ +#define LV_DRAW_LAYER_MAX_MEMORY 0 /**< No limit by default [bytes]*/ + /** Stack size of drawing thread. * NOTE: If FreeType or ThorVG is enabled, it is recommended to set it to 32KB or more. */ diff --git a/src/draw/lv_draw.c b/src/draw/lv_draw.c index 71226c587..be260edb8 100644 --- a/src/draw/lv_draw.c +++ b/src/draw/lv_draw.c @@ -36,7 +36,7 @@ static void lv_cleanup_task(lv_draw_task_t * t, lv_display_t * disp); static inline uint32_t get_layer_size_kb(uint32_t size_byte) { - return size_byte < 1024 ? 1 : size_byte >> 10; + return (size_byte + 1023) >> 10; } /********************** @@ -448,6 +448,15 @@ void * lv_draw_layer_alloc_buf(lv_layer_t * layer) int32_t h = lv_area_get_height(&layer->buf_area); uint32_t layer_size_byte = h * lv_draw_buf_width_to_stride(w, layer->color_format); +#if LV_DRAW_LAYER_MAX_MEMORY > 0 + /* Do not allocate the layer if the sum of allocated layer sizes + * will exceed `LV_DRAW_LAYER_MAX_MEMORY` */ + if((_draw_info.used_memory_for_layers + layer_size_byte) > LV_DRAW_LAYER_MAX_MEMORY) { + LV_LOG_WARN("LV_DRAW_LAYER_MAX_MEMORY was reached when allocating the layer."); + return NULL; + } +#endif + layer->draw_buf = lv_draw_buf_create(w, h, layer->color_format, 0); if(layer->draw_buf == NULL) { @@ -456,8 +465,8 @@ void * lv_draw_layer_alloc_buf(lv_layer_t * layer) return NULL; } - _draw_info.used_memory_for_layers_kb += get_layer_size_kb(layer_size_byte); - LV_LOG_INFO("Layer memory used: %" LV_PRIu32 " kB\n", _draw_info.used_memory_for_layers_kb); + _draw_info.used_memory_for_layers += layer_size_byte; + LV_LOG_INFO("Layer memory used: %" LV_PRIu32 " kB", get_layer_size_kb(_draw_info.used_memory_for_layers)); if(lv_color_format_has_alpha(layer->color_format)) { lv_draw_buf_clear(layer->draw_buf, NULL); @@ -535,8 +544,14 @@ static void lv_cleanup_task(lv_draw_task_t * t, lv_display_t * disp) int32_t h = lv_area_get_height(&layer_drawn->buf_area); uint32_t layer_size_byte = h * layer_drawn->draw_buf->header.stride; - _draw_info.used_memory_for_layers_kb -= get_layer_size_kb(layer_size_byte); - LV_LOG_INFO("Layer memory used: %" LV_PRIu32 " kB\n", _draw_info.used_memory_for_layers_kb); + if(_draw_info.used_memory_for_layers >= layer_size_byte) { + _draw_info.used_memory_for_layers -= layer_size_byte; + } + else { + _draw_info.used_memory_for_layers = 0; + LV_LOG_WARN("More layers were freed than allocated"); + } + LV_LOG_INFO("Layer memory used: %" LV_PRIu32 " kB", get_layer_size_kb(_draw_info.used_memory_for_layers)); lv_draw_buf_destroy(layer_drawn->draw_buf); layer_drawn->draw_buf = NULL; } diff --git a/src/draw/lv_draw_private.h b/src/draw/lv_draw_private.h index 2619632ec..80f2efd6b 100644 --- a/src/draw/lv_draw_private.h +++ b/src/draw/lv_draw_private.h @@ -178,7 +178,7 @@ struct _lv_draw_unit_t { typedef struct { lv_draw_unit_t * unit_head; uint32_t unit_cnt; - uint32_t used_memory_for_layers_kb; + uint32_t used_memory_for_layers; /* measured as bytes */ #if LV_USE_OS lv_thread_sync_t sync; #else diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 6475a2317..d0427003b 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -352,6 +352,18 @@ #endif #endif +/* Limit the max allocated memory for simple and transformed layers. + * It should be at least `LV_DRAW_LAYER_SIMPLE_BUF_SIZE` sized but if transformed layers are also used + * it should be enough to store the largest widget too (width x height x 4 area). + * Set it to 0 to have no limit. */ +#ifndef LV_DRAW_LAYER_MAX_MEMORY + #ifdef CONFIG_LV_DRAW_LAYER_MAX_MEMORY + #define LV_DRAW_LAYER_MAX_MEMORY CONFIG_LV_DRAW_LAYER_MAX_MEMORY + #else + #define LV_DRAW_LAYER_MAX_MEMORY 0 /**< No limit by default [bytes]*/ + #endif +#endif + /** Stack size of drawing thread. * NOTE: If FreeType or ThorVG is enabled, it is recommended to set it to 32KB or more. */