feat(cache): new cache framework (#5049)

Co-authored-by: _VIFEXTech <vifextech@foxmail.com>
This commit is contained in:
Benign X
2023-12-25 11:37:59 +08:00
committed by GitHub
parent 929bfdab33
commit a34f490394
47 changed files with 1924 additions and 1637 deletions

View File

@@ -271,8 +271,8 @@
/*Default cache size in bytes.
*Used by image decoders such as `lv_lodepng` to keep the decoded image in the memory.
*Data larger than the size of the cache also can be allocated but
*will be dropped immediately after usage.*/
*If size is not set to 0, the decoder will fail to decode when the cache is full.
*If size is 0, the cache function is not enabled and the decoded mem will be released immediately after use.*/
#define LV_CACHE_DEF_SIZE 0
/*Number of stops allowed per gradient. Increase this to allow more stops.

2
lvgl.h
View File

@@ -35,8 +35,6 @@ extern "C" {
#include "src/misc/lv_anim_timeline.h"
#include "src/misc/lv_profiler_builtin.h"
#include "src/misc/lv_rb.h"
#include "src/misc/lv_lru_rb.h"
#include "src/tick/lv_tick.h"

View File

@@ -17,8 +17,6 @@ extern "C" {
#include <stdbool.h>
#include "../misc/lv_cache.h"
#include "../misc/lv_cache_builtin.h"
#include "../draw/lv_draw.h"
#if LV_USE_DRAW_SW
#include "../draw/sw/lv_draw_sw.h"
@@ -104,9 +102,11 @@ typedef struct _lv_global_t {
lv_draw_buf_handlers_t draw_buf_handlers;
lv_ll_t img_decoder_ll;
lv_cache_manager_t cache_manager;
lv_cache_builtin_dsc_t cache_builtin_dsc;
#if LV_CACHE_DEF_SIZE > 0
lv_cache_t * img_cache;
size_t cache_builtin_max_size;
#endif
lv_draw_global_info_t draw_info;
#if defined(LV_DRAW_SW_SHADOW_CACHE_SIZE) && LV_DRAW_SW_SHADOW_CACHE_SIZE > 0
@@ -160,6 +160,10 @@ typedef struct _lv_global_t {
struct _lv_freetype_context_t * ft_context;
#endif
#if LV_USE_TINY_TTF
lv_cache_t * tiny_ttf_cache;
#endif
#if LV_USE_FONT_COMPRESSED
lv_font_fmt_rle_t font_fmt_rle;
#endif

View File

@@ -18,7 +18,6 @@ extern "C" {
#include "../misc/lv_style.h"
#include "../misc/lv_text.h"
#include "../misc/lv_profiler.h"
#include "../misc/lv_cache.h"
#include "lv_image_decoder.h"
#include "../osal/lv_os.h"
#include "lv_draw_buf.h"

View File

@@ -17,6 +17,7 @@
* DEFINES
*********************/
#define img_decoder_ll_p &(LV_GLOBAL_DEFAULT()->img_decoder_ll)
#define img_cache_p (LV_GLOBAL_DEFAULT()->img_cache)
/**********************
* TYPEDEFS
@@ -28,6 +29,13 @@
static uint32_t img_width_to_stride(lv_image_header_t * header);
#if LV_CACHE_DEF_SIZE > 0
static lv_cache_compare_res_t image_decoder_cache_compare_cb(const lv_image_cache_data_t * lhs,
const lv_image_cache_data_t * rhs);
static void image_decoder_cache_free_cb(lv_image_cache_data_t * entry, void * user_data);
static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc);
#endif
/**********************
* STATIC VARIABLES
**********************/
@@ -46,6 +54,15 @@ static uint32_t img_width_to_stride(lv_image_header_t * header);
void _lv_image_decoder_init(void)
{
_lv_ll_init(img_decoder_ll_p, sizeof(lv_image_decoder_t));
#if LV_CACHE_DEF_SIZE > 0
img_cache_p = lv_cache_create(&lv_cache_class_lru_rb_size,
sizeof(lv_image_cache_data_t), LV_CACHE_DEF_SIZE, (lv_cache_ops_t) {
.compare_cb = (lv_cache_compare_cb_t)image_decoder_cache_compare_cb,
.create_cb = NULL,
.free_cb = (lv_cache_free_cb_t)image_decoder_cache_free_cb,
});
#endif
}
/**
@@ -53,6 +70,9 @@ void _lv_image_decoder_init(void)
*/
void _lv_image_decoder_deinit(void)
{
#if LV_CACHE_DEF_SIZE > 0
lv_cache_destroy(img_cache_p, NULL);
#endif
_lv_ll_clear(img_decoder_ll_p);
}
@@ -134,6 +154,14 @@ lv_result_t lv_image_decoder_open(lv_image_decoder_dsc_t * dsc, const void * src
if(dsc->header.stride == 0) dsc->header.stride = img_width_to_stride(&dsc->header);
dsc->decoder = decoder;
#if LV_CACHE_DEF_SIZE > 0
dsc->cache = img_cache_p;
/*Check the cache first*/
if(try_cache(dsc) == LV_RESULT_OK) return LV_RESULT_OK;
#endif
res = decoder->open_cb(decoder, dsc, args);
/*Opened successfully. It is a good decoder for this image source*/
@@ -229,6 +257,36 @@ void lv_image_decoder_set_close_cb(lv_image_decoder_t * decoder, lv_image_decode
decoder->close_cb = close_cb;
}
void lv_image_decoder_set_cache_free_cb(lv_image_decoder_t * decoder, lv_cache_free_cb_t cache_free_cb)
{
decoder->cache_free_cb = cache_free_cb;
}
#if LV_CACHE_DEF_SIZE > 0
lv_cache_entry_t * lv_image_decoder_add_to_cache(lv_image_decoder_t * decoder,
lv_image_cache_data_t * search_key,
const lv_draw_buf_t * decoded, void * user_data)
{
lv_cache_entry_t * cache_entry = lv_cache_add(img_cache_p, search_key, NULL);
if(cache_entry == NULL) {
return NULL;
}
lv_image_cache_data_t * cached_data;
cached_data = lv_cache_entry_get_data(cache_entry);
/*Set the cache entry to decoder data*/
cached_data->decoded = decoded;
if(cached_data->src_type == LV_IMAGE_SRC_FILE) {
cached_data->src = lv_strdup(cached_data->src);
}
cached_data->user_data = user_data; /*Need to free data on cache invalidate instead of decoder_close*/
cached_data->decoder = decoder;
return cache_entry;
}
#endif
lv_draw_buf_t * lv_image_decoder_post_process(lv_image_decoder_dsc_t * dsc, lv_draw_buf_t * decoded)
{
if(decoded == NULL) return NULL; /*No need to adjust*/
@@ -280,3 +338,56 @@ static uint32_t img_width_to_stride(lv_image_header_t * header)
return ((uint32_t)header->w * lv_color_format_get_bpp(header->cf) + 7) >> 3;
}
}
#if LV_CACHE_DEF_SIZE > 0
static lv_cache_compare_res_t image_decoder_cache_compare_cb(
const lv_image_cache_data_t * lhs,
const lv_image_cache_data_t * rhs)
{
if(lhs->src_type == rhs->src_type) {
if(lhs->src_type == LV_IMAGE_SRC_FILE) {
int32_t cmp_res = lv_strcmp(lhs->src, rhs->src);
if(cmp_res != 0) {
return cmp_res > 0 ? 1 : -1;
}
}
else if(lhs->src_type == LV_IMAGE_SRC_VARIABLE) {
if(lhs->src != rhs->src) {
return lhs->src > rhs->src ? 1 : -1;
}
}
return 0;
}
return lhs->src_type > rhs->src_type ? 1 : -1;
}
static void image_decoder_cache_free_cb(lv_image_cache_data_t * entry, void * user_data)
{
LV_UNUSED(user_data); /*Unused*/
const lv_image_decoder_t * decoder = entry->decoder;
if(decoder && decoder->cache_free_cb) {
decoder->cache_free_cb(entry, user_data);
}
}
static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc)
{
lv_cache_t * cache = dsc->cache;
lv_image_cache_data_t search_key;
search_key.src_type = dsc->src_type;
search_key.src = dsc->src;
lv_cache_entry_t * entry = lv_cache_acquire(cache, &search_key, NULL);
if(entry) {
lv_image_cache_data_t * cached_data = lv_cache_entry_get_data(entry);
dsc->decoded = cached_data->decoded;
dsc->cache_entry = entry; /*Save the cache to release it in decoder_close*/
return LV_RESULT_OK;
}
return LV_RESULT_INVALID;
}
#endif

View File

