Skip to content

Commit e3a5365

Browse files
committed
PicoVector: Add tracked memory alloc, track the node buffer.
With a low GC threshold the node buffer was being collected (despite guards against this) and causing further drawing operations to be corrupted. Add a new tracked malloc method and use it for node buffers and font data. Tweak pp_poly_add_path to accept a point count, avoiding many successive rellocs when dealing with a known quantity of points (copying from an af_glyph_t.)
1 parent 6efa5cf commit e3a5365

File tree

8 files changed

+149
-27
lines changed

8 files changed

+149
-27
lines changed

libraries/pico_vector/af-memory.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@
33
#include <stdio.h>
44
#include <stdlib.h>
55

6+
void *af_tracked_malloc(size_t size) {
7+
return malloc(size);
8+
}
9+
10+
void *af_tracked_realloc(void *p, size_t size) {
11+
return realloc(p, size);
12+
}
13+
14+
void af_tracked_free(void *p) {
15+
free(p);
16+
}
17+
618
void *af_malloc(size_t size) {
719
return malloc(size);
820
}

libraries/pico_vector/af-memory.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
#ifdef __cplusplus
44
extern "C" {
55
#endif
6+
void *af_tracked_malloc(size_t size);
7+
void *af_tracked_realloc(void *p, size_t size);
8+
void af_tracked_free(void *p);
9+
610
void *af_malloc(size_t size);
711
void *af_realloc(void *p, size_t size);
812
void af_free(void *p);

libraries/pico_vector/alright-fonts.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040
#define PP_MALLOC(size) AF_MALLOC(size)
4141
#define PP_REALLOC(p, size) AF_REALLOC(p, size)
4242
#define PP_FREE(p) AF_FREE(p)
43+
44+
#define PP_TRACKED_MALLOC(size) AF_TRACKED_MALLOC(size)
45+
#define PP_TRACKED_REALLOC(p, size) AF_TRACKED_REALLOC(p, size)
46+
#define PP_TRACKED_FREE(p) AF_TRACKED_FREE(p)
4347
#endif // PP_MALLOC
4448
#endif // AF_MALLOC
4549

@@ -157,7 +161,7 @@ bool af_load_font_file(AF_FILE file, af_face_t *face) {
157161
size_t point_buffer_size = sizeof(af_point_t) * point_count;
158162

159163
// allocate buffer to store font glyph, path, and point data
160-
uint8_t *buffer = (uint8_t *)AF_MALLOC(glyph_buffer_size + path_buffer_size + point_buffer_size);
164+
uint8_t *buffer = (uint8_t *)AF_TRACKED_MALLOC(glyph_buffer_size + path_buffer_size + point_buffer_size);
161165

162166
if(!buffer) {
163167
return false; // failed memory allocation
@@ -224,7 +228,7 @@ void af_render_glyph(af_glyph_t* glyph, af_text_metrics_t *tm) {
224228

225229
pp_poly_t *poly = pp_poly_new();
226230
for(uint32_t i = 0; i < glyph->path_count; i++) {
227-
pp_path_t *path = pp_poly_add_path(poly);
231+
pp_path_t *path = pp_poly_add_path(poly, glyph->paths[i].point_count);
228232
for(uint32_t j = 0; j < glyph->paths[i].point_count; j++) {
229233
pp_path_add_point(path, {
230234
glyph->paths[i].points[j].x,

libraries/pico_vector/pico_vector.hpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,18 @@
1010
#define AF_REALLOC(p, size) af_realloc(p, size)
1111
#define AF_FREE(p) af_free(p)
1212

13+
#define AF_TRACKED_MALLOC(size) af_tracked_malloc(size)
14+
#define AF_TRACKED_REALLOC(p, size) af_tracked_realloc(p, size)
15+
#define AF_TRACKED_FREE(p) af_tracked_free(p)
16+
1317
#define PP_MALLOC(size) af_malloc(size)
1418
#define PP_REALLOC(p, size) af_realloc(p, size)
1519
#define PP_FREE(p) af_free(p)
1620

21+
#define PP_TRACKED_MALLOC(size) af_tracked_malloc(size)
22+
#define PP_TRACKED_REALLOC(p, size) af_tracked_realloc(p, size)
23+
#define PP_TRACKED_FREE(p) af_tracked_free(p)
24+
1725
#define AF_DEBUG(...) af_debug(__VA_ARGS__)
1826

1927
#include "pretty-poly.h"
@@ -108,10 +116,10 @@ namespace pimoroni {
108116

109117
bool set_font(std::string_view font_path, unsigned int font_size) {
110118
if(text_metrics.face) {
111-
af_free(text_metrics.face->glyphs);
112-
af_free(text_metrics.face);
119+
af_tracked_free(text_metrics.face->glyphs);
120+
af_tracked_free(text_metrics.face);
113121
}
114-
text_metrics.face = (af_face_t *)af_malloc(sizeof(af_face_t));
122+
text_metrics.face = (af_face_t *)af_tracked_malloc(sizeof(af_face_t));
115123
//bool result = text_metrics.face.load(font_path);
116124
void* font = fileio_open(font_path.data());
117125
bool result = af_load_font_file(font, text_metrics.face);
@@ -123,10 +131,10 @@ namespace pimoroni {
123131

124132
bool set_font(void* font, unsigned int font_size) {
125133
if(text_metrics.face) {
126-
af_free(text_metrics.face->glyphs);
127-
af_free(text_metrics.face);
134+
af_tracked_free(text_metrics.face->glyphs);
135+
af_tracked_free(text_metrics.face);
128136
}
129-
text_metrics.face = (af_face_t *)af_malloc(sizeof(af_face_t));
137+
text_metrics.face = (af_face_t *)af_tracked_malloc(sizeof(af_face_t));
130138
bool result = af_load_font_file(font, text_metrics.face);
131139

132140
set_font_size(font_size);

libraries/pico_vector/pretty-poly.h

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ typedef struct {
114114
pp_poly_t *pp_poly_new();
115115
void pp_poly_free(pp_poly_t *poly);
116116
pp_path_t* pp_poly_tail_path(pp_poly_t *p);
117-
pp_path_t* pp_poly_add_path(pp_poly_t *p);
117+
pp_path_t* pp_poly_add_path(pp_poly_t *p, int32_t points = -1);
118118
pp_rect_t pp_poly_bounds(pp_poly_t *p);
119119
int pp_poly_path_count(pp_poly_t *p);
120120
void pp_poly_merge(pp_poly_t *p, pp_poly_t *m);
@@ -154,6 +154,12 @@ void pp_deinit();
154154
#define PP_FREE(p) free(p)
155155
#endif
156156

157+
#ifndef PP_TRACKED_MALLOC
158+
#define PP_TRACKED_MALLOC(size) malloc(size)
159+
#define PP_TRACKED_REALLOC(p, size) realloc(p, size)
160+
#define PP_TRACKED_FREE(p) free(p)
161+
#endif
162+
157163
#ifdef PP_DEBUG
158164
#define debug(...) printf(__VA_ARGS__)
159165
#else
@@ -270,10 +276,10 @@ pp_poly_t *pp_poly_new() {
270276
return poly;
271277
}
272278

273-
pp_path_t *pp_path_new() {
279+
pp_path_t *pp_path_new(int32_t points = -1) {
274280
pp_path_t *path = (pp_path_t *)PP_MALLOC(sizeof(pp_path_t));
275281
memset(path, 0, sizeof(pp_path_t));
276-
path->storage = 8;
282+
path->storage = points > -1 ? points : 8;
277283
path->points = (pp_point_t *)PP_MALLOC(sizeof(pp_point_t) * path->storage);
278284
return path;
279285
}
@@ -313,8 +319,8 @@ int pp_poly_path_count(pp_poly_t *poly) {
313319
return i;
314320
}
315321

316-
pp_path_t* pp_poly_add_path(pp_poly_t *poly) {
317-
pp_path_t *path = pp_path_new();
322+
pp_path_t* pp_poly_add_path(pp_poly_t *poly, int32_t points) {
323+
pp_path_t *path = pp_path_new(points);
318324

319325
if(!poly->paths) {
320326
poly->paths = path;
@@ -411,13 +417,13 @@ uint8_t _pp_alpha_map_x16[17] = {0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160,
411417

412418
void pp_init(uint32_t max_nodes_per_scanline) {
413419
_pp_max_nodes_per_scanline = max_nodes_per_scanline;
414-
pp_nodes = (int32_t *)PP_MALLOC(PP_TILE_BUFFER_SIZE * 4 * max_nodes_per_scanline * 2 * sizeof(int32_t));
415-
pp_node_counts = (uint32_t *)PP_MALLOC(PP_TILE_BUFFER_SIZE * 4 * sizeof(uint32_t));
420+
pp_nodes = (int32_t *)PP_TRACKED_MALLOC(PP_TILE_BUFFER_SIZE * 4 * max_nodes_per_scanline * 2 * sizeof(int32_t));
421+
pp_node_counts = (uint32_t *)PP_TRACKED_MALLOC(PP_TILE_BUFFER_SIZE * 4 * sizeof(uint32_t));
416422
}
417423

418424
void pp_deinit() {
419-
PP_FREE(pp_nodes);
420-
PP_FREE(pp_node_counts);
425+
PP_TRACKED_FREE(pp_nodes);
426+
PP_TRACKED_FREE(pp_node_counts);
421427
}
422428

423429
void pp_clip(int32_t x, int32_t y, int32_t w, int32_t h) {

micropython/modules/picovector/picovector.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ static MP_DEFINE_CONST_FUN_OBJ_1(VECTOR_get_transform_obj, VECTOR_get_transform)
100100

101101
static MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_draw_obj, VECTOR_draw);
102102

103+
/* Stats */
104+
105+
static MP_DEFINE_CONST_FUN_OBJ_1(MALLOC_get_stats_obj, MALLOC_get_stats);
106+
103107
static const mp_rom_map_elem_t VECTOR_locals_dict_table[] = {
104108
{ MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&VECTOR_text_obj) },
105109
{ MP_ROM_QSTR(MP_QSTR_measure_text), MP_ROM_PTR(&VECTOR_measure_text_obj) },
@@ -116,6 +120,8 @@ static const mp_rom_map_elem_t VECTOR_locals_dict_table[] = {
116120
{ MP_ROM_QSTR(MP_QSTR_get_transform), MP_ROM_PTR(&VECTOR_get_transform_obj) },
117121

118122
{ MP_ROM_QSTR(MP_QSTR_draw), MP_ROM_PTR(&VECTOR_draw_obj) },
123+
124+
{ MP_ROM_QSTR(MP_QSTR_malloc_get_stats), MP_ROM_PTR(&MALLOC_get_stats_obj) },
119125
};
120126

121127
static MP_DEFINE_CONST_DICT(VECTOR_locals_dict, VECTOR_locals_dict_table);

micropython/modules/picovector/picovector.cpp

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,28 +62,83 @@ void af_debug(const char *fmt, ...) {
6262
#define mp_picovector_get_point_type mp_obj_get_float
6363
#define mp_picovector_set_point_type mp_obj_new_float
6464

65+
uint32_t _af_malloc_count = 0;
66+
uint32_t _af_malloc_bytes = 0;
67+
uint32_t _af_realloc_count = 0;
68+
69+
uint32_t _af_tracked_malloc_count = 0;
70+
uint32_t _af_tracked_malloc_bytes = 0;
71+
uint32_t _af_tracked_realloc_count = 0;
72+
6573

6674
void *af_malloc(size_t size) {
67-
//mp_printf(&mp_plat_print, "af_malloc %lu\n", size);
68-
//__printf_debug_flush();
69-
//void *addr = m_tracked_calloc(sizeof(uint8_t), size);
75+
_af_malloc_count++;
76+
_af_malloc_bytes += size;
7077
void *addr = m_malloc(size);
71-
//mp_printf(&mp_plat_print, "addr %lu\n", addr);
72-
//__printf_debug_flush();
7378
return addr;
7479
}
7580

81+
7682
void *af_realloc(void *p, size_t size) {
77-
return m_realloc(p, size);
83+
_af_realloc_count++;
84+
void *addr = m_realloc(p, size);
85+
return addr;
7886
}
7987

8088
void af_free(void *p) {
81-
//mp_printf(&mp_plat_print, "af_free\n");
82-
//__printf_debug_flush();
83-
//m_tracked_free(p);
8489
m_free(p);
8590
}
8691

92+
void *af_tracked_malloc(size_t size) {
93+
_af_tracked_malloc_count++;
94+
_af_tracked_malloc_bytes += size;
95+
96+
// Allocate an extra sizeof(size_t) bytes
97+
size_t *addr = (size_t *)m_tracked_calloc(sizeof(uint8_t), size + sizeof(size_t));
98+
// Tag our memory with its allocated size
99+
*addr = size;
100+
// Skip past the size_t size
101+
addr++;
102+
103+
#if DEBUG
104+
mp_printf(&mp_plat_print, "af_tracked_malloc %lu %p : %p\n", size, addr, (uint8_t *)addr + size);
105+
__printf_debug_flush();
106+
#endif
107+
108+
return (void *)addr;
109+
}
110+
111+
void *af_tracked_realloc(void *p, size_t size) {
112+
_af_tracked_realloc_count++;
113+
114+
void *addr = af_tracked_malloc(size);
115+
size_t old_size = *((size_t *)p - 1);
116+
memcpy(addr, p, std::min(old_size, size));
117+
118+
#if DEBUG
119+
mp_printf(&mp_plat_print, "af_tracked_realloc %lu -> %lu, %p -> %p : %p\n", old_size, size, p, addr, (uint8_t *)addr + size);
120+
__printf_debug_flush();
121+
#endif
122+
123+
af_tracked_free(p);
124+
125+
return addr;
126+
}
127+
128+
void af_tracked_free(void *p) {
129+
size_t *pp = (size_t *)p; // Convert our void pointer to size_t* so we can read the size marker
130+
pp--; // Skip back to get our real start
131+
132+
#if DEBUG
133+
size_t size = *pp; // First "size_t" should be the allocated size
134+
mp_printf(&mp_plat_print, "af_tracked_free %p (size %d)\n", p, size);
135+
__printf_debug_flush();
136+
#endif
137+
138+
m_tracked_free((void *)pp);
139+
//m_free(p);
140+
}
141+
87142
void* fileio_open(const char *filename) {
88143
mp_obj_t fn = mp_obj_new_str(filename, (mp_uint_t)strlen(filename));
89144

@@ -857,4 +912,27 @@ mp_obj_t VECTOR_draw(mp_obj_t self_in, mp_obj_t poly_in) {
857912
return mp_const_none;
858913
}
859914

915+
mp_obj_t MALLOC_get_stats(mp_obj_t self_in) {
916+
(void)self_in;
917+
918+
mp_obj_t tuple[6];
919+
tuple[0] = mp_obj_new_int(_af_malloc_count);
920+
tuple[1] = mp_obj_new_int(_af_realloc_count);
921+
tuple[2] = mp_obj_new_int(_af_malloc_bytes);
922+
923+
tuple[3] = mp_obj_new_int(_af_tracked_malloc_count);
924+
tuple[4] = mp_obj_new_int(_af_tracked_realloc_count);
925+
tuple[5] = mp_obj_new_int(_af_tracked_malloc_bytes);
926+
927+
_af_malloc_count = 0;
928+
_af_realloc_count = 0;
929+
_af_malloc_bytes = 0;
930+
931+
_af_tracked_malloc_count = 0;
932+
_af_tracked_realloc_count = 0;
933+
_af_tracked_malloc_bytes = 0;
934+
935+
return mp_obj_new_tuple(6, tuple);
936+
}
937+
860938
}

micropython/modules/picovector/picovector.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,8 @@ extern mp_obj_t VECTOR_get_transform(mp_obj_t self_in);
5151

5252
extern mp_obj_t VECTOR_draw(mp_obj_t self_in, mp_obj_t poly_in);
5353
extern mp_obj_t VECTOR_rotate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
54-
extern mp_obj_t VECTOR_translate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
54+
extern mp_obj_t VECTOR_translate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
55+
56+
/* Stats */
57+
58+
extern mp_obj_t MALLOC_get_stats(mp_obj_t self_in);

0 commit comments

Comments
 (0)