1 /**
2  * @file lv_nuttx_image_cache.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "lv_nuttx_image_cache.h"
11 #include "../../core/lv_global.h"
12 #include "../../../lvgl.h"
13 
14 #if LV_USE_NUTTX
15 
16 #include "../../draw/lv_draw_buf_private.h"
17 #include <nuttx/mm/mm.h>
18 
19 /*********************
20  *      DEFINES
21  *********************/
22 
23 #define HEAP_NAME "GImageCache"
24 
25 #define img_cache_p         (LV_GLOBAL_DEFAULT()->img_cache)
26 #define img_header_cache_p  (LV_GLOBAL_DEFAULT()->img_header_cache)
27 #define ctx                 (*(lv_nuttx_ctx_image_cache_t **)&LV_GLOBAL_DEFAULT()->nuttx_ctx->image_cache)
28 #define image_cache_draw_buf_handlers &(LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers)
29 
30 /**********************
31  *      TYPEDEFS
32  **********************/
33 typedef struct {
34     uint8_t * mem;
35     uint32_t mem_size;
36 
37     char name[sizeof(HEAP_NAME) + 10]; /**< +10 characters to store task pid. */
38 
39     struct mm_heap_s * heap;
40     uint32_t heap_size;
41 
42     bool initialized;
43     bool independent_image_heap;
44 
45     lv_draw_buf_malloc_cb malloc_cb;
46     lv_draw_buf_free_cb free_cb;
47 } lv_nuttx_ctx_image_cache_t;
48 /**********************
49  *  STATIC PROTOTYPES
50  **********************/
51 
52 static void * malloc_cb(size_t size_bytes, lv_color_format_t color_format);
53 static void free_cb(void * draw_buf);
54 
55 /**********************
56  *  STATIC VARIABLES
57  **********************/
58 
59 /**********************
60  *      MACROS
61  **********************/
62 
63 /**********************
64  *   GLOBAL FUNCTIONS
65  **********************/
66 
lv_nuttx_image_cache_init(bool use_independent_image_heap)67 void lv_nuttx_image_cache_init(bool use_independent_image_heap)
68 {
69     lv_draw_buf_handlers_t * handlers = image_cache_draw_buf_handlers;
70 
71     ctx = lv_malloc_zeroed(sizeof(lv_nuttx_ctx_image_cache_t));
72     LV_ASSERT_MALLOC(ctx);
73 
74     ctx->malloc_cb = handlers->buf_malloc_cb;
75     ctx->free_cb = handlers->buf_free_cb;
76 
77     handlers->buf_malloc_cb = malloc_cb;
78     handlers->buf_free_cb = free_cb;
79 
80     ctx->initialized = false;
81     ctx->independent_image_heap = use_independent_image_heap;
82 }
83 
lv_nuttx_image_cache_deinit(void)84 void lv_nuttx_image_cache_deinit(void)
85 {
86     if(ctx->independent_image_heap == false) goto FREE_CONTEXT;
87     if(ctx->initialized == false) goto FREE_CONTEXT;
88 
89     mm_uninitialize(ctx->heap);
90     free(ctx->mem);
91 
92 FREE_CONTEXT:
93     lv_draw_buf_handlers_t * handlers = image_cache_draw_buf_handlers;
94     handlers->buf_malloc_cb = ctx->malloc_cb;
95     handlers->buf_free_cb = ctx->free_cb;
96     lv_free(ctx);
97 
98     ctx = NULL;
99 }
100 
101 /**********************
102  *   STATIC FUNCTIONS
103  **********************/
104 
defer_init(void)105 static bool defer_init(void)
106 {
107     if(ctx->mem != NULL && ctx->heap != NULL) {
108         return true;
109     }
110 
111     if(lv_image_cache_is_enabled() == false) {
112         LV_LOG_INFO("Image cache is not initialized yet. Skipping deferred initialization. Because max_size is 0.");
113         return false;
114     }
115 
116     ctx->mem_size = img_cache_p->max_size;
117     ctx->mem = malloc(ctx->mem_size);
118     LV_ASSERT_MALLOC(ctx->mem);
119 
120     if(ctx->mem == NULL) {
121         LV_LOG_ERROR("Failed to allocate memory for image cache");
122         ctx->initialized = false;
123         return false;
124     }
125 
126     lv_snprintf(ctx->name, sizeof(ctx->name), HEAP_NAME "[%-4" LV_PRIu32 "]", (uint32_t)gettid());
127 
128     ctx->heap = mm_initialize(
129                     ctx->name,
130                     ctx->mem,
131                     ctx->mem_size
132                 );
133 
134     struct mallinfo info  = mm_mallinfo(ctx->heap);
135     ctx->heap_size = info.arena;
136 
137     LV_LOG_USER("heap info:");
138     LV_LOG_USER("  heap: %p", ctx->heap);
139     LV_LOG_USER("  mem: %p", ctx->mem);
140     LV_LOG_USER("  mem_size: %" LV_PRIu32, ctx->mem_size);
141     LV_LOG_USER("  arena: %d", info.arena);
142     LV_LOG_USER("  ordblks: %d", info.ordblks);
143     LV_LOG_USER("  aordblks: %d", info.aordblks);
144     LV_LOG_USER("  mxordblk: %d", info.mxordblk);
145     LV_LOG_USER("  uordblks: %d", info.uordblks);
146     LV_LOG_USER("  fordblks: %d", info.fordblks);
147 
148     ctx->initialized = true;
149     return true;
150 }
151 
heap_memdump(struct mm_heap_s * heap)152 static void heap_memdump(struct mm_heap_s * heap)
153 {
154     struct mm_memdump_s dump = {
155         PID_MM_ALLOC,
156 #if CONFIG_MM_BACKTRACE >= 0
157         0,
158         ULONG_MAX
159 #endif
160     };
161 
162     mm_memdump(heap, &dump);
163 }
164 
malloc_cb(size_t size_bytes,lv_color_format_t color_format)165 static void * malloc_cb(size_t size_bytes, lv_color_format_t color_format)
166 {
167     LV_UNUSED(color_format);
168     if(ctx->independent_image_heap == true && ctx->initialized == false) {
169         if(defer_init() == false) return NULL;
170     }
171 
172     /*Allocate larger memory to be sure it can be aligned as needed*/
173     size_bytes += LV_DRAW_BUF_ALIGN - 1;
174     uint32_t cache_max_size = lv_cache_get_max_size(img_cache_p, NULL);
175 
176     if(lv_cache_is_enabled(img_cache_p) && size_bytes > cache_max_size) {
177         LV_LOG_ERROR("data size (%" LV_PRIu32 ") is larger than max size (%" LV_PRIu32 ")",
178                      (uint32_t)size_bytes,
179                      cache_max_size);
180         return NULL;
181     }
182 
183     while(1) {
184         void * mem = NULL;
185         if(ctx->independent_image_heap) {
186             mem = mm_malloc(ctx->heap, size_bytes);
187         }
188         else if((!lv_cache_is_enabled(img_cache_p)) || (lv_cache_get_size(img_cache_p, NULL) + size_bytes < cache_max_size)) {
189             mem = ctx->malloc_cb(size_bytes, color_format);
190         }
191         if(mem) return mem;
192         LV_LOG_INFO("appears to be out of memory. attempting to evict one cache entry. with allocated size %" LV_PRIu32,
193                     (uint32_t)size_bytes);
194         bool evict_res = lv_cache_evict_one(img_cache_p, NULL);
195         if(evict_res == false) {
196             LV_LOG_ERROR("failed to evict one cache entry");
197             heap_memdump(ctx->heap);
198             return NULL;
199         }
200     }
201 }
202 
free_cb(void * draw_buf)203 static void free_cb(void * draw_buf)
204 {
205     if(ctx->independent_image_heap == true) {
206         mm_free(ctx->heap, draw_buf);
207     }
208     else {
209         ctx->free_cb(draw_buf);
210     }
211 }
212 
213 #endif /* LV_USE_NUTTX */
214