@@ -20,6 +20,7 @@ extern "C" {
#include "../misc/lv_fs.h"
#include "../misc/lv_types.h"
#include "../misc/lv_area.h"
#include "../misc/cache/lv_cache.h"
/*********************
* DEFINES
@@ -111,10 +112,22 @@ typedef struct _lv_image_decoder_t {
lv_image_decoder_open_f_t open_cb;
lv_image_decoder_get_area_cb_t get_area_cb;
lv_image_decoder_close_f_t close_cb;
uint32_t cache_data_type;
lv_cache_free_cb_t cache_free_cb;
void * user_data;
} lv_image_decoder_t;
typedef struct _lv_image_decoder_cache_data_t {
lv_cache_slot_size_t slot;
const void * src;
lv_image_src_t src_type;
const lv_draw_buf_t * decoded;
const lv_image_decoder_t * decoder;
void * user_data;
} lv_image_cache_data_t;
/**Describe an image decoding session. Stores data about the decoding*/
typedef struct _lv_image_decoder_dsc_t {
/**The decoder which was able to open the image source*/
@@ -149,8 +162,10 @@ typedef struct _lv_image_decoder_dsc_t {
* Can be set in `open` function or set NULL.*/
const char * error_msg;
lv_cache_t * cache;
/**Point to cache entry information*/
struct _lv_cache_entry_t * cache_entry;
lv_cache_entry_t * cache_entry;
/**Store any custom data here is required*/
void * user_data;
@@ -268,6 +283,14 @@ void lv_image_decoder_set_get_area_cb(lv_image_decoder_t * decoder, lv_image_dec
*/
void lv_image_decoder_set_close_cb(lv_image_decoder_t * decoder, lv_image_decoder_close_f_t close_cb);
void lv_image_decoder_set_cache_free_cb(lv_image_decoder_t * decoder, lv_cache_free_cb_t cache_free_cb);
#if LV_CACHE_DEF_SIZE > 0
lv_cache_entry_t * lv_image_decoder_add_to_cache(lv_image_decoder_t * decoder,
lv_image_cache_data_t * search_key,
const lv_draw_buf_t * decoded, void * user_data);
#endif
/**
* Check the decoded image, make any modification if decoder `args` requires.
* @note A new draw buf will be allocated if provided `decoded` is not modifiable or stride mismatch etc.

View File

@@ -40,6 +40,7 @@ static void execute_drawing(lv_draw_sdl_unit_t * u);
static int32_t dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer);
static int32_t evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task);
static bool draw_to_texture(lv_draw_sdl_unit_t * u, cache_data_t * data);
/**********************
* GLOBAL PROTOTYPES
@@ -58,13 +59,59 @@ static SDL_Texture * layer_get_texture(lv_layer_t * layer);
/**********************
* GLOBAL FUNCTIONS
**********************/
static bool sdl_texture_cache_create_cb(cache_data_t * cached_data, void * user_data)
{
return draw_to_texture((lv_draw_sdl_unit_t *)user_data, cached_data);
}
static void sdl_texture_cache_free_cb(cache_data_t * cached_data, void * user_data)
{
LV_UNUSED(user_data);
lv_free(cached_data->draw_dsc);
SDL_DestroyTexture(cached_data->texture);
cached_data->draw_dsc = NULL;
cached_data->texture = NULL;
}
static lv_cache_compare_res_t sdl_texture_cache_compare_cb(const cache_data_t * lhs, const cache_data_t * rhs)
{
if(lhs == rhs) return 0;
if(lhs->w != rhs->w) {
return lhs->w > rhs->w ? 1 : -1;
}
if(lhs->h != rhs->h) {
return lhs->h > rhs->h ? 1 : -1;
}
uint32_t lhs_dsc_size = lhs->draw_dsc->dsc_size;
uint32_t rhs_dsc_size = rhs->draw_dsc->dsc_size;
if(lhs_dsc_size != rhs_dsc_size) {
return lhs_dsc_size > rhs_dsc_size ? 1 : -1;
}
int cmp_res = memcmp(lhs->draw_dsc, rhs->draw_dsc, lhs->draw_dsc->dsc_size);
if(cmp_res != 0) {
return cmp_res > 0 ? 1 : -1;
}
return 0;
}
void lv_draw_sdl_init(void)
{
lv_draw_sdl_unit_t * draw_sdl_unit = lv_draw_create_unit(sizeof(lv_draw_sdl_unit_t));
draw_sdl_unit->base_unit.dispatch_cb = dispatch;
draw_sdl_unit->base_unit.evaluate_cb = evaluate;
draw_sdl_unit->texture_cache_data_type = lv_cache_register_data_type();
draw_sdl_unit->texture_cache = lv_cache_create(&lv_cache_class_lru_rb_count,
sizeof(cache_data_t), 128, (lv_cache_ops_t) {
.compare_cb = (lv_cache_compare_cb_t)sdl_texture_cache_compare_cb,
.create_cb = (lv_cache_create_cb_t)sdl_texture_cache_create_cb,
.free_cb = (lv_cache_free_cb_t)sdl_texture_cache_free_cb,
});
}
/**********************
@@ -121,34 +168,7 @@ static int32_t evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task)
return 0;
}
bool compare_cb(const void * data1, const void * data2, size_t data_size)
{
LV_UNUSED(data_size);
const cache_data_t * d1 = data1;
const cache_data_t * d2 = data2;
if(d1->w != d2->w) return false;
if(d1->h != d2->h) return false;
if(d1->draw_dsc->dsc_size != d2->draw_dsc->dsc_size) return false;
if(memcmp(d1->draw_dsc, d2->draw_dsc, d1->draw_dsc->dsc_size)) return false;
return true;
}
void invalidate_cb(lv_cache_entry_t * e)
{
const cache_data_t * d = e->data;
lv_free((void *)d->draw_dsc);
SDL_DestroyTexture(d->texture);
lv_free((void *)d);
e->data = NULL;
e->data_size = 0;
}
static lv_cache_entry_t * draw_to_texture(lv_draw_sdl_unit_t * u)
static bool draw_to_texture(lv_draw_sdl_unit_t * u, cache_data_t * data)
{
lv_draw_task_t * task = u->task_act;
@@ -165,7 +185,6 @@ static lv_cache_entry_t * draw_to_texture(lv_draw_sdl_unit_t * u)
lv_display_t * disp = _lv_refr_get_disp_refreshing();
uint32_t tick = lv_tick_get();
SDL_Texture * texture = NULL;
switch(task->type) {
case LV_DRAW_TASK_TYPE_FILL: {
@@ -210,7 +229,7 @@ static lv_cache_entry_t * draw_to_texture(lv_draw_sdl_unit_t * u)
SDL_Surface * surface = IMG_Load(&path[2]);
if(surface == NULL) {
fprintf(stderr, "could not load image: %s\n", IMG_GetError());
return NULL;
return false;
}
SDL_Renderer * renderer = lv_sdl_window_get_renderer(disp);
@@ -218,7 +237,7 @@ static lv_cache_entry_t * draw_to_texture(lv_draw_sdl_unit_t * u)
break;
}
default:
return NULL;
return false;
}
while(dest_layer.draw_task_head) {
@@ -244,7 +263,6 @@ static lv_cache_entry_t * draw_to_texture(lv_draw_sdl_unit_t * u)
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
}
cache_data_t * data = lv_malloc(sizeof(cache_data_t));
lv_draw_dsc_base_t * base_dsc = task->draw_dsc;
data->draw_dsc = lv_malloc(base_dsc->dsc_size);
@@ -253,14 +271,7 @@ static lv_cache_entry_t * draw_to_texture(lv_draw_sdl_unit_t * u)
data->h = lv_area_get_height(&task->area);
data->texture = texture;
lv_cache_entry_t * e = lv_cache_add(data, sizeof(cache_data_t), u->texture_cache_data_type,
lv_area_get_size(&task->area) * 4);
e->compare_cb = compare_cb;
e->invalidate_cb = invalidate_cb;
e->weight = lv_tick_elaps(tick);
e->weight += lv_area_get_size(&task->area) / 10000;
if(e->weight == 0) e->weight++;
return e;
return true;
}
static void blend_texture_layer(lv_draw_sdl_unit_t * u)
@@ -310,20 +321,14 @@ static void draw_from_cached_texture(lv_draw_sdl_unit_t * u)
void * user_data_saved = data_to_find.draw_dsc->user_data;
data_to_find.draw_dsc->user_data = NULL;
lv_cache_lock();
lv_cache_entry_t * e = lv_cache_find_by_data(&data_to_find, sizeof(data_to_find), u->texture_cache_data_type);
data_to_find.draw_dsc->user_data = user_data_saved;
if(e == NULL) {
printf("cache_miss %d\n", t->type);
e = draw_to_texture(u);
}
if(e == NULL) {
lv_cache_unlock();
lv_cache_entry_t * entry_cached = lv_cache_acquire_or_create(u->texture_cache, &data_to_find, u);
if(!entry_cached) {
return;
}
const cache_data_t * data_cached = lv_cache_get_data(e);
data_to_find.draw_dsc->user_data = user_data_saved;
cache_data_t * data_cached = lv_cache_entry_get_data(entry_cached);
SDL_Texture * texture = data_cached->texture;
lv_display_t * disp = _lv_refr_get_disp_refreshing();
SDL_Renderer * renderer = lv_sdl_window_get_renderer(disp);
@@ -368,8 +373,7 @@ static void draw_from_cached_texture(lv_draw_sdl_unit_t * u)
SDL_RenderSetClipRect(renderer, NULL);
lv_cache_release(e);
lv_cache_unlock();
lv_cache_release(u->texture_cache, entry_cached, u);
}
static void execute_drawing(lv_draw_sdl_unit_t * u)

View File

@@ -14,8 +14,10 @@ extern "C" {
* INCLUDES
*********************/
#include "../lv_draw.h"
#if LV_USE_DRAW_SDL
#include <src/misc/cache/lv_cache.h>
#include "../../misc/lv_area.h"
#include "../../misc/lv_color.h"
#include "../../display/lv_display.h"
@@ -33,6 +35,7 @@ typedef struct {
lv_draw_unit_t base_unit;
struct _lv_draw_task_t * task_act;
uint32_t texture_cache_data_type;
lv_cache_t * texture_cache;
} lv_draw_sdl_unit_t;
#if LV_DRAW_SW_SHADOW_CACHE_SIZE

View File

@@ -14,7 +14,6 @@
#include "../../misc/lv_log.h"
#include "../../core/lv_refr.h"
#include "../../stdlib/lv_mem.h"
#include "../../misc/lv_cache.h"
#include "../../misc/lv_math.h"
#include "../../misc/lv_color.h"
#include "../../stdlib/lv_string.h"
@@ -70,6 +69,7 @@ void lv_draw_sw_layer(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * dr
new_draw_dsc.src = &img_dsc;
lv_draw_sw_image(draw_unit, &new_draw_dsc, coords);
lv_image_cache_drop(&img_dsc);
#if LV_USE_LAYER_DEBUG || LV_USE_PARALLEL_DRAW_DEBUG
lv_area_t area_rot;

View File

@@ -62,9 +62,7 @@ void lv_draw_vg_lite_layer(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t
new_draw_dsc.src = &img_dsc;
lv_draw_vg_lite_img(draw_unit, &new_draw_dsc, coords);
lv_cache_lock();
lv_cache_invalidate_by_src(&img_dsc, LV_CACHE_SRC_TYPE_POINTER);
lv_cache_unlock();
lv_image_cache_invalidate(&img_dsc);
}
/**********************

View File

@@ -197,7 +197,9 @@ static void lv_barcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj
lv_draw_buf_t * draw_buf = lv_canvas_get_draw_buf(obj);
if(draw_buf == NULL) return;
lv_image_cache_drop(draw_buf);
/*@fixme destroy buffer in cache free_cb.*/
lv_draw_buf_destroy(draw_buf);
}

View File

@@ -70,8 +70,8 @@ static lv_fs_res_t fs_read_file_at(lv_fs_file_t * f, uint32_t pos, void * buff,
static lv_result_t decompress_image(lv_image_decoder_dsc_t * dsc, const lv_image_compressed_t * compressed);
static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc);
static void cache_invalidate_cb(lv_cache_entry_t * entry);
static bool bin_decoder_decode_data(lv_image_decoder_dsc_t * dsc);
static void bin_decoder_cache_free_cb(lv_image_cache_data_t * cached_data, void * user_data);
/**********************
* STATIC VARIABLES
@@ -103,7 +103,7 @@ void lv_bin_decoder_init(void)
lv_image_decoder_set_open_cb(decoder, lv_bin_decoder_open);
lv_image_decoder_set_get_area_cb(decoder, lv_bin_decoder_get_area);
lv_image_decoder_set_close_cb(decoder, lv_bin_decoder_close);
decoder->cache_data_type = lv_cache_register_data_type();
lv_image_decoder_set_cache_free_cb(decoder, (lv_cache_free_cb_t)bin_decoder_cache_free_cb);
}
lv_result_t lv_bin_decoder_info(lv_image_decoder_t * decoder, const void * src, lv_image_header_t * header)
@@ -173,120 +173,8 @@ lv_result_t lv_bin_decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d
LV_UNUSED(decoder);
LV_UNUSED(args);
/*Check the cache first*/
if(try_cache(dsc) == LV_RESULT_OK) return LV_RESULT_OK;
lv_fs_res_t res = LV_RESULT_INVALID;
uint32_t t = lv_tick_get();
/*Open the file if it's a file*/
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
/*Support only "*.bin" files*/
if(lv_strcmp(lv_fs_get_ext(dsc->src), "bin")) return LV_RESULT_INVALID;
/*If the file was open successfully save the file descriptor*/
decoder_data_t * decoder_data = get_decoder_data(dsc);
if(decoder_data == NULL) {
return LV_RESULT_INVALID;
}
dsc->user_data = decoder_data;
lv_fs_file_t * f = lv_malloc(sizeof(*f));
if(f == NULL) {
free_decoder_data(dsc);
return LV_RESULT_INVALID;
}
res = lv_fs_open(f, dsc->src, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("Open file failed: %d", res);
lv_free(f);
free_decoder_data(dsc);
return LV_RESULT_INVALID;
}
decoder_data->f = f; /*Now free_decoder_data will take care of the file*/
lv_color_format_t cf = dsc->header.cf;
if(dsc->header.flags & LV_IMAGE_FLAGS_COMPRESSED) {
res = decode_compressed(decoder, dsc);
}
else if(LV_COLOR_FORMAT_IS_INDEXED(cf)) {
/*Palette for indexed image and whole image of A8 image are always loaded to RAM for simplicity*/
res = decode_indexed(decoder, dsc);
}
else if(LV_COLOR_FORMAT_IS_ALPHA_ONLY(cf)) {
res = decode_alpha_only(decoder, dsc);
}
#if LV_BIN_DECODER_RAM_LOAD
else if(cf == LV_COLOR_FORMAT_ARGB8888 \
|| cf == LV_COLOR_FORMAT_XRGB8888 \
|| cf == LV_COLOR_FORMAT_RGB888 \
|| cf == LV_COLOR_FORMAT_RGB565 \
|| cf == LV_COLOR_FORMAT_RGB565A8) {
res = decode_rgb(decoder, dsc);
}
#else
else {
/* decode them in get_area_cb */
res = LV_RESULT_OK;
}
#endif
}
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
/*The variables should have valid data*/
lv_image_dsc_t * image = (lv_image_dsc_t *)dsc->src;
if(image->data == NULL) {
return LV_RESULT_INVALID;
}
lv_color_format_t cf = image->header.cf;
if(dsc->header.flags & LV_IMAGE_FLAGS_COMPRESSED) {
res = decode_compressed(decoder, dsc);
}
else if(LV_COLOR_FORMAT_IS_INDEXED(cf)) {
/*Need decoder data to store converted image*/
decoder_data_t * decoder_data = get_decoder_data(dsc);
if(decoder_data == NULL) {
return LV_RESULT_INVALID;
}
res = decode_indexed(decoder, dsc);
}
else if(LV_COLOR_FORMAT_IS_ALPHA_ONLY(cf)) {
/*Alpha only image will need decoder data to store pointer to decoded image, to free it when decoder closes*/
decoder_data_t * decoder_data = get_decoder_data(dsc);
if(decoder_data == NULL) {
return LV_RESULT_INVALID;
}
res = decode_alpha_only(decoder, dsc);
}
else {
/*In case of uncompressed formats the image stored in the ROM/RAM.
*So simply give its pointer*/
decoder_data_t * decoder_data = get_decoder_data(dsc);
lv_draw_buf_t * decoded = &decoder_data->c_array;
dsc->decoded = decoded;
lv_draw_buf_from_image(decoded, image);
if(decoded->header.stride == 0) {
/*Use the auto calculated value from decoder_info callback*/
decoded->header.stride = dsc->header.stride;
}
res = LV_RESULT_OK;
}
}
if(res != LV_RESULT_OK) {
free_decoder_data(dsc);
return res;
}
bool create_res = bin_decoder_decode_data(dsc);
if(create_res == false) return LV_RESULT_INVALID;
if(dsc->decoded == NULL) return LV_RESULT_OK; /*Need to read via get_area_cb*/
lv_draw_buf_t * decoded = (lv_draw_buf_t *)dsc->decoded;
@@ -302,37 +190,25 @@ lv_result_t lv_bin_decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d
decoder_data_t * decoder_data = get_decoder_data(dsc);
decoder_data->decoded = adjusted; /*Now this new buffer need to be free'd on decoder close*/
}
dsc->decoded = adjusted;
#if LV_CACHE_DEF_SIZE > 0
/*Add it to cache*/
t = lv_tick_elaps(t);
lv_cache_lock();
lv_cache_entry_t * cache = lv_cache_add(NULL, 0, decoder->cache_data_type, dsc->header.w * dsc->header.h * 4);
if(cache == NULL) {
lv_cache_unlock();
lv_image_cache_data_t search_key;
search_key.src_type = dsc->src_type;
search_key.src = dsc->src;
search_key.slot.size = dsc->decoded->data_size;
lv_cache_entry_t * cache_entry = lv_image_decoder_add_to_cache(decoder, &search_key, dsc->decoded, dsc->user_data);
if(cache_entry == NULL) {
free_decoder_data(dsc);
return LV_RESULT_INVALID;
}
dsc->cache_entry = cache_entry;
#endif
cache->weight = t;
cache->data = dsc->decoded;
cache->invalidate_cb = cache_invalidate_cb;
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
cache->src = lv_strdup(dsc->src);
cache->src_type = LV_CACHE_SRC_TYPE_PATH;
}
else {
cache->src_type = LV_CACHE_SRC_TYPE_POINTER;
cache->src = dsc->src;
}
cache->user_data = dsc->user_data; /*Need to free data on cache invalidate instead of decoder_close*/
dsc->decoded = lv_cache_get_data(cache); /*@note: Must get from cache to increase reference count.*/
dsc->cache_entry = cache;
lv_cache_unlock();
return res;
return LV_RESULT_OK;
}
void lv_bin_decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
@@ -347,9 +223,7 @@ void lv_bin_decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t *
if(dsc->cache_entry) {
/*Decoded data is in cache, release it from cache's callback*/
lv_cache_lock();
lv_cache_release(dsc->cache_entry);
lv_cache_unlock();
lv_cache_release(dsc->cache, dsc->cache_entry, NULL);
}
else {
/*Data not in cache, free the memory manually*/
@@ -1025,42 +899,130 @@ static lv_result_t decompress_image(lv_image_decoder_dsc_t * dsc, const lv_image
return LV_RESULT_OK;
}
static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc)
static bool bin_decoder_decode_data(lv_image_decoder_dsc_t * dsc)
{
lv_cache_lock();
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
const char * fn = dsc->src;
lv_image_decoder_t * decoder = dsc->decoder;
lv_cache_entry_t * cache = lv_cache_find_by_src(NULL, fn, LV_CACHE_SRC_TYPE_PATH);
if(cache) {
dsc->decoded = lv_cache_get_data(cache);
dsc->cache_entry = cache; /*Save the cache to release it in decoder_close*/
lv_cache_unlock();
return LV_RESULT_OK;
lv_fs_res_t res = LV_RESULT_INVALID;
/*Open the file if it's a file*/
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
/*Support only "*.bin" files*/
if(lv_strcmp(lv_fs_get_ext(dsc->src), "bin")) return false;
/*If the file was open successfully save the file descriptor*/
decoder_data_t * decoder_data = get_decoder_data(dsc);
if(decoder_data == NULL) {
return false;
}
dsc->user_data = decoder_data;
lv_fs_file_t * f = lv_malloc(sizeof(*f));
if(f == NULL) {
free_decoder_data(dsc);
return false;
}
res = lv_fs_open(f, dsc->src, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) {
LV_LOG_WARN("Open file failed: %d", res);
lv_free(f);
free_decoder_data(dsc);
return false;
}
decoder_data->f = f; /*Now free_decoder_data will take care of the file*/
lv_color_format_t cf = dsc->header.cf;
if(dsc->header.flags & LV_IMAGE_FLAGS_COMPRESSED) {
res = decode_compressed(decoder, dsc);
}
else if(LV_COLOR_FORMAT_IS_INDEXED(cf)) {
/*Palette for indexed image and whole image of A8 image are always loaded to RAM for simplicity*/
res = decode_indexed(decoder, dsc);
}
else if(LV_COLOR_FORMAT_IS_ALPHA_ONLY(cf)) {
res = decode_alpha_only(decoder, dsc);
}
#if LV_BIN_DECODER_RAM_LOAD
else if(cf == LV_COLOR_FORMAT_ARGB8888 \
|| cf == LV_COLOR_FORMAT_XRGB8888 \
|| cf == LV_COLOR_FORMAT_RGB888 \
|| cf == LV_COLOR_FORMAT_RGB565 \
|| cf == LV_COLOR_FORMAT_RGB565A8) {
res = decode_rgb(decoder, dsc);
}
#else
else {
/* decode them in get_area_cb */
res = LV_RESULT_OK;
}
#endif
}
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
const lv_image_dsc_t * img_dsc = dsc->src;
/*The variables should have valid data*/
lv_image_dsc_t * image = (lv_image_dsc_t *)dsc->src;
if(image->data == NULL) {
return false;
}
lv_cache_entry_t * cache = lv_cache_find_by_src(NULL, img_dsc, LV_CACHE_SRC_TYPE_POINTER);
if(cache) {
dsc->decoded = lv_cache_get_data(cache);
dsc->cache_entry = cache; /*Save the cache to release it in decoder_close*/
lv_cache_unlock();
return LV_RESULT_OK;
lv_color_format_t cf = image->header.cf;
if(dsc->header.flags & LV_IMAGE_FLAGS_COMPRESSED) {
res = decode_compressed(decoder, dsc);
}
else if(LV_COLOR_FORMAT_IS_INDEXED(cf)) {
/*Need decoder data to store converted image*/
decoder_data_t * decoder_data = get_decoder_data(dsc);
if(decoder_data == NULL) {
return false;
}
res = decode_indexed(decoder, dsc);
}
else if(LV_COLOR_FORMAT_IS_ALPHA_ONLY(cf)) {
/*Alpha only image will need decoder data to store pointer to decoded image, to free it when decoder closes*/
decoder_data_t * decoder_data = get_decoder_data(dsc);
if(decoder_data == NULL) {
return false;
}
res = decode_alpha_only(decoder, dsc);
}
else {
/*In case of uncompressed formats the image stored in the ROM/RAM.
*So simply give its pointer*/
decoder_data_t * decoder_data = get_decoder_data(dsc);
lv_draw_buf_t * decoded = &decoder_data->c_array;
dsc->decoded = decoded;
lv_draw_buf_from_image(decoded, image);
if(decoded->header.stride == 0) {
/*Use the auto calculated value from decoder_info callback*/
decoded->header.stride = dsc->header.stride;
}
res = LV_RESULT_OK;
}
}
lv_cache_unlock();
return LV_RESULT_INVALID;
if(res != LV_RESULT_OK) {
free_decoder_data(dsc);
return false;
}
return true;
}
static void cache_invalidate_cb(lv_cache_entry_t * entry)
static void bin_decoder_cache_free_cb(lv_image_cache_data_t * cached_data, void * user_data)
{
LV_UNUSED(user_data); /*Unused*/
lv_image_decoder_dsc_t fake = { 0 };
fake.user_data = entry->user_data;
fake.user_data = cached_data->user_data;
free_decoder_data(&fake);
if(entry->src_type == LV_CACHE_SRC_TYPE_PATH) lv_free((void *)entry->src);
if(cached_data->src_type == LV_IMAGE_SRC_FILE) lv_free((void *)cached_data->src);
}

View File

@@ -800,9 +800,7 @@ static void lv_ffmpeg_player_frame_update_cb(lv_timer_t * timer)
return;
}
lv_cache_lock();
lv_cache_invalidate_by_src(lv_image_get_src(obj), LV_CACHE_SRC_TYPE_POINTER);
lv_cache_unlock();
lv_image_cache_drop(lv_image_get_src(obj));
lv_obj_invalidate(obj);
}
@@ -839,9 +837,7 @@ static void lv_ffmpeg_player_destructor(const lv_obj_class_t * class_p,
player->timer = NULL;
}
lv_cache_lock();
lv_cache_invalidate_by_src(lv_image_get_src(obj), LV_CACHE_SRC_TYPE_POINTER);
lv_cache_unlock();
lv_image_cache_drop(lv_image_get_src(obj));
ffmpeg_close(player->ffmpeg_ctx);
player->ffmpeg_ctx = NULL;

View File

@@ -49,11 +49,11 @@ struct _lv_freetype_cache_node_t {
FT_Face face;
/*glyph outline cache*/
lv_lru_rb_t * glyph_outline_lru;
lv_cache_t * glyph_outline_cache;
int outline_cnt;
/*glyph size cache*/
lv_lru_rb_t * glyph_dsc_lru;
lv_cache_t * glyph_dsc_cache;
int dsc_cnt;
};
@@ -82,14 +82,14 @@ static lv_freetype_outline_node_t * lv_freetype_outline_lookup(lv_freetype_font_
/*glyph dsc cache lru callbacks*/
static bool freetype_glyph_outline_create_cb(lv_freetype_outline_node_t * node, lv_freetype_font_dsc_t * dsc);
static void freetype_glyph_outline_free_cb(lv_freetype_outline_node_t * node, lv_freetype_font_dsc_t * dsc);
static lv_lru_rb_compare_res_t freetype_glyph_outline_cmp_cb(const lv_freetype_outline_node_t * node_a,
const lv_freetype_outline_node_t * node_b);
static lv_cache_compare_res_t freetype_glyph_outline_cmp_cb(const lv_freetype_outline_node_t * node_a,
const lv_freetype_outline_node_t * node_b);
/*glyph dsc cache lru callbacks*/
static bool freetype_glyph_dsc_create_cb(lv_freetype_glyph_dsc_node_t * glyph_dsc_node, lv_freetype_font_dsc_t * dsc);
static void freetype_glyph_dsc_free_cb(lv_freetype_glyph_dsc_node_t * glyph_dsc_node, lv_freetype_font_dsc_t * dsc);
static lv_lru_rb_compare_res_t freetype_glyph_dsc_cmp_cb(const lv_freetype_glyph_dsc_node_t * node_a,
const lv_freetype_glyph_dsc_node_t * node_b);
static lv_cache_compare_res_t freetype_glyph_dsc_cmp_cb(const lv_freetype_glyph_dsc_node_t * node_a,
const lv_freetype_glyph_dsc_node_t * node_b);
/**********************
* STATIC VARIABLES
@@ -266,14 +266,23 @@ static lv_freetype_cache_node_t * lv_freetype_cache_node_lookup(lv_freetype_cont
cache->ref_cnt = 1;
cache->cache_context = cache_context;
cache->glyph_dsc_lru = lv_lru_rb_create(sizeof(lv_freetype_glyph_dsc_node_t), LV_FREETYPE_GLYPH_DSC_CACHE_SIZE,
(lv_lru_rb_compare_cb_t)freetype_glyph_dsc_cmp_cb,
(lv_lru_rb_create_cb_t)freetype_glyph_dsc_create_cb,
(lv_lru_rb_free_cb_t)freetype_glyph_dsc_free_cb);
cache->glyph_outline_lru = lv_lru_rb_create(sizeof(lv_freetype_outline_node_t), LV_FREETYPE_CACHE_FT_OUTLINES,
(lv_lru_rb_compare_cb_t)freetype_glyph_outline_cmp_cb,
(lv_lru_rb_create_cb_t)freetype_glyph_outline_create_cb,
(lv_lru_rb_free_cb_t)freetype_glyph_outline_free_cb);
lv_cache_ops_t glyph_dsc_cache_ops = {
.create_cb = (lv_cache_create_cb_t)freetype_glyph_dsc_create_cb,
.free_cb = (lv_cache_free_cb_t)freetype_glyph_dsc_free_cb,
.compare_cb = (lv_cache_compare_cb_t)freetype_glyph_dsc_cmp_cb,
};
lv_cache_ops_t glyph_outline_cache_ops = {
.create_cb = (lv_cache_create_cb_t)freetype_glyph_outline_create_cb,
.free_cb = (lv_cache_free_cb_t)freetype_glyph_outline_free_cb,
.compare_cb = (lv_cache_compare_cb_t)freetype_glyph_outline_cmp_cb,
};
cache->glyph_dsc_cache = lv_cache_create(&lv_cache_class_lru_rb, sizeof(lv_freetype_glyph_dsc_node_t),
LV_FREETYPE_GLYPH_DSC_CACHE_SIZE,
glyph_dsc_cache_ops);
cache->glyph_outline_cache = lv_cache_create(&lv_cache_class_lru_rb_count, sizeof(lv_freetype_outline_node_t),
LV_FREETYPE_CACHE_FT_OUTLINES,
glyph_outline_cache_ops);
LV_LOG_INFO("outline cache(name: %s, style: 0x%x) create %p, ref_cnt = %d",
pathname, style, cache, cache->ref_cnt);
@@ -303,8 +312,8 @@ static void lv_freetype_cache_node_drop(lv_freetype_font_dsc_t * dsc)
lv_ll_t * cache_ll = &cache_node->cache_context->cache_ll;
_lv_ll_remove(cache_ll, cache_node);
lv_lru_rb_destroy(cache_node->glyph_dsc_lru, dsc);
lv_lru_rb_destroy(cache_node->glyph_outline_lru, dsc);
lv_cache_destroy(cache_node->glyph_dsc_cache, dsc);
lv_cache_destroy(cache_node->glyph_outline_cache, dsc);
lv_free(cache_node);
}
@@ -352,8 +361,8 @@ static void freetype_glyph_dsc_free_cb(lv_freetype_glyph_dsc_node_t * glyph_dsc_
LV_LOG_INFO("cnt = %d", --dsc->cache_node->dsc_cnt);
}
static lv_lru_rb_compare_res_t freetype_glyph_dsc_cmp_cb(const lv_freetype_glyph_dsc_node_t * node_a,
const lv_freetype_glyph_dsc_node_t * node_b)
static lv_cache_compare_res_t freetype_glyph_dsc_cmp_cb(const lv_freetype_glyph_dsc_node_t * node_a,
const lv_freetype_glyph_dsc_node_t * node_b)
{
if(node_a->glyph_index != node_b->glyph_index)
return (node_a->glyph_index > node_b->glyph_index) ? 1 : -1;
@@ -394,10 +403,11 @@ static bool freetype_get_glyph_dsc_cb(const lv_font_t * font,
tmp_node.glyph_index = glyph_index;
tmp_node.size = dsc->size;
lv_freetype_glyph_dsc_node_t * new_node = lv_lru_rb_get_or_create(cache_node->glyph_dsc_lru, &tmp_node, dsc);
if(!new_node) {
lv_cache_entry_t * entry = lv_cache_acquire_or_create(cache_node->glyph_dsc_cache, &tmp_node, dsc);
if(entry == NULL) {
return false;
}
lv_freetype_glyph_dsc_node_t * new_node = lv_cache_entry_get_data(entry);
*dsc_out = new_node->glyph_dsc;
if((dsc->style & LV_FREETYPE_FONT_STYLE_ITALIC) && (unicode_letter_next == '\0')) {
@@ -437,8 +447,8 @@ static void freetype_glyph_outline_free_cb(lv_freetype_outline_node_t * node, lv
LV_LOG_INFO("cnt = %d", --dsc->cache_node->outline_cnt);
}
static lv_lru_rb_compare_res_t freetype_glyph_outline_cmp_cb(const lv_freetype_outline_node_t * node_a,
const lv_freetype_outline_node_t * node_b)
static lv_cache_compare_res_t freetype_glyph_outline_cmp_cb(const lv_freetype_outline_node_t * node_a,
const lv_freetype_outline_node_t * node_b)
{
if(node_a->glyph_index == node_b->glyph_index) {
return 0;
@@ -466,11 +476,12 @@ static lv_freetype_outline_node_t * lv_freetype_outline_lookup(lv_freetype_font_
lv_freetype_outline_node_t tmp_node;
tmp_node.glyph_index = glyph_index;
lv_freetype_outline_node_t * new_node = lv_lru_rb_get_or_create(cache_node->glyph_outline_lru, &tmp_node, dsc);
if(!new_node) {
lv_cache_entry_t * entry = lv_cache_acquire_or_create(cache_node->glyph_outline_cache, &tmp_node, dsc);
if(!entry) {
LV_LOG_ERROR("glyph outline lookup failed for glyph_index = %u", glyph_index);
return NULL;
}
lv_freetype_outline_node_t * new_node = lv_cache_entry_get_data(entry);
return new_node;
}

View File

@@ -62,9 +62,7 @@ void lv_gif_set_src(lv_obj_t * obj, const void * src)
/*Close previous gif if any*/
if(gifobj->gif) {
lv_cache_lock();
lv_cache_invalidate_by_src(lv_image_get_src(obj), LV_CACHE_SRC_TYPE_POINTER);
lv_cache_unlock();
lv_image_cache_drop(lv_image_get_src(obj));
gd_close_gif(gifobj->gif);
gifobj->gif = NULL;
@@ -150,9 +148,7 @@ static void lv_gif_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
LV_UNUSED(class_p);
lv_gif_t * gifobj = (lv_gif_t *) obj;
lv_cache_lock();
lv_cache_invalidate_by_src(lv_image_get_src(obj), LV_CACHE_SRC_TYPE_POINTER);
lv_cache_unlock();
lv_image_cache_drop(lv_image_get_src(obj));
if(gifobj->gif)
gd_close_gif(gifobj->gif);
@@ -178,9 +174,7 @@ static void next_frame_task_cb(lv_timer_t * t)
gd_render_frame(gifobj->gif, (uint8_t *)gifobj->imgdsc.data);
lv_cache_lock();
lv_cache_invalidate_by_src(lv_image_get_src(obj), LV_CACHE_SRC_TYPE_POINTER);
lv_cache_unlock();
lv_image_cache_drop(lv_image_get_src(obj));
lv_obj_invalidate(obj);
}

View File

@@ -39,9 +39,7 @@ static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t *
static lv_draw_buf_t * decode_jpeg_file(const char * filename);
static bool get_jpeg_size(const char * filename, uint32_t * width, uint32_t * height);
static void error_exit(j_common_ptr cinfo);
static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc);
static void cache_invalidate_cb(lv_cache_entry_t * entry);
static void jpeg_decoder_cache_free_cb(lv_image_cache_data_t * cached_data, void * user_data);
/**********************
* STATIC VARIABLES
**********************/
@@ -63,7 +61,7 @@ void lv_libjpeg_turbo_init(void)
lv_image_decoder_set_info_cb(dec, decoder_info);
lv_image_decoder_set_open_cb(dec, decoder_open);
lv_image_decoder_set_close_cb(dec, decoder_close);
dec->cache_data_type = lv_cache_register_data_type();
lv_image_decoder_set_cache_free_cb(dec, (lv_cache_free_cb_t)jpeg_decoder_cache_free_cb);
}
void lv_libjpeg_turbo_deinit(void)
@@ -153,44 +151,31 @@ static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d
LV_UNUSED(decoder); /*Unused*/
LV_UNUSED(args); /*Unused*/
/*Check the cache first*/
if(try_cache(dsc) == LV_RESULT_OK) return LV_RESULT_OK;
/*If it's a JPEG file...*/
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
const char * fn = dsc->src;
uint32_t t = lv_tick_get();
lv_draw_buf_t * decoded = decode_jpeg_file(fn);
if(decoded == NULL) {
LV_LOG_WARN("decode jpeg file failed");
return LV_RESULT_INVALID;
}
t = lv_tick_elaps(t);
lv_cache_lock();
lv_cache_entry_t * cache = lv_cache_add(decoded, decoded->data_size, decoder->cache_data_type,
decoded->data_size);
if(cache == NULL) {
lv_cache_unlock();
dsc->decoded = decoded;
#if LV_CACHE_DEF_SIZE > 0
lv_image_cache_data_t search_key;
search_key.src_type = dsc->src_type;
search_key.src = dsc->src;
search_key.slot.size = decoded->data_size;
lv_cache_entry_t * entry = lv_image_decoder_add_to_cache(decoder, &search_key, decoded, NULL);
if(entry == NULL) {
lv_draw_buf_destroy(decoded);
return LV_RESULT_INVALID;
}
cache->weight = t;
cache->invalidate_cb = cache_invalidate_cb;
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
cache->src = lv_strdup(dsc->src);
cache->src_type = LV_CACHE_SRC_TYPE_PATH;
}
else {
cache->src_type = LV_CACHE_SRC_TYPE_POINTER;
cache->src = dsc->src;
}
dsc->decoded = lv_cache_get_data(cache);
dsc->cache_entry = cache;
lv_cache_unlock();
dsc->cache_entry = entry;
#endif
return LV_RESULT_OK; /*If not returned earlier then it failed*/
}
@@ -203,28 +188,12 @@ static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder); /*Unused*/
lv_cache_lock();
lv_cache_release(dsc->cache_entry);
lv_cache_unlock();
}
static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc)
{
lv_cache_lock();
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
const char * fn = dsc->src;
lv_cache_entry_t * cache = lv_cache_find_by_src(NULL, fn, LV_CACHE_SRC_TYPE_PATH);
if(cache) {
dsc->decoded = lv_cache_get_data(cache);
dsc->cache_entry = cache; /*Save the cache to release it in decoder_close*/
lv_cache_unlock();
return LV_RESULT_OK;
}
}
lv_cache_unlock();
return LV_RESULT_INVALID;
#if LV_CACHE_DEF_SIZE > 0
lv_cache_release(dsc->cache, dsc->cache_entry, NULL);
#else
lv_draw_buf_destroy((lv_draw_buf_t *)dsc->decoded);
#endif
}
static uint8_t * alloc_file(const char * filename, uint32_t * size)
@@ -482,10 +451,12 @@ static void error_exit(j_common_ptr cinfo)
longjmp(myerr->jb, 1);
}
static void cache_invalidate_cb(lv_cache_entry_t * entry)
static void jpeg_decoder_cache_free_cb(lv_image_cache_data_t * cached_data, void * user_data)
{
if(entry->src_type == LV_CACHE_SRC_TYPE_PATH) lv_free((void *)entry->src);
lv_draw_buf_destroy((lv_draw_buf_t *)entry->data);
LV_UNUSED(user_data);
if(cached_data->src_type == LV_IMAGE_SRC_FILE) lv_free((void *)cached_data->src);
lv_draw_buf_destroy((lv_draw_buf_t *)cached_data->decoded);
}
#endif /*LV_USE_LIBJPEG_TURBO*/

View File

@@ -28,9 +28,8 @@ static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d
const lv_image_decoder_args_t * args);
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
static lv_draw_buf_t * decode_png_file(const char * filename);
static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc);
static void cache_invalidate_cb(lv_cache_entry_t * entry);
static void png_decoder_cache_free_cb(lv_image_cache_data_t * cached_data, void * user_data);
/**********************
* STATIC VARIABLES
**********************/
@@ -52,7 +51,7 @@ void lv_libpng_init(void)
lv_image_decoder_set_info_cb(dec, decoder_info);
lv_image_decoder_set_open_cb(dec, decoder_open);
lv_image_decoder_set_close_cb(dec, decoder_close);
dec->cache_data_type = lv_cache_register_data_type();
lv_image_decoder_set_cache_free_cb(dec, (lv_cache_free_cb_t)png_decoder_cache_free_cb);
}
void lv_libpng_deinit(void)
@@ -128,13 +127,9 @@ static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d
LV_UNUSED(decoder); /*Unused*/
LV_UNUSED(args); /*Unused*/
/*Check the cache first*/
if(try_cache(dsc) == LV_RESULT_OK) return LV_RESULT_OK;
/*If it's a PNG file...*/
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
const char * fn = dsc->src;
uint32_t t = lv_tick_get();
lv_draw_buf_t * decoded = decode_png_file(fn);
if(decoded == NULL) {
return LV_RESULT_INVALID;
@@ -156,31 +151,22 @@ static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d
}
}
t = lv_tick_elaps(t);
dsc->decoded = decoded;
lv_cache_lock();
lv_cache_entry_t * cache = lv_cache_add(decoded, 0, decoder->cache_data_type, decoded->data_size);
if(cache == NULL) {
lv_cache_unlock();
#if LV_CACHE_DEF_SIZE > 0
lv_image_cache_data_t search_key;
search_key.src_type = dsc->src_type;
search_key.src = dsc->src;
search_key.slot.size = decoded->data_size;
lv_cache_entry_t * entry = lv_image_decoder_add_to_cache(decoder, &search_key, decoded, NULL);
if(entry == NULL) {
lv_draw_buf_destroy(decoded);
return LV_RESULT_INVALID;
}
cache->weight = t;
cache->invalidate_cb = cache_invalidate_cb;
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
cache->src = lv_strdup(dsc->src);
cache->src_type = LV_CACHE_SRC_TYPE_PATH;
}
else {
cache->src_type = LV_CACHE_SRC_TYPE_POINTER;
cache->src = dsc->src;
}
dsc->decoded = lv_cache_get_data(cache);
dsc->cache_entry = cache;
lv_cache_unlock();
dsc->cache_entry = entry;
#endif
return LV_RESULT_OK; /*The image is fully decoded. Return with its pointer*/
}
@@ -194,28 +180,11 @@ static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t *
{
LV_UNUSED(decoder); /*Unused*/
lv_cache_lock();
lv_cache_release(dsc->cache_entry);
lv_cache_unlock();
}
static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc)
{
lv_cache_lock();
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
const char * fn = dsc->src;
lv_cache_entry_t * cache = lv_cache_find_by_src(NULL, fn, LV_CACHE_SRC_TYPE_PATH);
if(cache) {
dsc->decoded = lv_cache_get_data(cache);
dsc->cache_entry = cache; /*Save the cache to release it in decoder_close*/
lv_cache_unlock();
return LV_RESULT_OK;
}
}
lv_cache_unlock();
return LV_RESULT_INVALID;
#if LV_CACHE_DEF_SIZE > 0
lv_cache_release(dsc->cache, dsc->cache_entry, NULL);
#else
lv_draw_buf_destroy((lv_draw_buf_t *)dsc->decoded);
#endif
}
static uint8_t * alloc_file(const char * filename, uint32_t * size)
@@ -323,10 +292,12 @@ static lv_draw_buf_t * decode_png_file(const char * filename)
return decoded;
}
static void cache_invalidate_cb(lv_cache_entry_t * entry)
static void png_decoder_cache_free_cb(lv_image_cache_data_t * cached_data, void * user_data)
{
lv_free((void *)entry->src);
lv_draw_buf_destroy((lv_draw_buf_t *)entry->data);
LV_UNUSED(user_data);
if(cached_data->src_type == LV_IMAGE_SRC_FILE) lv_free((void *)cached_data->src);
lv_draw_buf_destroy((lv_draw_buf_t *)cached_data->decoded);
}
#endif /*LV_USE_LIBPNG*/

View File

@@ -30,9 +30,7 @@ static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d
static void decoder_close(lv_image_decoder_t * dec, lv_image_decoder_dsc_t * dsc);
static void convert_color_depth(uint8_t * img_p, uint32_t px_cnt);
static lv_draw_buf_t * decode_png_data(const void * png_data, size_t png_data_size);
static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc);
static void cache_invalidate_cb(lv_cache_entry_t * entry);
static void lodepng_decoder_cache_free_cb(lv_image_cache_data_t * cached_data, void * user_data);
/**********************
* STATIC VARIABLES
**********************/
@@ -54,7 +52,7 @@ void lv_lodepng_init(void)
lv_image_decoder_set_info_cb(dec, decoder_info);
lv_image_decoder_set_open_cb(dec, decoder_open);
lv_image_decoder_set_close_cb(dec, decoder_close);
dec->cache_data_type = lv_cache_register_data_type();
lv_image_decoder_set_cache_free_cb(dec, (lv_cache_free_cb_t)lodepng_decoder_cache_free_cb);
}
void lv_lodepng_deinit(void)
@@ -158,9 +156,6 @@ static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d
LV_UNUSED(decoder);
LV_UNUSED(args);
/*Check the cache first*/
if(try_cache(dsc) == LV_RESULT_OK) return LV_RESULT_OK;
const uint8_t * png_data = NULL;
size_t png_data_size = 0;
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
@@ -186,14 +181,6 @@ static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d
return LV_RESULT_INVALID;
}
lv_cache_lock();
lv_cache_entry_t * cache = lv_cache_add(NULL, 0, decoder->cache_data_type, dsc->header.w * dsc->header.h * 4);
if(cache == NULL) {
lv_cache_unlock();
return LV_RESULT_INVALID;
}
uint32_t t = lv_tick_get();
lv_draw_buf_t * decoded = decode_png_data(png_data, png_data_size);
/*Stride check and adjustment accordingly*/
if(args && args->stride_align) {
@@ -211,24 +198,26 @@ static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d
}
}
t = lv_tick_elaps(t);
cache->weight = t;
cache->data = decoded;
cache->invalidate_cb = cache_invalidate_cb;
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
cache->src = lv_strdup(dsc->src);
cache->src_type = LV_CACHE_SRC_TYPE_PATH;
lv_free((void *)png_data);
}
else {
cache->src_type = LV_CACHE_SRC_TYPE_POINTER;
cache->src = dsc->src;
dsc->decoded = decoded;
#if LV_CACHE_DEF_SIZE > 0
lv_image_cache_data_t search_key;
search_key.src_type = dsc->src_type;
search_key.src = dsc->src;
search_key.slot.size = decoded->data_size;
lv_cache_entry_t * entry = lv_image_decoder_add_to_cache(decoder, &search_key, decoded, NULL);
if(entry == NULL) {
return LV_RESULT_INVALID;
}
dsc->cache_entry = entry;
#endif
dsc->decoded = lv_cache_get_data(cache);
dsc->cache_entry = cache;
lv_cache_unlock();
return LV_RESULT_OK; /*If not returned earlier then it failed*/
}
@@ -242,40 +231,11 @@ static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t *
{
LV_UNUSED(decoder);
lv_cache_lock();
lv_cache_release(dsc->cache_entry);
lv_cache_unlock();
}
static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc)
{
lv_cache_lock();
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
const char * fn = dsc->src;
lv_cache_entry_t * cache = lv_cache_find_by_src(NULL, fn, LV_CACHE_SRC_TYPE_PATH);
if(cache) {
dsc->decoded = lv_cache_get_data(cache);
dsc->cache_entry = cache; /*Save the cache to release it in decoder_close*/
lv_cache_unlock();
return LV_RESULT_OK;
}
}
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
const lv_image_dsc_t * img_dsc = dsc->src;
lv_cache_entry_t * cache = lv_cache_find_by_src(NULL, img_dsc, LV_CACHE_SRC_TYPE_POINTER);
if(cache) {
dsc->decoded = lv_cache_get_data(cache);
dsc->cache_entry = cache; /*Save the cache to release it in decoder_close*/
lv_cache_unlock();
return LV_RESULT_OK;
}
}
lv_cache_unlock();
return LV_RESULT_INVALID;
#if LV_CACHE_DEF_SIZE > 0
lv_cache_release(dsc->cache, dsc->cache_entry, NULL);
#else
lv_draw_buf_destroy((lv_draw_buf_t *)dsc->decoded);
#endif
}
static lv_draw_buf_t * decode_png_data(const void * png_data, size_t png_data_size)
@@ -314,10 +274,12 @@ static void convert_color_depth(uint8_t * img_p, uint32_t px_cnt)
}
}
static void cache_invalidate_cb(lv_cache_entry_t * entry)
static void lodepng_decoder_cache_free_cb(lv_image_cache_data_t * cached_data, void * user_data)
{
if(entry->src_type == LV_CACHE_SRC_TYPE_PATH) lv_free((void *)entry->src);
lv_draw_buf_destroy((lv_draw_buf_t *)entry->data);
LV_UNUSED(user_data);
if(cached_data->src_type == LV_IMAGE_SRC_FILE) lv_free((void *)cached_data->src);
lv_draw_buf_destroy((lv_draw_buf_t *)cached_data->decoded);
}
#endif /*LV_USE_LODEPNG*/

View File

@@ -221,7 +221,9 @@ static void lv_qrcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
lv_draw_buf_t * draw_buf = lv_canvas_get_draw_buf(obj);
if(draw_buf == NULL) return;
lv_image_cache_drop(draw_buf);
/*@fixme destroy buffer in cache free_cb.*/
lv_draw_buf_destroy(draw_buf);
}

View File

@@ -174,9 +174,7 @@ static void lv_rlottie_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj
rlottie->dest_frame = 0;
}
lv_cache_lock();
lv_cache_invalidate_by_src(&rlottie->imgdsc, LV_CACHE_SRC_TYPE_POINTER);
lv_cache_unlock();
lv_image_cache_drop(&rlottie->imgdsc);
if(rlottie->allocated_buf) {
lv_free(rlottie->allocated_buf);

View File

@@ -1,6 +1,8 @@
#include "lv_tiny_ttf.h"
#if LV_USE_TINY_TTF
#include <stdio.h>
#include "../../core/lv_global.h"
#include "lv_tiny_ttf.h"
#define STB_RECT_PACK_IMPLEMENTATION
#define STBRP_STATIC
@@ -12,6 +14,8 @@
#define STBTT_malloc(x, u) ((void)(u), lv_malloc(x))
#define STBTT_free(x, u) ((void)(u), lv_free(x))
#define tiny_ttf_cache LV_GLOBAL_DEFAULT()->tiny_ttf_cache
#if LV_TINY_TTF_FILE_SUPPORT != 0
/* a hydra stream that can be in memory or from a file*/
typedef struct ttf_cb_stream {
@@ -72,9 +76,19 @@ typedef struct ttf_font_desc {
int descent;
} ttf_font_desc_t;
typedef struct ttf_cache_entry {
typedef struct _tiny_ttf_cache_data_t {
lv_font_t * font;
uint32_t unicode;
uint32_t size;
uint8_t * buffer;
} ttf_cache_entry_t;
uint32_t buffer_size;
} tiny_ttf_cache_data_t;
static bool tiny_ttf_cache_create_cb(tiny_ttf_cache_data_t * node, void * user_data);
static void tiny_ttf_cache_free_cb(tiny_ttf_cache_data_t * node, void * user_data);
static lv_cache_compare_res_t tiny_ttf_cache_compare_cb(const tiny_ttf_cache_data_t * lhs,
const tiny_ttf_cache_data_t * rhs);
static bool ttf_get_glyph_dsc_cb(const lv_font_t * font, lv_font_glyph_dsc_t * dsc_out, uint32_t unicode_letter,
uint32_t unicode_letter_next)
@@ -121,73 +135,26 @@ static bool ttf_get_glyph_dsc_cb(const lv_font_t * font, lv_font_glyph_dsc_t * d
return true; /*true: glyph found; false: glyph was not found*/
}
static void cache_invalidate_cb(lv_cache_entry_t * entry)
{
lv_draw_buf_free((void *)entry->data);
}
static const uint8_t * ttf_get_glyph_bitmap_cb(const lv_font_t * font, uint32_t unicode_letter, uint8_t * bitmap_buf)
{
LV_UNUSED(bitmap_buf);
ttf_font_desc_t * dsc = (ttf_font_desc_t *)font->dsc;
const stbtt_fontinfo * info = (const stbtt_fontinfo *)&dsc->info;
int g1 = stbtt_FindGlyphIndex(info, (int)unicode_letter);
if(g1 == 0) {
/* Glyph not found */
return NULL;
}
int x1, y1, x2, y2;
stbtt_GetGlyphBitmapBox(info, g1, dsc->scale, dsc->scale, &x1, &y1, &x2, &y2);
int w, h;
w = x2 - x1 + 1;
h = y2 - y1 + 1;
uint32_t stride = lv_draw_buf_width_to_stride(w, LV_COLOR_FORMAT_A8);
lv_cache_lock();
uint32_t cp = unicode_letter;
lv_cache_entry_t * cache = lv_cache_find_by_src(NULL, font, LV_CACHE_SRC_TYPE_POINTER);
while(cache) {
if(cache->param1 == (int32_t)font->line_height && cache->param2 == (int32_t)cp) break;
cache = lv_cache_find_by_src(cache, font, LV_CACHE_SRC_TYPE_POINTER);
}
if(cache) {
uint8_t * buffer = (uint8_t *)lv_cache_get_data(cache);
lv_cache_release(cache);
lv_cache_unlock();
return buffer;
}
size_t szb = h * stride;
tiny_ttf_cache_data_t search_key = {
.font = (lv_font_t *)font,
.unicode = unicode_letter,
.size = font->line_height,
};
uint8_t * buffer = lv_draw_buf_malloc(szb, LV_COLOR_FORMAT_A8);
if(NULL == buffer) {
LV_LOG_ERROR("tiny_ttf: out of memory\n");
lv_cache_unlock();
return NULL;
}
lv_cache_entry_t * entry = lv_cache_acquire_or_create(tiny_ttf_cache, &search_key, (void *)font->dsc);
lv_cache_entry_t * entry = lv_cache_add(buffer, 0, LV_CACHE_DATA_TYPE_NOT_SET, szb);
if(entry == NULL) {
lv_cache_unlock();
lv_draw_buf_free(buffer);
LV_LOG_ERROR("tiny_ttf: cache not allocated\n");
LV_LOG_ERROR("cache not allocated\n");
return NULL;
}
/* This smells. We add the codepoint to the base pointer to get a key. */
entry->src = font;
entry->src_type = LV_CACHE_SRC_TYPE_POINTER;
entry->param1 = font->line_height;
entry->param2 = cp;
entry->invalidate_cb = cache_invalidate_cb;
/*Just to increment life*/
lv_cache_get_data(entry);
memset(buffer, 0, szb);
stbtt_MakeGlyphBitmap(info, bitmap_buf, w, h, stride, dsc->scale, dsc->scale, g1);
lv_memcpy(buffer, bitmap_buf, stride * h);
lv_cache_release(entry);
lv_cache_unlock();
return bitmap_buf; /*Or NULL if not found*/
tiny_ttf_cache_data_t * cached_data = lv_cache_entry_get_data(entry);
lv_cache_release(tiny_ttf_cache, entry, NULL);
return cached_data->buffer;
}
static lv_result_t lv_tiny_ttf_create(lv_font_t * out_font, const char * path, const void * data, size_t data_size,
@@ -282,9 +249,85 @@ void lv_tiny_ttf_destroy(lv_font_t * font)
lv_fs_close(&ttf->file);
}
#endif
lv_cache_drop_all(tiny_ttf_cache, (void *)font->dsc);
lv_free(ttf);
font->dsc = NULL;
}
}
}
void lv_tiny_ttf_init(void)
{
lv_cache_ops_t ops = {
.compare_cb = (lv_cache_compare_cb_t)tiny_ttf_cache_compare_cb,
.create_cb = (lv_cache_create_cb_t)tiny_ttf_cache_create_cb,
.free_cb = (lv_cache_free_cb_t)tiny_ttf_cache_free_cb,
};
tiny_ttf_cache = lv_cache_create(&lv_cache_class_lru_rb_count, sizeof(tiny_ttf_cache_data_t), 128, ops);
}
void lv_tiny_ttf_deinit(void)
{
lv_cache_destroy(tiny_ttf_cache, NULL);
}
static bool tiny_ttf_cache_create_cb(tiny_ttf_cache_data_t * node, void * user_data)
{
ttf_font_desc_t * dsc = (ttf_font_desc_t *)user_data;
uint32_t unicode_letter = node->unicode;
const stbtt_fontinfo * info = (const stbtt_fontinfo *)&dsc->info;
int g1 = stbtt_FindGlyphIndex(info, (int)unicode_letter);
if(g1 == 0) {
/* Glyph not found */
return false;
}
int x1, y1, x2, y2;
stbtt_GetGlyphBitmapBox(info, g1, dsc->scale, dsc->scale, &x1, &y1, &x2, &y2);
int w, h;
w = x2 - x1 + 1;
h = y2 - y1 + 1;
uint32_t stride = lv_draw_buf_width_to_stride(w, LV_COLOR_FORMAT_A8);
size_t szb = h * stride;
uint8_t * buffer = lv_draw_buf_malloc(szb, LV_COLOR_FORMAT_A8);
if(NULL == buffer) {
LV_LOG_ERROR("tiny_ttf: out of memory\n");
return false;
}
memset(buffer, 0, szb);
stbtt_MakeGlyphBitmap(info, buffer, w, h, stride, dsc->scale, dsc->scale, g1);
node->buffer = buffer;
node->buffer_size = szb;
return true;
}
static void tiny_ttf_cache_free_cb(tiny_ttf_cache_data_t * node, void * user_data)
{
LV_UNUSED(user_data);
lv_draw_buf_free(node->buffer);
}
static lv_cache_compare_res_t tiny_ttf_cache_compare_cb(const tiny_ttf_cache_data_t * lhs,
const tiny_ttf_cache_data_t * rhs)
{
if(lhs->font != rhs->font) {
return lhs->font > rhs->font ? 1 : -1;
}
if(lhs->unicode != rhs->unicode) {
return lhs->unicode > rhs->unicode ? 1 : -1;
}
if(lhs->size != rhs->size) {
return lhs->size > rhs->size ? 1 : -1;
}
return 0;
}
#endif

View File

@@ -37,6 +37,10 @@ lv_result_t lv_tiny_ttf_create_file(lv_font_t * font, const char * path, int32_t
lv_result_t lv_tiny_ttf_create_file_ex(lv_font_t * font, const char * path, int32_t font_size, size_t cache_size);
#endif
void lv_tiny_ttf_init(void);
void lv_tiny_ttf_deinit(void);
/* create a font from the specified data pointer with the specified line height.*/
lv_result_t lv_tiny_ttf_create_data(lv_font_t * font, const void * data, size_t data_size, int32_t font_size);

View File

@@ -767,8 +767,8 @@
/*Default cache size in bytes.
*Used by image decoders such as `lv_lodepng` to keep the decoded image in the memory.
*Data larger than the size of the cache also can be allocated but
*will be dropped immediately after usage.*/
*If size is not set to 0, the decoder will fail to decode when the cache is full.
*If size is 0, the cache function is not enabled and the decoded mem will be released immediately after use.*/
#ifndef LV_CACHE_DEF_SIZE
#ifdef CONFIG_LV_CACHE_DEF_SIZE
#define LV_CACHE_DEF_SIZE CONFIG_LV_CACHE_DEF_SIZE

View File

@@ -22,8 +22,6 @@
#include "libs/lodepng/lv_lodepng.h"
#include "libs/libpng/lv_libpng.h"
#include "draw/lv_draw.h"
#include "misc/lv_cache.h"
#include "misc/lv_cache_builtin.h"
#include "misc/lv_async.h"
#include "misc/lv_fs.h"
#if LV_USE_DRAW_VGLITE
@@ -199,12 +197,6 @@ void lv_init(void)
lv_draw_vg_lite_init();
#endif
_lv_cache_init();
_lv_cache_builtin_init();
lv_cache_lock();
lv_cache_set_max_size(LV_CACHE_DEF_SIZE);
lv_cache_unlock();
/*Test if the IDE has UTF-8 encoding*/
const char * txt = "Á";
@@ -297,6 +289,10 @@ void lv_init(void)
# endif
#endif
#if LV_USE_TINY_TTF
lv_tiny_ttf_init();
#endif
lv_initialized = true;
LV_LOG_TRACE("finished");
@@ -334,6 +330,10 @@ void lv_deinit(void)
lv_freetype_uninit();
#endif
#if LV_USE_TINY_TTF
lv_tiny_ttf_deinit();
#endif
#if LV_USE_THEME_DEFAULT
lv_theme_default_deinit();
#endif
@@ -346,10 +346,6 @@ void lv_deinit(void)
lv_theme_mono_deinit();
#endif
_lv_cache_builtin_deinit();
_lv_cache_deinit();
_lv_image_decoder_deinit();
_lv_refr_deinit();

404
src/misc/cache/_lv_cache_lru_rb.c vendored Normal file
View File

@@ -0,0 +1,404 @@
/**
* @file _lv_cache_lru_rb.c
*
*/
/*********************
* INCLUDES
*********************/
#include "_lv_cache_lru_rb.h"
#include "../../stdlib/lv_sprintf.h"
#include "../../stdlib/lv_string.h"
#include "../lv_ll.h"
#include "../lv_rb.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef uint32_t (get_data_size_cb_t)(const void * data);
struct _lv_lru_rb_t {
lv_cache_t cache;
lv_rb_t rb;
lv_ll_t ll;
get_data_size_cb_t * get_data_size_cb;
};
typedef struct _lv_lru_rb_t lv_lru_rb_t_;
/**********************
* STATIC PROTOTYPES
**********************/
static void * alloc_cb(void);
static bool init_cnt_cb(lv_cache_t * cache);
static bool init_size_cb(lv_cache_t * cache);
static void destroy_cb(lv_cache_t * cache, void * user_data);
static lv_cache_entry_t * get_cb(lv_cache_t * cache, const void * key, void * user_data);
static lv_cache_entry_t * add_cb(lv_cache_t * cache, const void * key, void * user_data);
static void remove_cb(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data);
static void drop_cb(lv_cache_t * cache, const void * key, void * user_data);
static void drop_all_cb(lv_cache_t * cache, void * user_data);
static void * alloc_new_node(lv_lru_rb_t_ * lru, void * key, void * user_data);
inline static void ** get_lru_node(lv_lru_rb_t_ * lru, lv_rb_node_t * node);
static uint32_t cnt_get_data_size_cb(const void * data);
static uint32_t size_get_data_size_cb(const void * data);
/**********************
* GLOBAL VARIABLES
**********************/
const lv_cache_class_t lv_cache_class_lru_rb_count = {
.alloc_cb = alloc_cb,
.init_cb = init_cnt_cb,
.destroy_cb = destroy_cb,
.get_cb = get_cb,
.add_cb = add_cb,
.remove_cb = remove_cb,
.drop_cb = drop_cb,
.drop_all_cb = drop_all_cb
};
const lv_cache_class_t lv_cache_class_lru_rb_size = {
.alloc_cb = alloc_cb,
.init_cb = init_size_cb,
.destroy_cb = destroy_cb,
.get_cb = get_cb,
.add_cb = add_cb,
.remove_cb = remove_cb,
.drop_cb = drop_cb,
.drop_all_cb = drop_all_cb
};
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/
static void * alloc_new_node(lv_lru_rb_t_ * lru, void * key, void * user_data)
{
LV_UNUSED(user_data);
LV_ASSERT_NULL(lru);
LV_ASSERT_NULL(key);
if(lru == NULL || key == NULL) {
return NULL;
}
lv_rb_node_t * node = lv_rb_insert(&lru->rb, key);
if(node == NULL)
goto FAILED_HANDLER2;
void * data = node->data;
lv_cache_entry_t * entry = lv_cache_entry_get_entry(data, lru->cache.node_size);
lv_memcpy(data, key, lru->cache.node_size);
void * lru_node = _lv_ll_ins_head(&lru->ll);
if(lru_node == NULL)
goto FAILED_HANDLER1;
lv_memcpy(lru_node, &node, sizeof(void *));
lv_memcpy(get_lru_node(lru, node), &lru_node, sizeof(void *));
lv_cache_entry_init(entry, &lru->cache, lru->cache.node_size);
goto FAILED_HANDLER2;
FAILED_HANDLER1:
lv_rb_drop_node(&lru->rb, node);
node = NULL;
FAILED_HANDLER2:
return node;
}
inline static void ** get_lru_node(lv_lru_rb_t_ * lru, lv_rb_node_t * node)
{
return (void **)((char *)node->data + lru->rb.size - sizeof(void *));
}
static void * alloc_cb(void)
{
void * res = lv_malloc(sizeof(lv_lru_rb_t_));
LV_ASSERT_MALLOC(res);
if(res == NULL) {
LV_LOG_ERROR("malloc failed");
return NULL;
}
lv_memzero(res, sizeof(lv_lru_rb_t_));
return res;
}
static bool init_cnt_cb(lv_cache_t * cache)
{
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
LV_ASSERT_NULL(lru->cache.ops.compare_cb);
LV_ASSERT_NULL(lru->cache.ops.free_cb);
LV_ASSERT(lru->cache.node_size > 0);
if(lru->cache.node_size <= 0 || lru->cache.max_size <= 0
|| lru->cache.ops.compare_cb == NULL || lru->cache.ops.free_cb == NULL) {
return false;
}
/*add void* to store the ll node pointer*/
if(!lv_rb_init(&lru->rb, lru->cache.ops.compare_cb, lv_cache_entry_get_size(lru->cache.node_size) + sizeof(void *))) {
return NULL;
}
_lv_ll_init(&lru->ll, sizeof(void *));
lru->get_data_size_cb = cnt_get_data_size_cb;
return lru;
}
static bool init_size_cb(lv_cache_t * cache)
{
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
LV_ASSERT_NULL(lru->cache.ops.compare_cb);
LV_ASSERT_NULL(lru->cache.ops.free_cb);
LV_ASSERT(lru->cache.node_size > 0);
if(lru->cache.node_size <= 0 || lru->cache.max_size <= 0
|| lru->cache.ops.compare_cb == NULL || lru->cache.ops.free_cb == NULL) {
return false;
}
/*add void* to store the ll node pointer*/
if(!lv_rb_init(&lru->rb, lru->cache.ops.compare_cb, lv_cache_entry_get_size(lru->cache.node_size) + sizeof(void *))) {
return NULL;
}
_lv_ll_init(&lru->ll, sizeof(void *));
lru->get_data_size_cb = size_get_data_size_cb;
return lru;
}
static void destroy_cb(lv_cache_t * cache, void * user_data)
{
LV_UNUSED(user_data);
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
LV_ASSERT_NULL(lru);
if(lru == NULL) {
return;
}
cache->clz->drop_all_cb(cache, user_data);
}
static lv_cache_entry_t * get_cb(lv_cache_t * cache, const void * key, void * user_data)
{
LV_UNUSED(user_data);
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
LV_ASSERT_NULL(lru);
LV_ASSERT_NULL(key);
if(lru == NULL || key == NULL) {
return NULL;
}
/*try the first ll node first*/
void * head = _lv_ll_get_head(&lru->ll);
if(head) {
lv_rb_node_t * node = *(lv_rb_node_t **)head;
void * data = node->data;
lv_cache_entry_t * entry = lv_cache_entry_get_entry(data, cache->node_size);
if(lru->cache.ops.compare_cb(data, key) == 0) {
return entry;
}
}
lv_rb_node_t * node = lv_rb_find(&lru->rb, key);
/*cache hit*/
if(node) {
void * lru_node = *get_lru_node(lru, node);
head = _lv_ll_get_head(&lru->ll);
_lv_ll_move_before(&lru->ll, lru_node, head);
lv_cache_entry_t * entry = lv_cache_entry_get_entry(node->data, cache->node_size);
return entry;
}
return NULL;
}
static lv_cache_entry_t * add_cb(lv_cache_t * cache, const void * key, void * user_data)
{
LV_UNUSED(user_data);
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
LV_ASSERT_NULL(lru);
LV_ASSERT_NULL(key);
if(lru == NULL || key == NULL) {
return NULL;
}
uint32_t data_size = lru->get_data_size_cb(key);
if(data_size > lru->cache.max_size) {
LV_LOG_ERROR("data size (%" LV_PRIu32 ") is larger than max size (%" LV_PRIu32 ")", data_size,
(uint32_t)lru->cache.max_size);
return NULL;
}
void * tail = _lv_ll_get_tail(&lru->ll);
void * curr = tail;
while(cache->size + data_size > lru->cache.max_size) {
if(curr == NULL) {
LV_LOG_ERROR("failed to drop cache");
return NULL;
}
lv_rb_node_t * tail_node = *(lv_rb_node_t **)curr;
void * search_key = tail_node->data;
lv_cache_entry_t * entry = lv_cache_entry_get_entry(search_key, cache->node_size);
if(lv_cache_entry_get_ref(entry) == 0) {
cache->clz->drop_cb(cache, search_key, user_data);
curr = _lv_ll_get_tail(&lru->ll);
continue;
}
curr = _lv_ll_get_prev(&lru->ll, curr);
}
/*cache miss*/
lv_rb_node_t * new_node = alloc_new_node(lru, (void *)key, user_data);
if(new_node == NULL) {
return NULL;
}
lv_cache_entry_t * entry = lv_cache_entry_get_entry(new_node->data, cache->node_size);
cache->size += data_size;
return entry;
}
static void remove_cb(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data)
{
LV_UNUSED(user_data);
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
LV_ASSERT_NULL(lru);
LV_ASSERT_NULL(entry);
if(lru == NULL || entry == NULL) {
return;
}
void * data = lv_cache_entry_get_data(entry);
lv_rb_node_t * node = lv_rb_find(&lru->rb, data);
if(node == NULL) {
return;
}
void * lru_node = *get_lru_node(lru, node);
lv_rb_remove_node(&lru->rb, node);
_lv_ll_remove(&lru->ll, lru_node);
lv_free(lru_node);
cache->size -= lru->get_data_size_cb(data);
}
static void drop_cb(lv_cache_t * cache, const void * key, void * user_data)
{
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
LV_ASSERT_NULL(lru);
LV_ASSERT_NULL(key);
if(lru == NULL || key == NULL) {
return;
}
lv_rb_node_t * node = lv_rb_find(&lru->rb, key);
if(node == NULL) {
return;
}
void * data = node->data;
lru->cache.ops.free_cb(data, user_data);
cache->size -= lru->get_data_size_cb(data);
lv_cache_entry_t * entry = lv_cache_entry_get_entry(data, cache->node_size);
void * lru_node = *get_lru_node(lru, node);
lv_rb_remove_node(&lru->rb, node);
lv_cache_entry_delete(entry);
_lv_ll_remove(&lru->ll, lru_node);
lv_free(lru_node);
}
static void drop_all_cb(lv_cache_t * cache, void * user_data)
{
lv_lru_rb_t_ * lru = (lv_lru_rb_t_ *)cache;
LV_ASSERT_NULL(lru);
if(lru == NULL) {
return;
}
uint32_t used_cnt = 0;
lv_rb_node_t ** node;
_LV_LL_READ(&lru->ll, node) {
/*free user handled data and do other clean up*/
void * search_key = (*node)->data;
lv_cache_entry_t * entry = lv_cache_entry_get_entry(search_key, cache->node_size);
if(lv_cache_entry_get_ref(entry) == 0) {
lru->cache.ops.free_cb(search_key, user_data);
}
else {
LV_LOG_WARN("entry (%p) is still referenced (%" LV_PRId32 ")", entry, lv_cache_entry_get_ref(entry));
used_cnt++;
}
}
if(used_cnt > 0) {
LV_LOG_WARN("%" LV_PRId32 " entries are still referenced", used_cnt);
}
lv_rb_destroy(&lru->rb);
_lv_ll_clear(&lru->ll);
cache->size = 0;
}
static uint32_t cnt_get_data_size_cb(const void * data)
{
LV_UNUSED(data);
return 1;
}
static uint32_t size_get_data_size_cb(const void * data)
{
lv_cache_slot_size_t * slot = (lv_cache_slot_size_t *)data;
return slot->size;
}

44
src/misc/cache/_lv_cache_lru_rb.h vendored Normal file
View File

@@ -0,0 +1,44 @@
/**
* @file _lv_cache_lru_rb.h
*
*/
#ifndef LV_CACHE_LRU_RB_H
#define LV_CACHE_LRU_RB_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_cache_entry.h"
#include "lv_cache_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/*************************
* GLOBAL VARIABLES
*************************/
LV_ATTRIBUTE_EXTERN_DATA extern const lv_cache_class_t lv_cache_class_lru_rb_count;
LV_ATTRIBUTE_EXTERN_DATA extern const lv_cache_class_t lv_cache_class_lru_rb_size;
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CACHE_LRU_RB_H*/

213
src/misc/cache/lv_cache.c vendored Normal file
View File

@@ -0,0 +1,213 @@
/**
* @file lv_cache.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_cache.h"
#include "../../stdlib/lv_sprintf.h"
#include "../lv_assert.h"
#include "lv_cache_entry_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void cache_drop_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data);
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_cache_t * lv_cache_create(const lv_cache_class_t * cache_class,
size_t node_size, size_t max_size,
lv_cache_ops_t ops)
{
lv_cache_t * cache = cache_class->alloc_cb();
LV_ASSERT_MALLOC(cache);
cache->clz = cache_class;
cache->node_size = node_size;
cache->max_size = max_size;
cache->size = 0;
cache->ops = ops;
cache->clz->init_cb(cache);
lv_mutex_init(&cache->lock);
return cache;
}
void lv_cache_destroy(lv_cache_t * cache, void * user_data)
{
LV_ASSERT_NULL(cache);
lv_mutex_lock(&cache->lock);
cache->clz->destroy_cb(cache, user_data);
lv_mutex_unlock(&cache->lock);
lv_mutex_delete(&cache->lock);
lv_free(cache);
}
lv_cache_entry_t * lv_cache_acquire(lv_cache_t * cache, const void * key, void * user_data)
{
LV_ASSERT_NULL(cache);
LV_ASSERT_NULL(key);
lv_mutex_lock(&cache->lock);
lv_cache_entry_t * entry = cache->clz->get_cb(cache, key, user_data);
if(entry != NULL) {
lv_cache_entry_acquire_data(entry);
}
lv_mutex_unlock(&cache->lock);
return entry;
}
void lv_cache_release(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data)
{
LV_ASSERT_NULL(entry);
lv_mutex_lock(&cache->lock);
lv_cache_entry_release_data(entry, user_data);
if(lv_cache_entry_get_ref(entry) == 0 && lv_cache_entry_is_invalid(entry)) {
cache->ops.free_cb(lv_cache_entry_get_data(entry), user_data);
lv_cache_entry_delete(entry);
}
lv_mutex_unlock(&cache->lock);
}
lv_cache_entry_t * lv_cache_add(lv_cache_t * cache, const void * key, void * user_data)
{
LV_ASSERT_NULL(cache);
LV_ASSERT_NULL(key);
lv_mutex_lock(&cache->lock);
lv_cache_entry_t * entry = cache->clz->add_cb(cache, key, user_data);
if(entry != NULL) {
lv_cache_entry_acquire_data(entry);
}
lv_mutex_unlock(&cache->lock);
return entry;
}
lv_cache_entry_t * lv_cache_acquire_or_create(lv_cache_t * cache, const void * key, void * user_data)
{
LV_ASSERT_NULL(cache);
LV_ASSERT_NULL(key);
lv_mutex_lock(&cache->lock);
lv_cache_entry_t * entry = cache->clz->get_cb(cache, key, user_data);
if(entry != NULL) {
lv_cache_entry_acquire_data(entry);
lv_mutex_unlock(&cache->lock);
return entry;
}
entry = cache->clz->add_cb(cache, key, user_data);
if(entry == NULL) {
lv_mutex_unlock(&cache->lock);
return NULL;
}
bool create_res = cache->ops.create_cb(lv_cache_entry_get_data(entry), user_data);
if(create_res == false) {
cache->clz->remove_cb(cache, entry, user_data);
lv_cache_entry_delete(entry);
entry = NULL;
}
else {
lv_cache_entry_acquire_data(entry);
}
lv_mutex_unlock(&cache->lock);
return entry;
}
void lv_cache_drop(lv_cache_t * cache, const void * key, void * user_data)
{
LV_ASSERT_NULL(cache);
LV_ASSERT_NULL(key);
lv_mutex_lock(&cache->lock);
cache_drop_internal_no_lock(cache, key, user_data);
lv_mutex_unlock(&cache->lock);
}
void lv_cache_drop_all(lv_cache_t * cache, void * user_data)
{
LV_ASSERT_NULL(cache);
lv_mutex_lock(&cache->lock);
cache->clz->drop_all_cb(cache, user_data);
lv_mutex_unlock(&cache->lock);
}
void lv_cache_set_max_size(lv_cache_t * cache, size_t max_size, void * user_data)
{
LV_UNUSED(user_data);
cache->max_size = max_size;
}
size_t lv_cache_get_max_size(lv_cache_t * cache, void * user_data)
{
LV_UNUSED(user_data);
return cache->max_size;
}
size_t lv_cache_get_size(lv_cache_t * cache, void * user_data)
{
LV_UNUSED(user_data);
return cache->size;
}
size_t lv_cache_get_free_size(lv_cache_t * cache, void * user_data)
{
LV_UNUSED(user_data);
return cache->max_size - cache->size;
}
void lv_cache_set_compare_cb(lv_cache_t * cache, lv_cache_compare_cb_t compare_cb, void * user_data)
{
LV_UNUSED(user_data);
cache->ops.compare_cb = compare_cb;
}
void lv_cache_set_create_cb(lv_cache_t * cache, lv_cache_create_cb_t alloc_cb, void * user_data)
{
LV_UNUSED(user_data);
cache->ops.create_cb = alloc_cb;
}
void lv_cache_set_free_cb(lv_cache_t * cache, lv_cache_free_cb_t free_cb, void * user_data)
{
LV_UNUSED(user_data);
cache->ops.free_cb = free_cb;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void cache_drop_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data)
{
lv_cache_entry_t * entry = cache->clz->get_cb(cache, key, user_data);
if(entry == NULL) {
return;
}
if(lv_cache_entry_get_ref(entry) == 0) {
cache->clz->remove_cb(cache, entry, user_data);
cache->ops.free_cb(lv_cache_entry_get_data(entry), user_data);
lv_cache_entry_delete(entry);
}
else {
lv_cache_entry_set_invalid(entry, true);
cache->clz->remove_cb(cache, entry, user_data);
}
}

67
src/misc/cache/lv_cache.h vendored Normal file
View File

@@ -0,0 +1,67 @@
/**
* @file lv_cache.h
*
*/
#ifndef LV_CACHE1_H
#define LV_CACHE1_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_cache_entry.h"
#include "lv_cache_private.h"
#include <stdbool.h>
#include <stdlib.h>
#include "_lv_cache_lru_rb.h"
#include "lv_image_cache.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_cache_t * lv_cache_create(const lv_cache_class_t * cache_class,
size_t node_size, size_t max_size,
lv_cache_ops_t ops);
void lv_cache_destroy(lv_cache_t * cache, void * user_data);
lv_cache_entry_t * lv_cache_acquire(lv_cache_t * cache, const void * key, void * user_data);
lv_cache_entry_t * lv_cache_acquire_or_create(lv_cache_t * cache, const void * key, void * user_data);
lv_cache_entry_t * lv_cache_add(lv_cache_t * cache, const void * key, void * user_data);
void lv_cache_release(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data);
void lv_cache_drop(lv_cache_t * cache, const void * key, void * user_data);
void lv_cache_drop_all(lv_cache_t * cache, void * user_data);
void lv_cache_set_max_size(lv_cache_t * cache, size_t max_size, void * user_data);
size_t lv_cache_get_max_size(lv_cache_t * cache, void * user_data);
size_t lv_cache_get_size(lv_cache_t * cache, void * user_data);
size_t lv_cache_get_free_size(lv_cache_t * cache, void * user_data);
void lv_cache_set_compare_cb(lv_cache_t * cache, lv_cache_compare_cb_t compare_cb, void * user_data);
void lv_cache_set_create_cb(lv_cache_t * cache, lv_cache_create_cb_t alloc_cb, void * user_data);
void lv_cache_set_free_cb(lv_cache_t * cache, lv_cache_free_cb_t free_cb, void * user_data);
/*************************
* GLOBAL VARIABLES
*************************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CACHE_H*/

167
src/misc/cache/lv_cache_entry.c vendored Normal file
View File

@@ -0,0 +1,167 @@
/**
* @file lv_cache_entry.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_cache_entry.h"
#include "../../stdlib/lv_sprintf.h"
#include "../lv_assert.h"
#include "lv_cache.h"
#include "lv_cache_entry_private.h"
#include "lv_cache_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_cache_entry_t {
const lv_cache_t * cache;
int32_t ref_cnt;
uint32_t node_size;
bool is_invalid;
};
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_cache_entry_reset_ref(lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
entry->ref_cnt = 0;
}
void lv_cache_entry_inc_ref(lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
entry->ref_cnt++;
}
void lv_cache_entry_dec_ref(lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
entry->ref_cnt--;
if(entry->ref_cnt < 0) {
LV_LOG_WARN("ref_cnt(%" LV_PRIu32 ") < 0", entry->ref_cnt);
entry->ref_cnt = 0;
}
}
int32_t lv_cache_entry_get_ref(lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
return entry->ref_cnt;
}
uint32_t lv_cache_entry_get_node_size(lv_cache_entry_t * entry)
{
return entry->node_size;
}
void lv_cache_entry_set_node_size(lv_cache_entry_t * entry, uint32_t node_size)
{
LV_ASSERT_NULL(entry);
entry->node_size = node_size;
}
void lv_cache_entry_set_invalid(lv_cache_entry_t * entry, bool is_invalid)
{
LV_ASSERT_NULL(entry);
entry->is_invalid = is_invalid;
}
bool lv_cache_entry_is_invalid(lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
return entry->is_invalid;
}
void * lv_cache_entry_get_data(lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
return (uint8_t *)entry - entry->node_size;
}
void * lv_cache_entry_acquire_data(lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
lv_cache_entry_inc_ref(entry);
return lv_cache_entry_get_data(entry);
}
void lv_cache_entry_release_data(lv_cache_entry_t * entry, void * user_data)
{
LV_UNUSED(user_data);
LV_ASSERT_NULL(entry);
if(lv_cache_entry_get_ref(entry) == 0) {
LV_LOG_ERROR("ref_cnt(%" LV_PRIu32 ") == 0", entry->ref_cnt);
return;
}
lv_cache_entry_dec_ref(entry);
}
lv_cache_entry_t * lv_cache_entry_get_entry(void * data, const uint32_t node_size)
{
LV_ASSERT_NULL(data);
return (lv_cache_entry_t *)((uint8_t *)data + node_size);
}
void lv_cache_entry_set_cache(lv_cache_entry_t * entry, const lv_cache_t * cache)
{
LV_ASSERT_NULL(entry);
entry->cache = cache;
}
const lv_cache_t * lv_cache_entry_get_cache(const lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
return entry->cache;
}
uint32_t lv_cache_entry_get_size(const uint32_t node_size)
{
return node_size + sizeof(lv_cache_entry_t);
}
lv_cache_entry_t * lv_cache_entry_alloc(const uint32_t node_size, const lv_cache_t * cache)
{
void * res = lv_malloc_zeroed(lv_cache_entry_get_size(node_size));
LV_ASSERT_MALLOC(res)
if(res == NULL) {
LV_LOG_ERROR("malloc failed");
return NULL;
}
lv_cache_entry_t * entry = (lv_cache_entry_t *)res;
lv_cache_entry_init(entry, cache, node_size);
return (lv_cache_entry_t *)((uint8_t *)entry + node_size);
}
void lv_cache_entry_init(lv_cache_entry_t * entry, const lv_cache_t * cache, const uint32_t node_size)
{
LV_ASSERT_NULL(entry);
LV_ASSERT_NULL(cache);
entry->cache = cache;
entry->node_size = node_size;
entry->ref_cnt = 0;
entry->is_invalid = false;
}
void lv_cache_entry_delete(lv_cache_entry_t * entry)
{
LV_ASSERT_NULL(entry);
void * data = lv_cache_entry_get_data(entry);
lv_free(data);
}
/**********************
* STATIC FUNCTIONS
**********************/

55
src/misc/cache/lv_cache_entry.h vendored Normal file
View File

@@ -0,0 +1,55 @@
/**
* @file lv_cache_entry.h
*
*/
#ifndef LV_CACHE_ENTRY_H
#define LV_CACHE_ENTRY_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../osal/lv_os.h"
#include "../lv_types.h"
#include "lv_cache_private.h"
#include <stdbool.h>
#include <stdlib.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
uint32_t lv_cache_entry_get_size(const uint32_t node_size);
int32_t lv_cache_entry_get_ref(lv_cache_entry_t * entry);
uint32_t lv_cache_entry_get_node_size(lv_cache_entry_t * entry);
bool lv_cache_entry_is_invalid(lv_cache_entry_t * entry);
void * lv_cache_entry_get_data(lv_cache_entry_t * entry);
const lv_cache_t * lv_cache_entry_get_cache(const lv_cache_entry_t * entry);
lv_cache_entry_t * lv_cache_entry_get_entry(void * data, const uint32_t node_size);
lv_cache_entry_t * lv_cache_entry_alloc(const uint32_t node_size, const lv_cache_t * cache);
void lv_cache_entry_init(lv_cache_entry_t * entry, const lv_cache_t * cache, const uint32_t node_size);
void lv_cache_entry_delete(lv_cache_entry_t * entry);
/*************************
* GLOBAL VARIABLES
*************************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CACHE_ENTRY_H*/

51
src/misc/cache/lv_cache_entry_private.h vendored Normal file
View File

@@ -0,0 +1,51 @@
/**
* @file lv_cache_entry_private.h
*
*/
#ifndef LV_CACHE_ENTRY_PRIVATE
#define LV_CACHE_ENTRY_PRIVATE
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_types.h"
#include <stdbool.h>
#include <stdlib.h>
#include "../../osal/lv_os.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_cache_entry_reset_ref(lv_cache_entry_t * entry);
void lv_cache_entry_inc_ref(lv_cache_entry_t * entry);
void lv_cache_entry_dec_ref(lv_cache_entry_t * entry);
void lv_cache_entry_set_node_size(lv_cache_entry_t * entry, uint32_t node_size);
void lv_cache_entry_set_invalid(lv_cache_entry_t * entry, bool is_invalid);
void lv_cache_entry_set_cache(lv_cache_entry_t * entry, const lv_cache_t * cache);
void * lv_cache_entry_acquire_data(lv_cache_entry_t * entry);
void lv_cache_entry_release_data(lv_cache_entry_t * entry, void * user_data);
/*************************
* GLOBAL VARIABLES
*************************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CACHE_ENTRY_PRIVATE*/

114
src/misc/cache/lv_cache_private.h vendored Normal file
View File

@@ -0,0 +1,114 @@
/**
* @file lv_cache_private.h
*
*/
#ifndef LV_CACHE_PRIVATE_H
#define LV_CACHE_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_types.h"
#include <stdbool.h>
#include <stdlib.h>
#include "../../osal/lv_os.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*-----------------
* Cache entry slot
*----------------*/
struct _lv_cache_ops_t;
struct _lv_cache_t;
struct _lv_cache_class_t;
struct _lv_cache_entry_t;
typedef struct _lv_cache_ops_t lv_cache_ops_t;
typedef struct _lv_cache_t lv_cache_t;
typedef struct _lv_cache_class_t lv_cache_class_t;
typedef struct _lv_cache_entry_t lv_cache_entry_t;
typedef int8_t lv_cache_compare_res_t;
typedef bool (*lv_cache_create_cb_t)(void * node, void * user_data);
typedef void (*lv_cache_free_cb_t)(void * node, void * user_data);
typedef lv_cache_compare_res_t (*lv_cache_compare_cb_t)(const void * a, const void * b);
typedef void * (*lv_cache_alloc_cb_t)(void);
typedef bool (*lv_cache_init_cb_t)(lv_cache_t * cache);
typedef void (*lv_cache_destroy_cb_t)(lv_cache_t * cache, void * user_data);
typedef lv_cache_entry_t * (*lv_cache_get_cb_t)(lv_cache_t * cache, const void * key, void * user_data);
typedef lv_cache_entry_t * (*lv_cache_add_cb_t)(lv_cache_t * cache, const void * key, void * user_data);
typedef void (*lv_cache_remove_cb_t)(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data);
typedef void (*lv_cache_drop_cb_t)(lv_cache_t * cache, const void * key, void * user_data);
typedef void (*lv_cache_clear_cb_t)(lv_cache_t * cache, void * user_data);
struct _lv_cache_ops_t {
lv_cache_compare_cb_t compare_cb;
lv_cache_create_cb_t create_cb;
lv_cache_free_cb_t free_cb;
};
struct _lv_cache_t {
const lv_cache_class_t * clz;
size_t node_size;
size_t max_size;
size_t size;
lv_cache_ops_t ops;
lv_mutex_t lock;
};
struct _lv_cache_class_t {
lv_cache_alloc_cb_t alloc_cb;
lv_cache_init_cb_t init_cb;
lv_cache_destroy_cb_t destroy_cb;
lv_cache_get_cb_t get_cb;
lv_cache_add_cb_t add_cb;
lv_cache_remove_cb_t remove_cb;
lv_cache_drop_cb_t drop_cb;
lv_cache_clear_cb_t drop_all_cb;
};
/*-----------------
* Cache entry slot
*----------------*/
struct _lv_cache_slot_size_t;
typedef struct _lv_cache_slot_size_t lv_cache_slot_size_t;
struct _lv_cache_slot_size_t {
size_t size;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/*************************
* GLOBAL VARIABLES
*************************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CACHE_PRIVATE_H*/

59
src/misc/cache/lv_image_cache.c vendored Normal file
View File

@@ -0,0 +1,59 @@
/**
* @file lv_image_cache.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../lv_assert.h"
#include "lv_image_cache.h"
#include "../../core/lv_global.h"
/*********************
* DEFINES
*********************/
#define img_cache_p (LV_GLOBAL_DEFAULT()->img_cache)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_image_cache_drop(const void * src)
{
#if LV_CACHE_DEF_SIZE > 0
if(src == NULL) {
lv_cache_drop_all(img_cache_p, NULL);
return;
}
lv_image_cache_data_t search_key = {
.src = src,
.src_type = lv_image_src_get_type(src),
};
lv_cache_drop(img_cache_p, &search_key, NULL);
#else
LV_UNUSED(src);
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -1,10 +1,10 @@
/**
* @file lv_cache_builtin.h
*
* @file lv_image_cache.h
*
*/
#ifndef LV_CACHE_BUILTIN_H
#define LV_CACHE_BUILTIN_H
#ifndef LV_IMAGE_CACHE_H
#define LV_IMAGE_CACHE_H
#ifdef __cplusplus
extern "C" {
@@ -13,7 +13,7 @@ extern "C" {
/*********************
* INCLUDES
*********************/
#include "lv_ll.h"
#include "lv_cache_private.h"
/*********************
* DEFINES
@@ -23,18 +23,13 @@ extern "C" {
* TYPEDEFS
**********************/
typedef struct {
uint32_t cur_size;
lv_ll_t entry_ll;
} lv_cache_builtin_dsc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
void _lv_cache_builtin_init(void);
void _lv_cache_builtin_deinit(void);
void lv_image_cache_drop(const void * src);
/*************************
* GLOBAL VARIABLES
*************************/
/**********************
* MACROS
@@ -44,4 +39,4 @@ void _lv_cache_builtin_deinit(void);
} /*extern "C"*/
#endif
#endif /*LV_CACHE_BUILTIN_H*/
#endif /*LV_IMAGE_CACHE_H*/

View File

@@ -1,164 +0,0 @@
/**
* @file lv_cache.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_cache.h"
#include "../stdlib/lv_string.h"
#include "../osal/lv_os.h"
#include "../core/lv_global.h"
/*********************
* DEFINES
*********************/
#define _cache_manager LV_GLOBAL_DEFAULT()->cache_manager
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void _lv_cache_init(void)
{
lv_memzero(&_cache_manager, sizeof(lv_cache_manager_t));
lv_mutex_init(&_cache_manager.mutex);
}
void _lv_cache_deinit(void)
{
lv_mutex_delete(&_cache_manager.mutex);
}
void lv_cache_set_manager(lv_cache_manager_t * manager)
{
LV_ASSERT(_cache_manager.locked);
if(manager == NULL) return;
if(_cache_manager.empty_cb != NULL) _cache_manager.empty_cb();
else if(_cache_manager.set_max_size_cb != NULL) _cache_manager.set_max_size_cb(0);
_cache_manager.add_cb = manager->add_cb;
_cache_manager.find_by_data_cb = manager->find_by_data_cb;
_cache_manager.invalidate_cb = manager->invalidate_cb;
_cache_manager.get_data_cb = manager->get_data_cb;
_cache_manager.release_cb = manager->release_cb;
_cache_manager.set_max_size_cb = manager->set_max_size_cb;
_cache_manager.empty_cb = manager->empty_cb;
if(_cache_manager.set_max_size_cb != NULL) _cache_manager.set_max_size_cb(_cache_manager.max_size);
}
lv_cache_entry_t * lv_cache_add(const void * data, size_t data_size, uint32_t data_type, size_t memory_usage)
{
LV_ASSERT(_cache_manager.locked);
if(_cache_manager.add_cb == NULL) return NULL;
return _cache_manager.add_cb(data, data_size, data_type, memory_usage);
}
lv_cache_entry_t * lv_cache_find_by_data(const void * data, size_t data_size, uint32_t data_type)
{
LV_ASSERT(_cache_manager.locked);
if(_cache_manager.find_by_data_cb == NULL) return NULL;
return _cache_manager.find_by_data_cb(data, data_size, data_type);
}
lv_cache_entry_t * lv_cache_find_by_src(lv_cache_entry_t * entry, const void * src, lv_cache_src_type_t src_type)
{
LV_ASSERT(_cache_manager.locked);
if(_cache_manager.find_by_src_cb == NULL) return NULL;
return _cache_manager.find_by_src_cb(entry, src, src_type);
}
void lv_cache_invalidate(lv_cache_entry_t * entry)
{
LV_ASSERT(_cache_manager.locked);
if(_cache_manager.invalidate_cb == NULL) return;
_cache_manager.invalidate_cb(entry);
}
void lv_cache_invalidate_by_src(const void * src, lv_cache_src_type_t src_type)
{
lv_cache_entry_t * next;
LV_ASSERT(_cache_manager.locked);
lv_cache_entry_t * entry = lv_cache_find_by_src(NULL, src, src_type);
while(entry) {
next = lv_cache_find_by_src(entry, src, src_type);
lv_cache_invalidate(entry);
entry = next;
}
}
const void * lv_cache_get_data(lv_cache_entry_t * entry)
{
LV_ASSERT(_cache_manager.locked);
if(_cache_manager.get_data_cb == NULL) return NULL;
const void * data = _cache_manager.get_data_cb(entry);
return data;
}
void lv_cache_release(lv_cache_entry_t * entry)
{
LV_ASSERT(_cache_manager.locked);
if(_cache_manager.release_cb == NULL) return;
_cache_manager.release_cb(entry);
}
void lv_cache_set_max_size(size_t size)
{
LV_ASSERT(_cache_manager.locked);
if(_cache_manager.set_max_size_cb == NULL) return;
_cache_manager.set_max_size_cb(size);
_cache_manager.max_size = size;
}
size_t lv_cache_get_max_size(void)
{
return _cache_manager.max_size;
}
void lv_cache_lock(void)
{
lv_mutex_lock(&_cache_manager.mutex);
_cache_manager.locked = 1;
}
void lv_cache_unlock(void)
{
_cache_manager.locked = 0;
lv_mutex_unlock(&_cache_manager.mutex);
}
uint32_t lv_cache_register_data_type(void)
{
_cache_manager.last_data_type++;
return _cache_manager.last_data_type;
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -1,306 +0,0 @@
/**
* @file lv_cache.h
*
*/
#ifndef LV_CACHE_H
#define LV_CACHE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "../osal/lv_os.h"
/*********************
* DEFINES
*********************/
#define LV_CACHE_DATA_TYPE_NOT_SET 0xFFFFFFFF
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_CACHE_SRC_TYPE_PATH,
LV_CACHE_SRC_TYPE_POINTER,
} lv_cache_src_type_t;
typedef struct _lv_cache_entry_t {
/**
* The data to cache.
* Can be just a pointer to a byte array, or pointer to a complex structure, or anything else*/
const void * data;
/**
* Size of data in bytes.
* It's not the size of the cached data, just the size of the structure pointed by `data`
* E.g. `data` can point to descriptor struct and the size of that struct needs to be stored here.
* It can be used in the `compary_cb` to compare `data` fields of the entries with a requested one*/
uint32_t data_size;
/**An integer ID to tell the type of the cached data.
* If set to `LV_CACHE_DATA_TYPE_NOT_SET` `lv_cache_find_by_data` cannot be used*/
uint32_t data_type;
/**
* The source from which the cache is created.
* It's can or cannot be the same as data, or different, or NULL.
* It's used to find the cache entries which are relted to same source.
* E.g. the same image, font, etc. */
const void * src;
lv_cache_src_type_t src_type;
/**Arbitrary parameters to better identify the source*/
int32_t param1;
int32_t param2;
/** Memory in bytes used by data. */
uint32_t memory_usage;
/**
* Called to compare the data of cache entries.
* Before calling this function LVGL checks that `data_size` of both entries are the same.
* This callback look into `data` and check all the pointers and their content on any level.
* @param data1 first data to compare
* @param data2 second data to compare
* @param data_size size of data
* @return true: `data1` and `data2` are the same
*/
bool (*compare_cb)(const void * data1, const void * data2, size_t data_size);
/**
* Called when the entry is invalidated to free its data
* @param e the cache entry to free
*/
void (*invalidate_cb)(struct _lv_cache_entry_t * e);
/** User processing tag*/
uint32_t process_state;
/** On access to any cache entry, `life` of each cache entry will be incremented by their own `weight` to keep the entry alive longer*/
uint32_t weight;
/** The current `life`. Entries with the smallest life will be purged from the cache if a new entry needs to be cached*/
int32_t life;
/** Count how many times the cached data is being used.
* It will be incremented in lv_cache_get_data and decremented in lv_cache_release.
* A data will dropped from the cache only if its usage_count is zero */
uint32_t usage_count;
/** The cache entry was larger then the max cache size so only a temporary entry was allocated
* The entry will be closed and freed in `lv_cache_release` automatically*/
uint32_t temporary : 1;
/**Any user data if needed*/
void * user_data;
} lv_cache_entry_t;
/**
* Add a new entry to the cache with the given size.
* It won't allocate any buffers just free enough space to be a new entry
* with `size` bytes fits.
* @param data data the cache, can be a complex structure too
* @param data_size the size of data (if it's a struct then the size of the struct)
* @param data_type type of data to identify the kind of this cache entry
* @param memory_usage the size of memory used by this entry (`data` might contain pointers so it's size of all buffers related to entry)
* @return a handler for the new cache entry
*/
typedef lv_cache_entry_t * (*lv_cache_add_cb)(const void * data, size_t data_size, uint32_t data_type,
size_t memory_usage);
/**
* Find a cache entry based on its data
* @param data the data to find
* @param data_size size of data
* @param data_type ID for the data type
* @return the cache entry with given `data` or NULL if not found
*/
typedef lv_cache_entry_t * (*lv_cache_find_by_data_cb)(const void * data, size_t data_size, uint32_t data_type);
/**
* Get the next entry which has the given source and parameters
* @param prev_entry pointer to the previous entry from which the nest should be found. NULL means to start from the beginning.
* @param src a pointer or a string
* @param src_type element of lv_cache_src_type_t
* @return the cache entry with given source or NULL if not found
*/
typedef lv_cache_entry_t * (*lv_cache_find_by_src_cb)(lv_cache_entry_t * entry, const void * src,
lv_cache_src_type_t src_type);
/**
* Invalidate (drop) a cache entry
* @param entry the entry to invalidate. (can be retrieved by `lv_cache_find()`)
*/
typedef void (*lv_cache_invalidate_cb)(lv_cache_entry_t * entry);
/**
* Get the data of a cache entry.
* It is considered a cached data access so the cache manager can count that
* this entry was used on more times, and therefore it's more relevant.
* It also increments entry->usage_count to indicate that the data is being used
* and cannot be dropped.
* @param entry the cache entry whose data should be retrieved
*/
typedef const void * (*lv_cache_get_data_cb)(lv_cache_entry_t * entry);
/**
* Mark the cache entry as unused. It decrements entry->usage_count.
* @param entry the cache entry to invalidate
*/
typedef void (*lv_cache_release_cb)(lv_cache_entry_t * entry);
/**
* Set maximum cache size in bytes.
* @param size the max size in byes
*/
typedef void (*lv_cache_set_max_size_cb)(size_t size);
/**
* Empty the cache.
*/
typedef void (*lv_cache_empty_cb)(void);
typedef struct {
lv_cache_add_cb add_cb;
lv_cache_find_by_data_cb find_by_data_cb;
lv_cache_find_by_src_cb find_by_src_cb;
lv_cache_invalidate_cb invalidate_cb;
lv_cache_get_data_cb get_data_cb;
lv_cache_release_cb release_cb;
lv_cache_set_max_size_cb set_max_size_cb;
lv_cache_empty_cb empty_cb;
lv_mutex_t mutex;
size_t max_size;
uint32_t locked : 1; /**< Show the mutex state, used to log unlocked cache access*/
uint32_t last_data_type;
} lv_cache_manager_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the cache module
*/
void _lv_cache_init(void);
/**
* Deinitialize the cache module
*/
void _lv_cache_deinit(void);
/**
* Set new cache manager
* @param manager the new cache manager with callback functions set
*/
void lv_cache_set_manager(lv_cache_manager_t * manager);
/**
* Add a new entry to the cache with the given size.
* It won't allocate any buffers just free enough space to be a new entry
* with `size` bytes fits.
* @param data data the cache, can be a complex structure too
* @param data_size the size of data (if it's a struct then the size of the struct)
* @param data_type type of data to identify the kind of this cache entry
* @param memory_usage the size of memory used by this entry (`data` might contain pointers so it's size of all buffers related to entry)
* @return a handler for the new cache entry
*/
lv_cache_entry_t * lv_cache_add(const void * data, size_t data_size, uint32_t data_type, size_t memory_usage);
/**
* Find a cache entry based on its data
* @param data the data to find
* @param data_size size of data
* @param data_type ID of data type
* @return the cache entry with given `data` or NULL if not found
*/
lv_cache_entry_t * lv_cache_find_by_data(const void * data, size_t data_size, uint32_t data_type);
/**
* Get the next entry which has the given source and parameters
* @param prev_entry pointer to the previous entry from which the nest should be found. NULL means to start from the beginning.
* @param src a pointer or a string
* @param src_type element of lv_cache_src_type_t
* @return the cache entry with given source or NULL if not found
*/
lv_cache_entry_t * lv_cache_find_by_src(lv_cache_entry_t * entry, const void * src, lv_cache_src_type_t src_type);
/**
* Invalidate (drop) a cache entry. It will call the entry's `invalidate_cb` to free the resources
* @param entry the entry to invalidate. (can be retrieved by `lv_cache_find()`)
*/
void lv_cache_invalidate(lv_cache_entry_t * entry);
/**
* Invalidate all cache entries with a given source
* @param src a pointer or a string
* @param src_type element of lv_cache_src_type_t
*/
void lv_cache_invalidate_by_src(const void * src, lv_cache_src_type_t src_type);
/**
* Get the data of a cache entry.
* It is considered a cached data access so the cache manager can count that
* this entry was used on more times, and therefore it's more relevant.
* It also increments entry->usage_count to indicate that the data is being used
* and cannot be dropped.
* @param entry the cache entry whose data should be retrieved
*/
const void * lv_cache_get_data(lv_cache_entry_t * entry);
/**
* Mark the cache entry as unused. It decrements entry->usage_count.
* @param entry
*/
void lv_cache_release(lv_cache_entry_t * entry);
/**
* Set maximum cache size in bytes.
* @param size the max size in byes
*/
void lv_cache_set_max_size(size_t size);
/**
* Get the max size of the cache
* @return the max size in bytes
*/
size_t lv_cache_get_max_size(void);
/**
* Lock the mutex of the cache.
* Needs to be called manually before any cache operation,
*/
void lv_cache_lock(void);
/**
* Unlock the mutex of the cache.
* Needs to be called manually after any cache operation,
*/
void lv_cache_unlock(void);
/**
* Register a data type which can be used as `entry->data_type`.
* @return the registered unique data type ID.
*/
uint32_t lv_cache_register_data_type(void);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CACHE_H*/

View File

@@ -1,238 +0,0 @@
/**
* @file lv_cache_builtin.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_cache.h"
#include "../stdlib/lv_string.h"
#include "../core/lv_global.h"
#include "../misc/lv_ll.h"
#include "../osal/lv_os.h"
/*********************
* DEFINES
*********************/
#define _cache_manager LV_GLOBAL_DEFAULT()->cache_manager
#define dsc LV_GLOBAL_DEFAULT()->cache_builtin_dsc
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_cache_entry_t * add_cb(const void * data, size_t data_size, uint32_t data_type, size_t memory_usage);
static lv_cache_entry_t * find_by_data_cb(const void * data, size_t data_size, uint32_t data_type);
static lv_cache_entry_t * find_by_src_cb(lv_cache_entry_t * entry, const void * src, lv_cache_src_type_t src_type);
static void invalidate_cb(lv_cache_entry_t * entry);
static const void * get_data_cb(lv_cache_entry_t * entry);
static void release_cb(lv_cache_entry_t * entry);
static void set_max_size_cb(size_t new_size);
static void empty_cb(void);
static bool drop_youngest(void);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
#if LV_USE_LOG && LV_LOG_TRACE_CACHE
#define LV_TRACE_CACHE(...) LV_LOG_TRACE(__VA_ARGS__)
#else
#define LV_TRACE_CACHE(...)
#endif
/**********************
* GLOBAL FUNCTIONS
**********************/
void _lv_cache_builtin_init(void)
{
lv_memzero(&dsc, sizeof(lv_cache_builtin_dsc_t));
_lv_ll_init(&dsc.entry_ll, sizeof(lv_cache_entry_t));
_cache_manager.add_cb = add_cb;
_cache_manager.find_by_data_cb = find_by_data_cb;
_cache_manager.find_by_src_cb = find_by_src_cb;
_cache_manager.invalidate_cb = invalidate_cb;
_cache_manager.get_data_cb = get_data_cb;
_cache_manager.release_cb = release_cb;
_cache_manager.set_max_size_cb = set_max_size_cb;
_cache_manager.empty_cb = empty_cb;
}
void _lv_cache_builtin_deinit(void)
{
lv_cache_entry_t * entry = _lv_ll_get_head(&dsc.entry_ll);
lv_cache_entry_t * next;
while(entry) {
next = _lv_ll_get_next(&dsc.entry_ll, entry);
invalidate_cb(entry);
entry = next;
}
_lv_ll_clear(&dsc.entry_ll);
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_cache_entry_t * add_cb(const void * data, size_t data_size, uint32_t data_type, size_t memory_usage)
{
size_t max_size = lv_cache_get_max_size();
/*Can't cache data larger than max size*/
bool temporary = memory_usage > max_size ? true : false;
if(!temporary) {
/*Keep dropping items until there is enough space*/
while(dsc.cur_size + memory_usage > _cache_manager.max_size) {
bool ret = drop_youngest();
/*No item could be dropped.
*It can happen because the usage_count of the remaining items are not zero.*/
if(ret == false) {
temporary = true;
break;
}
}
}
lv_cache_entry_t * entry = _lv_ll_ins_head(&dsc.entry_ll);
lv_memzero(entry, sizeof(lv_cache_entry_t));
entry->memory_usage = memory_usage;
entry->weight = 1;
entry->temporary = temporary;
entry->data = data;
entry->data_size = data_size;
entry->data_type = data_type;
if(temporary) {
LV_TRACE_CACHE("Add temporary cache: %lu bytes", (unsigned long)memory_usage);
}
else {
LV_TRACE_CACHE("Add cache: %lu bytes", (unsigned long)memory_usage);
dsc.cur_size += memory_usage;
}
return entry;
}
static lv_cache_entry_t * find_by_data_cb(const void * data, size_t data_size, uint32_t data_type)
{
lv_cache_entry_t * entry = _lv_ll_get_head(&dsc.entry_ll);
while(entry) {
if(entry->data_type == data_type && entry->data_size == data_size) {
if(entry->compare_cb(entry->data, data, data_size)) {
return entry;
}
}
entry = _lv_ll_get_next(&dsc.entry_ll, entry);
}
return NULL;
}
static lv_cache_entry_t * find_by_src_cb(lv_cache_entry_t * entry, const void * src, lv_cache_src_type_t src_type)
{
if(entry == NULL) entry = _lv_ll_get_head(&dsc.entry_ll);
else entry = _lv_ll_get_next(&dsc.entry_ll, entry);
while(entry) {
if(src_type == LV_CACHE_SRC_TYPE_POINTER && entry->src == src) return entry;
if(src_type == LV_CACHE_SRC_TYPE_PATH && lv_strcmp(entry->src, src) == 0) return entry;
entry = _lv_ll_get_next(&dsc.entry_ll, entry);
}
return NULL;
}
static void invalidate_cb(lv_cache_entry_t * entry)
{
if(entry == NULL) return;
dsc.cur_size -= entry->memory_usage;
LV_TRACE_CACHE("Drop cache: %u bytes", (uint32_t)entry->memory_usage);
if(entry->invalidate_cb) entry->invalidate_cb(entry);
_lv_ll_remove(&dsc.entry_ll, entry);
lv_free(entry);
}
static const void * get_data_cb(lv_cache_entry_t * entry)
{
lv_cache_entry_t * e = _lv_ll_get_head(&dsc.entry_ll);
while(e) {
e->life += e->weight;
e = _lv_ll_get_next(&dsc.entry_ll, e);
}
entry->usage_count++;
return entry->data;
}
static void release_cb(lv_cache_entry_t * entry)
{
if(entry == NULL) return;
if(entry->temporary) {
invalidate_cb(entry);
}
else {
if(entry->usage_count == 0) {
LV_LOG_ERROR("More lv_cache_release than lv_cache_get_data");
return;
}
entry->usage_count--;
}
}
static void set_max_size_cb(size_t new_size)
{
while(dsc.cur_size > new_size) {
bool ret = drop_youngest();
/*No item could be dropped.
*It can happen because the usage_count of the remaining items are not zero.*/
if(ret == false) return;
}
}
static void empty_cb(void)
{
lv_cache_entry_t * entry_to_drop = NULL;
lv_cache_entry_t * entry = _lv_ll_get_head(&dsc.entry_ll);
while(entry) {
entry_to_drop = entry;
entry = _lv_ll_get_next(&dsc.entry_ll, entry);
invalidate_cb(entry_to_drop);
}
}
static bool drop_youngest(void)
{
int32_t life_min = INT32_MAX;
lv_cache_entry_t * entry_to_drop = NULL;
lv_cache_entry_t * entry = _lv_ll_get_head(&dsc.entry_ll);
while(entry) {
if(entry->life < life_min && entry->usage_count == 0) entry_to_drop = entry;
entry = _lv_ll_get_next(&dsc.entry_ll, entry);
}
if(entry_to_drop == NULL) {
return false;
}
invalidate_cb(entry_to_drop);
return true;
}

View File

@@ -1,314 +0,0 @@
/**
* @file lv_lru_rb.c
*
*/
/**
* @brief cache model
* */
/*************************************************************************\
* *
* ┏ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ┓ *
* ┏ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ┌ ─ ─ ─ ┐ *
* ┌ ─ ─ ─ ─ ─ ─ ─ ┃ ┃ Cache insert ┃ *
* ┃ RB Tree │ │Hitting│ head *
* └ ─ ─ ─ ─ ─ ─ ─ ┃ ┃ ─ ─ ─ ─ ┃ *
* ┃ ┌─┬─┬─┬─┐ ┌─────┐ *
* ┌──│◄│B│►│ │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┃─ ─ ╋ ─ ─▶│ B │ ┃ *
* ┃ │ └─┴─┴─┴─┘ └──▲──┘ *
* │ │ ┃ ┃ │ ┃ *
* ┃ │ │ ┌──┴──┐ *
* │ └──────┐ ┌ ─┃─ ─ ╋ ─ ─▶│ E │ ┃ *
* ┃ ▼ ┌ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ └──▲──┘ *
* ┌─┬─┬─┬─┐ ▼ │ ┃ ┃ │ ┃ *
* ┃│◄│A│►│ │─ ─ ┘ ┌─┬─┬─┬─┐ │ ┌──┴──┐ *
* └─┴─┴─┴─┘ ┌───│◄│D│►│ │─ ─ ─ ─ ─ ─│─ ╋ ┐ ┃ ─ ▶│ A │ ┌ ─ ─ ─ ─ ┐ ┃ *
* ┃ │ └─┴─┴─┴─┘ └──▲──┘ LRU *
* │ │ │ ┃ │ ┃ │ │ Cache │ ┃ *
* ┃ ▼ └──────┐ ┌──┴──┐ ─ ─ ─ ─ ─ *
* ┌─┬─┬─┬─┐ ▼ │ ┃ └ ─┃─ ─ ▶│ D │ ┃ *
* ┃ │◄│C│►│ │─ ─ ┌─┬─┬─┬─┐ └──▲──┘ *
* └─┴─┴─┴─┘ │ │◄│E│►│ │─ ┘ ┃ ┃ │ ┃ *
* ┃ └─┴─┴─┴─┘ ┌──┴──┐ *
* │ │ ─ ╋ ─ ─┃─ ─ ▶│ C │ ┃ *
* ┃ ─ ─ ─ ─ ┼ ─ ─ ┘ └──▲──┘ *
* ▼ ┃ ┃ ┌ ─ ─│─ ─ ┐ ┃ *
* ┃ ┌─┬─┬─┬─┐ ┌──┴──┐ *
* │◄│F│►│ │─ ─┃─ ─ ╋ ─ ┼▶│ F │ │ ┃ *
* ┃ └─┴─┴─┴─┘ └─────┘ *
* ┃ ┃ └ ─ ─ ─ ─ ┘ ┃ *
* ┃ remove *
* ┃ ┃ tail ┃ *
* ┗ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ ━ *
* *
\*************************************************************************/
/*********************
* INCLUDES
*********************/
#include "lv_lru_rb.h"
#include "lv_ll.h"
#include "lv_rb.h"
#include "../stdlib/lv_string.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_lru_rb_t {
lv_rb_t rb;
lv_ll_t lru_ll;
size_t max_size;
size_t size;
lv_lru_rb_create_cb_t create_cb;
lv_lru_rb_free_cb_t free_cb;
};
/**********************
* STATIC PROTOTYPES
**********************/
static void * alloc_new_node(lv_lru_rb_t * lru, void * key, void * user_data);
inline static void ** get_lru_node(lv_lru_rb_t * lru, lv_rb_node_t * node);
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_lru_rb_t * lv_lru_rb_create(size_t node_size, size_t max_size, lv_lru_rb_compare_cb_t compare_cb,
lv_lru_rb_create_cb_t create_cb, lv_lru_rb_free_cb_t free_cb)
{
LV_ASSERT_NULL(compare_cb);
LV_ASSERT_NULL(create_cb);
LV_ASSERT_NULL(free_cb);
LV_ASSERT(node_size > 0);
LV_ASSERT(max_size > 0);
if(node_size == 0 || max_size == 0 || compare_cb == NULL || create_cb == NULL || free_cb == NULL) {
return NULL;
}
lv_lru_rb_t * lru;
lru = lv_malloc(sizeof(lv_lru_rb_t));
LV_ASSERT_MALLOC(lru);
if(lru == NULL) {
return NULL;
}
lv_memzero(lru, sizeof(lv_lru_rb_t));
/*add void* to store the ll node pointer*/
if(!lv_rb_init(&lru->rb, (lv_rb_compare_t)compare_cb, node_size + sizeof(void *))) {
return NULL;
}
_lv_ll_init(&lru->lru_ll, sizeof(void *));
lv_lru_rb_set_max_size(lru, max_size, NULL);
lv_lru_rb_set_compare_cb(lru, compare_cb, NULL);
lv_lru_rb_set_create_cb(lru, create_cb, NULL);
lv_lru_rb_set_free_cb(lru, free_cb, NULL);
return lru;
}
void lv_lru_rb_destroy(lv_lru_rb_t * lru, void * user_data)
{
LV_ASSERT_NULL(lru);
if(lru == NULL) {
return;
}
lv_lru_rb_drop_all(lru, user_data);
lv_free(lru);
}
void * lv_lru_rb_get_or_create(lv_lru_rb_t * lru, const void * key, void * user_data)
{
LV_ASSERT_NULL(lru);
LV_ASSERT_NULL(key);
if(lru == NULL || key == NULL) {
return NULL;
}
/*try the first ll node first*/
void * head = _lv_ll_get_head(&lru->lru_ll);
if(head) {
lv_rb_node_t * node = *(lv_rb_node_t **)head;
if(lru->rb.compare(node->data, key) == 0) {
return node->data;
}
}
lv_rb_node_t * node = lv_rb_find(&lru->rb, key);
/*cache hit*/
if(node) {
void * lru_node = *get_lru_node(lru, node);
head = _lv_ll_get_head(&lru->lru_ll);
_lv_ll_move_before(&lru->lru_ll, lru_node, head);
return node->data;
}
while(lru->size >= lru->max_size) {
void * tail = _lv_ll_get_tail(&lru->lru_ll);
lv_rb_node_t * tail_node = *(lv_rb_node_t **)tail;
void * search_key = tail_node->data;
lv_lru_rb_drop(lru, search_key, user_data);
}
/*cache miss*/
lv_rb_node_t * new_node = alloc_new_node(lru, (void *)key, user_data);
if(new_node == NULL) {
return NULL;
}
lru->size++;
return new_node->data;
}
void lv_lru_rb_drop(lv_lru_rb_t * lru, const void * key, void * user_data)
{
LV_ASSERT_NULL(lru);
LV_ASSERT_NULL(key);
if(lru == NULL || key == NULL) {
return;
}
lv_rb_node_t * node = lv_rb_find(&lru->rb, key);
if(node == NULL) {
return;
}
lru->free_cb(node->data, user_data);
void * lru_node = *get_lru_node(lru, node);
lv_rb_drop_node(&lru->rb, node);
_lv_ll_remove(&lru->lru_ll, lru_node);
lv_free(lru_node);
lru->size--;
}
void lv_lru_rb_drop_all(lv_lru_rb_t * lru, void * user_data)
{
LV_ASSERT_NULL(lru);
if(lru == NULL) {
return;
}
lv_rb_node_t ** node;
_LV_LL_READ(&lru->lru_ll, node) {
/*free user handled data and do other clean up*/
lru->free_cb((*node)->data, user_data);
}
lv_rb_destroy(&lru->rb);
_lv_ll_clear(&lru->lru_ll);
lru->size = 0;
}
void lv_lru_rb_set_max_size(lv_lru_rb_t * lru, size_t max_size, void * user_data)
{
LV_UNUSED(user_data);
lru->max_size = max_size;
}
size_t lv_lru_rb_get_max_size(lv_lru_rb_t * lru, void * user_data)
{
LV_UNUSED(user_data);
return lru->max_size;
}
size_t lv_lru_rb_get_size(lv_lru_rb_t * lru, void * user_data)
{
LV_UNUSED(user_data);
return lru->size;
}
size_t lv_lru_rb_get_free_size(lv_lru_rb_t * lru, void * user_data)
{
return lv_lru_rb_get_max_size(lru, user_data) - lv_lru_rb_get_size(lru, user_data);
}
void lv_lru_rb_set_compare_cb(lv_lru_rb_t * lru, lv_lru_rb_compare_cb_t compare_cb, void * user_data)
{
LV_UNUSED(user_data);
lru->rb.compare = (lv_rb_compare_t)compare_cb;
}
void lv_lru_rb_set_create_cb(lv_lru_rb_t * lru, lv_lru_rb_create_cb_t create_cb, void * user_data)
{
LV_UNUSED(user_data);
lru->create_cb = create_cb;
}
void lv_lru_rb_set_free_cb(lv_lru_rb_t * lru, lv_lru_rb_free_cb_t free_cb, void * user_data)
{
LV_UNUSED(user_data);
lru->free_cb = free_cb;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void * alloc_new_node(lv_lru_rb_t * lru, void * key, void * user_data)
{
LV_ASSERT_NULL(lru);
LV_ASSERT_NULL(key);
if(lru == NULL || key == NULL) {
return NULL;
}
lv_rb_node_t * node = lv_rb_insert(&lru->rb, key);
if(node == NULL)
goto FAILED_HANDLER3;
lv_memcpy(node->data, key, lru->rb.size - sizeof(void *));
bool alloc_res = lru->create_cb(node->data, user_data);
if(alloc_res == false)
goto FAILED_HANDLER2;
void * lru_node = _lv_ll_ins_head(&lru->lru_ll);
if(lru_node == NULL)
goto FAILED_HANDLER1;
lv_memcpy(lru_node, &node, sizeof(void *));
lv_memcpy(get_lru_node(lru, node), &lru_node, sizeof(void *));
goto FAILED_HANDLER3;
FAILED_HANDLER1:
lru->free_cb(node->data, user_data);
FAILED_HANDLER2:
lv_rb_drop_node(&lru->rb, node);
node = NULL;
FAILED_HANDLER3:
return node;
}
inline static void ** get_lru_node(lv_lru_rb_t * lru, lv_rb_node_t * node)
{
return (void **)((char *)node->data + lru->rb.size - sizeof(void *));
}

View File

@@ -1,64 +0,0 @@
/**
* @file lv_lru_rb.h
*
*/
#ifndef LV_LRU_RB_H
#define LV_LRU_RB_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lv_types.h"
#include "stdbool.h"
#include "lv_assert.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_lru_rb_t;
typedef struct _lv_lru_rb_t lv_lru_rb_t;
typedef int8_t lv_lru_rb_compare_res_t;
typedef bool (*lv_lru_rb_create_cb_t)(void * node, void * user_data);
typedef void (*lv_lru_rb_free_cb_t)(void * node, void * user_data);
typedef lv_lru_rb_compare_res_t (*lv_lru_rb_compare_cb_t)(const void * a, const void * b);
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_lru_rb_t * lv_lru_rb_create(size_t node_size, size_t max_size, lv_lru_rb_compare_cb_t compare_cb,
lv_lru_rb_create_cb_t create_cb, lv_lru_rb_free_cb_t free_cb);
void lv_lru_rb_destroy(lv_lru_rb_t * lru, void * user_data);
void * lv_lru_rb_get_or_create(lv_lru_rb_t * lru, const void * key, void * user_data);
void lv_lru_rb_drop(lv_lru_rb_t * lru, const void * key, void * user_data);
void lv_lru_rb_drop_all(lv_lru_rb_t * lru, void * user_data);
void lv_lru_rb_set_max_size(lv_lru_rb_t * lru, size_t max_size, void * user_data);
size_t lv_lru_rb_get_max_size(lv_lru_rb_t * lru, void * user_data);
size_t lv_lru_rb_get_size(lv_lru_rb_t * lru, void * user_data);
size_t lv_lru_rb_get_free_size(lv_lru_rb_t * lru, void * user_data);
void lv_lru_rb_set_compare_cb(lv_lru_rb_t * lru, lv_lru_rb_compare_cb_t compare_cb, void * user_data);
void lv_lru_rb_set_create_cb(lv_lru_rb_t * lru, lv_lru_rb_create_cb_t create_cb, void * user_data);
void lv_lru_rb_set_free_cb(lv_lru_rb_t * lru, lv_lru_rb_free_cb_t free_cb, void * user_data);
/*************************
* GLOBAL VARIABLES
*************************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_LRU_RB_H*/

View File

@@ -287,6 +287,7 @@ void lv_rb_destroy(lv_rb_t * tree)
node = parent;
}
}
tree->root = NULL;
}
lv_rb_node_t * lv_rb_minimum(lv_rb_t * tree)

View File

@@ -15,7 +15,7 @@
#include "../../display/lv_display.h"
#include "../../draw/sw/lv_draw_sw.h"
#include "../../stdlib/lv_string.h"
#include "../../misc/cache/lv_cache.h"
/*********************
* DEFINES
*********************/
@@ -79,12 +79,11 @@ void lv_canvas_set_buffer(lv_obj_t * obj, void * buf, int32_t w, int32_t h, lv_c
const void * src = lv_image_get_src(obj);
if(src) {
lv_cache_lock();
lv_cache_invalidate_by_src(src, LV_CACHE_SRC_TYPE_POINTER);
lv_cache_unlock();
lv_image_cache_drop(src);
}
lv_image_set_src(obj, canvas->draw_buf);
lv_image_cache_drop(canvas->draw_buf);
}
void lv_canvas_set_draw_buf(lv_obj_t * obj, lv_draw_buf_t * draw_buf)
@@ -97,12 +96,11 @@ void lv_canvas_set_draw_buf(lv_obj_t * obj, lv_draw_buf_t * draw_buf)
const void * src = lv_image_get_src(obj);
if(src) {
lv_cache_lock();
lv_cache_invalidate_by_src(src, LV_CACHE_SRC_TYPE_POINTER);
lv_cache_unlock();
lv_image_cache_drop(src);
}
lv_image_set_src(obj, draw_buf);
lv_image_cache_drop(draw_buf);
}
void lv_canvas_set_px(lv_obj_t * obj, int32_t x, int32_t y, lv_color_t color, lv_opa_t opa)
@@ -385,9 +383,7 @@ static void lv_canvas_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
lv_canvas_t * canvas = (lv_canvas_t *)obj;
if(canvas->draw_buf == NULL) return;
lv_cache_lock();
lv_cache_invalidate_by_src(canvas->draw_buf, LV_CACHE_SRC_TYPE_POINTER);
lv_cache_unlock();
lv_image_cache_drop(&canvas->draw_buf);
}
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -1,6 +1,5 @@
#define LV_MEM_SIZE (32 * 1024 * 1024)
#define LV_SHADOW_CACHE_SIZE (8 * 1024)
#define LV_IMAGE_CACHE_DEF_SIZE 32
#define LV_USE_LOG 1
#define LV_LOG_LEVEL LV_LOG_LEVEL_TRACE
#define LV_LOG_PRINTF 1
@@ -97,3 +96,5 @@
#define LV_USE_OBJ_ID_BUILTIN 1
#define LV_USE_OBJ_PROPERTY 1
#define LV_BIN_DECODER_RAM_LOAD 1
#define LV_CACHE_DEF_SIZE (10 * 1024 * 1024)

View File

@@ -12,8 +12,8 @@
#define LV_BUILD_EXAMPLES 1
#define LV_USE_THEME_SIMPLE 1
#define LV_USE_THEME_DEFAULT 0
#define LV_USE_THEME_SIMPLE 1
#define LV_USE_THEME_DEFAULT 0
#define LV_USE_LODEPNG 1
#define LV_USE_BMP 1

152
tests/src/test_cases/cache/test_cache.c vendored Normal file
View File

@@ -0,0 +1,152 @@
#if LV_BUILD_TEST
#include "../lvgl.h"
#include "lv_test_helpers.h"
#include "unity/unity.h"
static uint32_t MEM_SIZE = 0;
// Cache size in bytes
#define CACHE_SIZE_BYTES 1000
lv_cache_t * cache;
typedef struct _test_data {
lv_cache_slot_size_t slot;
int32_t key1;
int32_t key2;
void * data; // malloced data
} test_data;
static lv_cache_compare_res_t compare_cb(const test_data * lhs, const test_data * rhs)
{
if(lhs->key1 != rhs->key1) {
return lhs->key1 > rhs->key1 ? 1 : -1;
}
if(lhs->key2 != rhs->key2) {
return lhs->key2 > rhs->key2 ? 1 : -1;
}
return 0;
}
static void free_cb(test_data * node, void * user_data)
{
lv_free(node->data);
}
void setUp(void)
{
/* Function run before every test */
MEM_SIZE = lv_test_get_free_mem();
lv_cache_ops_t ops = {
.compare_cb = (lv_cache_compare_cb_t) compare_cb,
.create_cb = NULL,
.free_cb = (lv_cache_free_cb_t)free_cb,
};
cache = lv_cache_create(&lv_cache_class_lru_rb_size, sizeof(test_data), CACHE_SIZE_BYTES, ops);
}
void tearDown(void)
{
/* Function run after every test */
lv_cache_destroy(cache, NULL);
cache = NULL;
TEST_ASSERT_MEM_LEAK_LESS_THAN(MEM_SIZE, 32);
}
void test_cache_1(void)
{
void * record_data_ptr = NULL;
// create many node unless cache is full
uint32_t curr_mem_size = 8;
uint32_t curr_total_mem_size = 0;
while(curr_total_mem_size < CACHE_SIZE_BYTES) {
test_data search_key = {
.slot.size = curr_mem_size,
.key1 = (int32_t)curr_mem_size,
.key2 = (int32_t)curr_mem_size + 1
};
// acquire cache first
lv_cache_entry_t * entry = lv_cache_acquire(cache, &search_key, NULL);
if(entry != NULL) {
continue;
}
// if cache miss then add cache
entry = lv_cache_add(cache, &search_key, NULL);
TEST_ASSERT_NOT_NULL(entry);
test_data * data = lv_cache_entry_get_data(entry);
TEST_ASSERT_NOT_NULL(data);
data->data = lv_malloc(data->slot.size);
// record data ptr when {key1 = 32, key2 = 33}.
if(search_key.key1 == 32 && search_key.key2 == 33) {
record_data_ptr = data->data;
}
lv_cache_release(cache, entry, NULL);
curr_total_mem_size += curr_mem_size;
curr_mem_size *= 2;
TEST_PRINTF("cache free: %d, allocated: %d", lv_cache_get_free_size(cache, NULL), curr_mem_size);
}
/*
* allocated = 8 + 16 + 32 + 64 + 128 + 256 - 8 - 16 + 512 = 992
* free = 1000 - 992 = 8
* The last node size should be 1024, but the cache's max size is 1000. So new entry will be allocated failed and
* the loop will break.
* */
TEST_ASSERT_EQUAL(8, lv_cache_get_free_size(cache, NULL));
/*
* Search entry {key1 = 32, key2 = 33}
*/
test_data search_key32 = {
.key1 = 32,
.key2 = 33
};
lv_cache_entry_t * entry_key32 = lv_cache_acquire(cache, &search_key32, NULL);
test_data * cached_data_key32 = lv_cache_entry_get_data(entry_key32);
TEST_ASSERT_EQUAL(record_data_ptr, cached_data_key32->data);
/*
* Now drop the cache {key1 = 32, key2 = 33}. However, this entry is acquired once without release, so `drop`
* will not release the memory allocated by this entry.
*/
uint32_t mem_curr_free = lv_test_get_free_mem();
lv_cache_drop(cache, &search_key32, NULL);
/*
* Though it doesn't release the data, the entry and other structure has been freed.
* lv_rb_note_t (4 ptr + 1 int32 may align to 8 bit on 64 bit machine) + lv_ll (2 ptr + node_size).
* Also, the def heap has some other aligned attributes. It'll also affect the final result.
*/
TEST_ASSERT_MEM_LEAK_LESS_THAN(mem_curr_free,
sizeof(lv_rb_node_t)
+ sizeof(void *) + (sizeof(lv_ll_node_t *) + sizeof(lv_ll_node_t *))
+ 32); // the last 32 is an error in memory allocating
mem_curr_free = lv_test_get_free_mem();
lv_cache_release(cache, entry_key32, NULL);
TEST_ASSERT_MEM_LEAK_LESS_THAN(mem_curr_free,
lv_cache_entry_get_size(sizeof(test_data)) + sizeof(void *)
+ 32
+ 32);
// Now the freed cache size should be 8 + 32 = 40
TEST_ASSERT_EQUAL(40, lv_cache_get_free_size(cache, NULL));
}
#endif

View File

@@ -114,6 +114,8 @@ void canvas_blend_test(lv_obj_t * canvas_large, lv_draw_image_dsc_t * img_dsc,
lv_canvas_init_layer(canvas_large, &layer);
lv_draw_image(&layer, img_dsc, &area);
lv_canvas_finish_layer(canvas_large, &layer);
lv_image_cache_drop(img);
}
static void canvas_draw(const char * name, lv_color_format_t large_render_cf)

View File

@@ -58,7 +58,7 @@ void test_lodepng_1(void)
TEST_ASSERT_EQUAL_SCREENSHOT("libs/png_1.png");
TEST_ASSERT_MEM_LEAK_LESS_THAN(mem_before, 32);
TEST_ASSERT_MEM_LEAK_LESS_THAN(mem_before, 40);
/* Re-add libpng decoder */
lv_libpng_init();