diff --git a/components/micropython/usermodule/mp_sys_display.c b/components/micropython/usermodule/mp_sys_display.c index fbc7cdca301d8273114c7b770709f945e32b8c5c..e0c4cbaf831b81e873f4cf0bc8b072a23dc4397d 100644 --- a/components/micropython/usermodule/mp_sys_display.c +++ b/components/micropython/usermodule/mp_sys_display.c @@ -36,18 +36,76 @@ STATIC mp_obj_t mp_set_backlight(mp_obj_t percent_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_set_backlight_obj, mp_set_backlight); -static Ctx *global_ctx = NULL; -STATIC mp_obj_t mp_get_ctx(void) { - if (!global_ctx) global_ctx = st3m_ctx(0); - if (global_ctx == NULL) return mp_const_none; - return mp_ctx_from_ctx(global_ctx); +STATIC mp_obj_t mp_set_gfx_mode(mp_obj_t mode) { + st3m_set_gfx_mode(mp_obj_get_int(mode)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_set_gfx_mode_obj, mp_set_gfx_mode); + +STATIC mp_obj_t mp_get_gfx_mode(void) { + return mp_obj_new_int(st3m_get_gfx_mode()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_get_gfx_mode_obj, mp_get_gfx_mode); + +STATIC mp_obj_t mp_set_palette(mp_obj_t pal_in) { + size_t count = mp_obj_get_int(mp_obj_len(pal_in)); + uint8_t *pal = m_malloc(count); + for (size_t i = 0; i < count; i++) { + pal[i] = mp_obj_get_int( + mp_obj_subscr(pal_in, mp_obj_new_int(i), MP_OBJ_SENTINEL)); + } + st3m_gfx_set_palette(pal, count / 3); +#if MICROPY_MALLOC_USES_ALLOCATED_SIZE + m_free(pal, count); +#else + m_free(pal); +#endif + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_set_palette_obj, mp_set_palette); + +STATIC mp_obj_t mp_ctx(mp_obj_t mode_in) { + int mode = mp_obj_get_int(mode_in); + Ctx *ctx = NULL; + switch (mode) { + case 0: + case 16: + ctx = st3m_ctx(0); + if (ctx == NULL) return mp_const_none; + break; + case st3m_gfx_overlay: + case 8: + case 24: + case 32: + ctx = st3m_overlay_ctx(); + break; + } + return mp_ctx_from_ctx(ctx); } -STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_get_ctx_obj, mp_get_ctx); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_ctx_obj, mp_ctx); -STATIC mp_obj_t mp_get_overlay_ctx(void) { - return mp_ctx_from_ctx(st3m_overlay_ctx()); +STATIC mp_obj_t mp_fb(mp_obj_t mode_in) { + int mode = mp_obj_get_int(mode_in); + int size = 240 * 240; + switch (mode) { + case 0: + size *= 2; + mode = 16; + break; + case 16: + size *= 2; + break; + case 24: + size *= 3; + break; + case st3m_gfx_overlay: + case 32: + size *= 4; + break; + } + return mp_obj_new_bytearray_by_ref(size, st3m_gfx_fb(mode)); } -STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_get_overlay_ctx_obj, mp_get_overlay_ctx); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_fb_obj, mp_fb); STATIC mp_obj_t mp_update(mp_obj_t ctx_in) { mp_ctx_obj_t *self = MP_OBJ_TO_PTR(ctx_in); @@ -55,10 +113,7 @@ STATIC mp_obj_t mp_update(mp_obj_t ctx_in) { mp_raise_ValueError("not a ctx"); return mp_const_none; } - if (global_ctx) { - st3m_ctx_end_frame(self->ctx); - global_ctx = NULL; - } + st3m_ctx_end_frame(self->ctx); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_update_obj, mp_update); @@ -84,9 +139,11 @@ STATIC const mp_rom_map_elem_t mp_module_sys_display_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_set_backlight), MP_ROM_PTR(&mp_set_backlight_obj) }, { MP_ROM_QSTR(MP_QSTR_overlay_clip), MP_ROM_PTR(&mp_overlay_clip_obj) }, { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&mp_update_obj) }, - { MP_ROM_QSTR(MP_QSTR_get_ctx), MP_ROM_PTR(&mp_get_ctx_obj) }, - { MP_ROM_QSTR(MP_QSTR_get_overlay_ctx), - MP_ROM_PTR(&mp_get_overlay_ctx_obj) }, + { MP_ROM_QSTR(MP_QSTR_fb), MP_ROM_PTR(&mp_fb_obj) }, + { MP_ROM_QSTR(MP_QSTR_ctx), MP_ROM_PTR(&mp_ctx_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_gfx_mode), MP_ROM_PTR(&mp_set_gfx_mode_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_palette), MP_ROM_PTR(&mp_set_palette_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_gfx_mode), MP_ROM_PTR(&mp_get_gfx_mode_obj) }, { MP_ROM_QSTR(MP_QSTR_fps), MP_ROM_PTR(&mp_fps_obj) }, }; diff --git a/components/st3m/st3m_gfx.c b/components/st3m/st3m_gfx.c index 2e63ed758c4c43130d9813ab7df9c5574a164694..48048d0c8138603109932a354333d12006d9861e 100644 --- a/components/st3m/st3m_gfx.c +++ b/components/st3m/st3m_gfx.c @@ -19,75 +19,55 @@ #include "st3m_counter.h" #include "st3m_version.h" -#define ST3M_GFX_NBUFFERS 1 -#define ST3M_GFX_NCTX 1 - -// A framebuffer descriptor, pointing at a framebuffer. -typedef struct { - // The numeric ID of this descriptor. - int num; - // SPIRAM buffer. - uint16_t buffer[240 * 240]; - Ctx *ctx; -} st3m_framebuffer_desc_t; +static EXT_RAM_BSS_ATTR uint16_t fb[240 * 240]; // Get a free drawlist ctx to draw into. // // ticks_to_wait can be used to limit the time to wait for a free ctx // descriptor, or portDELAY_MAX can be specified to wait forever. If the timeout // expires, NULL will be returned. -static st3m_ctx_desc_t *st3m_gfx_drawctx_free_get(TickType_t ticks_to_wait); +static Ctx *st3m_gfx_drawctx_free_get(TickType_t ticks_to_wait); // Submit a filled ctx descriptor to the rasterization pipeline. -static void st3m_gfx_drawctx_pipe_put(st3m_ctx_desc_t *desc); +static void st3m_gfx_drawctx_pipe_put(void); static const char *TAG = "st3m-gfx"; -// Framebuffer descriptors, containing framebuffer and ctx for each of the -// framebuffers. -// -// These live in SPIRAM, as we don't have enough space in SRAM/IRAM. -EXT_RAM_BSS_ATTR static st3m_framebuffer_desc_t - framebuffer_descs[ST3M_GFX_NBUFFERS]; - #define OVERLAY_WIDTH 240 #define OVERLAY_HEIGHT 240 #define OVERLAY_X 0 #define OVERLAY_Y 0 -static int _st3m_overlay_y1 = 0; -static int _st3m_overlay_x1 = 0; -static int _st3m_overlay_y0 = 0; -static int _st3m_overlay_x0 = 0; +static st3m_gfx_mode _st3m_gfx_mode = st3m_gfx_default; EXT_RAM_BSS_ATTR static uint8_t st3m_overlay_fb[OVERLAY_WIDTH * OVERLAY_HEIGHT * 4]; EXT_RAM_BSS_ATTR uint16_t st3m_overlay_backup[OVERLAY_WIDTH * OVERLAY_HEIGHT]; -static Ctx *_st3m_overlay_ctx = NULL; -static st3m_ctx_desc_t dctx_descs[ST3M_GFX_NCTX]; +// drawlist ctx +static Ctx *user_ctx = NULL; -// Queue of free framebuffer descriptors, written into by crtc once rendered, -// read from by rasterizer when new frame starts. -static QueueHandle_t framebuffer_freeq = NULL; -// Queue of framebuffer descriptors to blit out. -static QueueHandle_t framebuffer_blitq = NULL; +// RGB565_byteswapped framebuffer ctx - our global ctx +// user_ctx gets replayed on this +static Ctx *fb_ctx = NULL; -static st3m_counter_rate_t blit_rate; -static st3m_counter_timer_t blit_read_time; -static st3m_counter_timer_t blit_work_time; -static st3m_counter_timer_t blit_write_time; +// RGBA8 overlay ctx +static Ctx *overlay_ctx = NULL; + +// corner pixel coordinates for overlay clip +static int _st3m_overlay_y1 = 0; +static int _st3m_overlay_x1 = 0; +static int _st3m_overlay_y0 = 0; +static int _st3m_overlay_x0 = 0; -static QueueHandle_t dctx_freeq = NULL; -static QueueHandle_t dctx_rastq = NULL; +static float smoothed_fps = 0.0f; +static QueueHandle_t user_ctx_freeq = NULL; +static QueueHandle_t user_ctx_rastq = NULL; static st3m_counter_rate_t rast_rate; -static st3m_counter_timer_t rast_read_fb_time; -static st3m_counter_timer_t rast_read_dctx_time; -static st3m_counter_timer_t rast_work_time; -static st3m_counter_timer_t rast_write_time; +static TaskHandle_t graphics_task; -static TaskHandle_t rast_task; +////////////////////////// // Attempt to receive from a queue forever, but log an error if it takes longer // than two seconds to get something. @@ -111,46 +91,129 @@ void st3m_ctx_merge_overlay(uint16_t *fb, uint8_t *overlay, int ostride, void st3m_ctx_unmerge_overlay(uint16_t *fb, uint16_t *overlay_backup, int x0, int y0, int w, int h); -static float smoothed_fps = 0.0f; - float st3m_gfx_fps(void) { return smoothed_fps; } -static void st3m_gfx_rast_task(void *_arg) { +void st3m_gfx_set_palette(uint8_t *pal_in, int count) { + if (count > 256) count = 256; + if (count < 0) count = 0; + uint8_t *pal = + ((uint8_t *)st3m_overlay_fb) + sizeof(st3m_overlay_fb) - 256 * 3; + for (int i = 0; i < count * 3; i++) pal[i] = pal_in[i]; +} + +static void grayscale_palette(void) { + uint8_t pal[256 * 3]; + for (int i = 0; i < 256; i++) { + pal[i * 3 + 0] = i; + pal[i * 3 + 1] = i; + pal[i * 3 + 2] = i; + } + st3m_gfx_set_palette(pal, 256); +} + +static void fire_palette(void) { + uint8_t pal[256 * 3]; + for (int i = 0; i < 256; i++) { + pal[i * 3 + 0] = i; + pal[i * 3 + 1] = (i / 255.0) * (i / 255.0) * 255; + pal[i * 3 + 2] = (i / 255.0) * (i / 255.0) * (i / 255.0) * 255; + } + st3m_gfx_set_palette(pal, 256); +} + +static void ice_palette(void) { + uint8_t pal[256 * 3]; + for (int i = 0; i < 256; i++) { + pal[i * 3 + 0] = (i / 255.0) * (i / 255.0) * (i / 255.0) * 255; + pal[i * 3 + 1] = (i / 255.0) * (i / 255.0) * 255; + pal[i * 3 + 2] = i; + } + st3m_gfx_set_palette(pal, 256); +} + +void st3m_set_gfx_mode(st3m_gfx_mode mode) { + memset(fb, 0, sizeof(fb)); + memset(st3m_overlay_fb, 0, sizeof(st3m_overlay_fb)); + + switch ((int)mode) { + case 8: + fire_palette(); + break; + case 9: + ice_palette(); + mode = 8; + break; + case 10: + grayscale_palette(); + mode = 8; + break; + } + _st3m_gfx_mode = mode; +} +st3m_gfx_mode st3m_get_gfx_mode(void) { return _st3m_gfx_mode; } +uint8_t *st3m_gfx_fb(st3m_gfx_mode mode) { + switch (_st3m_gfx_mode) { + case st3m_gfx_default: + case st3m_gfx_16bpp: + return (uint8_t *)fb; + case st3m_gfx_32bpp: + case st3m_gfx_24bpp: + case st3m_gfx_8bpp: + case st3m_gfx_overlay: + return st3m_overlay_fb; + } + return st3m_overlay_fb; +} + +static void st3m_gfx_task(void *_arg) { (void)_arg; while (true) { int descno = 0; - st3m_framebuffer_desc_t *fb = &framebuffer_descs[descno]; - - xQueueReceiveNotifyStarved(dctx_rastq, &descno, - "rast task starved (dctx)!"); - st3m_ctx_desc_t *draw = &dctx_descs[descno]; - ctx_set_textureclock(framebuffer_descs[0].ctx, - ctx_textureclock(framebuffer_descs[0].ctx) + 1); + xQueueReceiveNotifyStarved(user_ctx_rastq, &descno, + "rast task starved (user_ctx)!"); - ctx_render_ctx(draw->ctx, fb->ctx); - ctx_drawlist_clear(draw->ctx); + ctx_set_textureclock(fb_ctx, ctx_textureclock(fb_ctx) + 1); st3m_overlay_ctx(); - if (_st3m_overlay_y1 != _st3m_overlay_y0) - st3m_ctx_merge_overlay( - framebuffer_descs[descno].buffer, - st3m_overlay_fb + - 4 * ((_st3m_overlay_y0 - OVERLAY_Y) * OVERLAY_WIDTH + - (_st3m_overlay_x0 - OVERLAY_X)), - OVERLAY_WIDTH * 4, st3m_overlay_backup, _st3m_overlay_x0, - _st3m_overlay_y0, _st3m_overlay_x1 - _st3m_overlay_x0 + 1, - _st3m_overlay_y1 - _st3m_overlay_y0 + 1); - flow3r_bsp_display_send_fb(framebuffer_descs[descno].buffer, 16); - if (_st3m_overlay_y1 != _st3m_overlay_y0) - st3m_ctx_unmerge_overlay(framebuffer_descs[descno].buffer, - st3m_overlay_backup, _st3m_overlay_x0, - _st3m_overlay_y0, - _st3m_overlay_x1 - _st3m_overlay_x0 + 1, - _st3m_overlay_y1 - _st3m_overlay_y0 + 1); - - xQueueSend(dctx_freeq, &descno, portMAX_DELAY); + + switch (_st3m_gfx_mode) { + case st3m_gfx_8bpp: + case st3m_gfx_24bpp: + case st3m_gfx_32bpp: + flow3r_bsp_display_send_fb(st3m_overlay_fb, _st3m_gfx_mode); + break; + case st3m_gfx_overlay: + flow3r_bsp_display_send_fb(st3m_overlay_fb, 32); + break; + case st3m_gfx_default: + ctx_render_ctx(user_ctx, fb_ctx); + if (_st3m_overlay_y1 != _st3m_overlay_y0) + st3m_ctx_merge_overlay( + fb, + st3m_overlay_fb + 4 * ((_st3m_overlay_y0 - OVERLAY_Y) * + OVERLAY_WIDTH + + (_st3m_overlay_x0 - OVERLAY_X)), + OVERLAY_WIDTH * 4, st3m_overlay_backup, + _st3m_overlay_x0, _st3m_overlay_y0, + _st3m_overlay_x1 - _st3m_overlay_x0 + 1, + _st3m_overlay_y1 - _st3m_overlay_y0 + 1); + flow3r_bsp_display_send_fb(fb, 16); + if (_st3m_overlay_y1 != _st3m_overlay_y0) + st3m_ctx_unmerge_overlay( + fb, st3m_overlay_backup, _st3m_overlay_x0, + _st3m_overlay_y0, + _st3m_overlay_x1 - _st3m_overlay_x0 + 1, + _st3m_overlay_y1 - _st3m_overlay_y0 + 1); + break; + case st3m_gfx_16bpp: + flow3r_bsp_display_send_fb(fb, 16); + break; + } + ctx_drawlist_clear(user_ctx); + + xQueueSend(user_ctx_freeq, &descno, portMAX_DELAY); st3m_counter_rate_sample(&rast_rate); float rate = 1000000.0 / st3m_counter_rate_average(&rast_rate); smoothed_fps = smoothed_fps * 0.9 + 0.1 * rate; @@ -338,181 +401,139 @@ void st3m_gfx_show_textview(st3m_gfx_textview_t *tv) { return; } - st3m_ctx_desc_t *target = st3m_gfx_drawctx_free_get(portMAX_DELAY); + st3m_gfx_drawctx_free_get(portMAX_DELAY); - ctx_save(target->ctx); + ctx_save(user_ctx); // Draw background. - ctx_rgb(target->ctx, 0, 0, 0); - ctx_rectangle(target->ctx, -120, -120, 240, 240); - ctx_fill(target->ctx); + ctx_rgb(user_ctx, 0, 0, 0); + ctx_rectangle(user_ctx, -120, -120, 240, 240); + ctx_fill(user_ctx); - st3m_gfx_flow3r_logo(target->ctx, 0, -30, 150); + st3m_gfx_flow3r_logo(user_ctx, 0, -30, 150); int y = 20; - ctx_gray(target->ctx, 1.0); - ctx_text_align(target->ctx, CTX_TEXT_ALIGN_CENTER); - ctx_text_baseline(target->ctx, CTX_TEXT_BASELINE_MIDDLE); - ctx_font_size(target->ctx, 20.0); + ctx_gray(user_ctx, 1.0); + ctx_text_align(user_ctx, CTX_TEXT_ALIGN_CENTER); + ctx_text_baseline(user_ctx, CTX_TEXT_BASELINE_MIDDLE); + ctx_font_size(user_ctx, 20.0); // Draw title, if any. if (tv->title != NULL) { - ctx_move_to(target->ctx, 0, y); - ctx_text(target->ctx, tv->title); + ctx_move_to(user_ctx, 0, y); + ctx_text(user_ctx, tv->title); y += 20; } - ctx_font_size(target->ctx, 15.0); - ctx_gray(target->ctx, 0.8); + ctx_font_size(user_ctx, 15.0); + ctx_gray(user_ctx, 0.8); // Draw messages. const char **lines = tv->lines; if (lines != NULL) { while (*lines != NULL) { const char *text = *lines++; - ctx_move_to(target->ctx, 0, y); - ctx_text(target->ctx, text); + ctx_move_to(user_ctx, 0, y); + ctx_text(user_ctx, text); y += 15; } } // Draw version. - ctx_font_size(target->ctx, 15.0); - ctx_gray(target->ctx, 0.6); - ctx_move_to(target->ctx, 0, 100); - ctx_text(target->ctx, st3m_version); + ctx_font_size(user_ctx, 15.0); + ctx_gray(user_ctx, 0.6); + ctx_move_to(user_ctx, 0, 100); + ctx_text(user_ctx, st3m_version); - ctx_restore(target->ctx); + ctx_restore(user_ctx); - st3m_gfx_drawctx_pipe_put(target); + st3m_gfx_drawctx_pipe_put(); } void st3m_gfx_init(void) { // Make sure we're not being re-initialized. - assert(framebuffer_freeq == NULL); - - st3m_counter_rate_init(&blit_rate); - st3m_counter_timer_init(&blit_read_time); - st3m_counter_timer_init(&blit_work_time); - st3m_counter_timer_init(&blit_write_time); st3m_counter_rate_init(&rast_rate); - st3m_counter_timer_init(&rast_read_fb_time); - st3m_counter_timer_init(&rast_read_dctx_time); - st3m_counter_timer_init(&rast_work_time); - st3m_counter_timer_init(&rast_write_time); flow3r_bsp_display_init(); - // Create framebuffer queues. - framebuffer_freeq = xQueueCreate(ST3M_GFX_NBUFFERS + 1, sizeof(int)); - assert(framebuffer_freeq != NULL); - framebuffer_blitq = xQueueCreate(ST3M_GFX_NBUFFERS + 1, sizeof(int)); - assert(framebuffer_blitq != NULL); - // Create drawlist ctx queues. - dctx_freeq = xQueueCreate(ST3M_GFX_NCTX + 1, sizeof(int)); - assert(dctx_freeq != NULL); - dctx_rastq = xQueueCreate(ST3M_GFX_NCTX + 1, sizeof(int)); - assert(dctx_rastq != NULL); - - for (int i = 0; i < ST3M_GFX_NBUFFERS; i++) { - // Setup framebuffer descriptor. - st3m_framebuffer_desc_t *fb_desc = &framebuffer_descs[i]; - fb_desc->num = i; - fb_desc->ctx = ctx_new_for_framebuffer( - fb_desc->buffer, FLOW3R_BSP_DISPLAY_WIDTH, - FLOW3R_BSP_DISPLAY_HEIGHT, FLOW3R_BSP_DISPLAY_WIDTH * 2, - CTX_FORMAT_RGB565_BYTESWAPPED); - if (i) { - ctx_set_texture_source(fb_desc->ctx, framebuffer_descs[0].ctx); - ctx_set_texture_cache(fb_desc->ctx, framebuffer_descs[0].ctx); - } - assert(fb_desc->ctx != NULL); - // translate x and y by 120 px to have (0,0) at center of the screen - int32_t offset_x = FLOW3R_BSP_DISPLAY_WIDTH / 2; - int32_t offset_y = FLOW3R_BSP_DISPLAY_HEIGHT / 2; - ctx_apply_transform(fb_desc->ctx, 1, 0, offset_x, 0, 1, offset_y, 0, 0, - 1); - - // Push descriptor to freeq. - BaseType_t res = xQueueSend(framebuffer_freeq, &i, 0); - assert(res == pdTRUE); - } - - for (int i = 0; i < ST3M_GFX_NCTX; i++) { - // Setup dctx descriptor. - st3m_ctx_desc_t *dctx_desc = &dctx_descs[i]; - dctx_desc->num = i; - dctx_desc->ctx = ctx_new_drawlist(FLOW3R_BSP_DISPLAY_WIDTH, - FLOW3R_BSP_DISPLAY_HEIGHT); - ctx_set_texture_cache(dctx_desc->ctx, framebuffer_descs[0].ctx); - assert(dctx_desc->ctx != NULL); - - // Push descriptor to freeq. - BaseType_t res = xQueueSend(dctx_freeq, &i, 0); - assert(res == pdTRUE); - } - - // Start rast. - BaseType_t res = xTaskCreate(st3m_gfx_rast_task, "rast", 8192, NULL, - ESP_TASK_PRIO_MIN + 1, &rast_task); + user_ctx_freeq = xQueueCreate(1 + 1, sizeof(int)); + assert(user_ctx_freeq != NULL); + user_ctx_rastq = xQueueCreate(1 + 1, sizeof(int)); + assert(user_ctx_rastq != NULL); + + // Setup framebuffer descriptor. + fb_ctx = ctx_new_for_framebuffer( + fb, FLOW3R_BSP_DISPLAY_WIDTH, FLOW3R_BSP_DISPLAY_HEIGHT, + FLOW3R_BSP_DISPLAY_WIDTH * 2, CTX_FORMAT_RGB565_BYTESWAPPED); + assert(fb_ctx != NULL); + // translate x and y by 120 px to have (0,0) at center of the screen + int32_t offset_x = FLOW3R_BSP_DISPLAY_WIDTH / 2; + int32_t offset_y = FLOW3R_BSP_DISPLAY_HEIGHT / 2; + ctx_apply_transform(fb_ctx, 1, 0, offset_x, 0, 1, offset_y, 0, 0, 1); + + // Setup user_ctx descriptor. + user_ctx = + ctx_new_drawlist(FLOW3R_BSP_DISPLAY_WIDTH, FLOW3R_BSP_DISPLAY_HEIGHT); + assert(user_ctx != NULL); + ctx_set_texture_cache(user_ctx, fb_ctx); + + // Push descriptor to freeq. + int i = 0; + BaseType_t res = xQueueSend(user_ctx_freeq, &i, 0); + assert(res == pdTRUE); + + // Start rasterization, compositing and scan-out + res = xTaskCreate(st3m_gfx_task, "graphics", 8192, NULL, + ESP_TASK_PRIO_MIN + 1, &graphics_task); assert(res == pdPASS); } -static st3m_ctx_desc_t *st3m_gfx_drawctx_free_get(TickType_t ticks_to_wait) { +static Ctx *st3m_gfx_drawctx_free_get(TickType_t ticks_to_wait) { int descno; - BaseType_t res = xQueueReceive(dctx_freeq, &descno, ticks_to_wait); + BaseType_t res = xQueueReceive(user_ctx_freeq, &descno, ticks_to_wait); if (res != pdTRUE) { return NULL; } - return &dctx_descs[descno]; + return user_ctx; } -static void st3m_gfx_drawctx_pipe_put(st3m_ctx_desc_t *desc) { - xQueueSend(dctx_rastq, &desc->num, portMAX_DELAY); +static void st3m_gfx_drawctx_pipe_put(void) { + int i = 0; + xQueueSend(user_ctx_rastq, &i, portMAX_DELAY); } uint8_t st3m_gfx_drawctx_pipe_full(void) { - return uxQueueSpacesAvailable(dctx_rastq) == 0; + return uxQueueSpacesAvailable(user_ctx_rastq) == 0; } void st3m_gfx_flush(void) { ESP_LOGW(TAG, "Pipeline flush/reset requested..."); // Drain all workqs and freeqs. - xQueueReset(dctx_freeq); - xQueueReset(dctx_rastq); - xQueueReset(framebuffer_freeq); - xQueueReset(framebuffer_blitq); + xQueueReset(user_ctx_freeq); + xQueueReset(user_ctx_rastq); // Delay, making sure pipeline tasks have returned all used descriptors. One // second is enough to make sure we've processed everything. vTaskDelay(1000 / portTICK_PERIOD_MS); // And drain again. - xQueueReset(framebuffer_freeq); - xQueueReset(dctx_freeq); + xQueueReset(user_ctx_freeq); - // Now, re-submit all descriptors to freeqs. - for (int i = 0; i < ST3M_GFX_NBUFFERS; i++) { - BaseType_t res = xQueueSend(framebuffer_freeq, &i, 0); - assert(res == pdTRUE); - } - for (int i = 0; i < ST3M_GFX_NCTX; i++) { - BaseType_t res = xQueueSend(dctx_freeq, &i, 0); - assert(res == pdTRUE); - } + int i = 0; + BaseType_t res = xQueueSend(user_ctx_freeq, &i, 0); + assert(res == pdTRUE); ESP_LOGW(TAG, "Pipeline flush/reset done."); } -static int st3m_dctx_no = 0; // always 0 - but this keeps pipelined - // rendering working as a compile time opt-in +static int st3m_user_ctx_no = 0; // always 0 - but this keeps pipelined + // rendering working as a compile time opt-in Ctx *st3m_ctx(TickType_t ticks_to_wait) { - st3m_ctx_desc_t *foo = st3m_gfx_drawctx_free_get(ticks_to_wait); - if (!foo) return NULL; - st3m_dctx_no = foo->num; - return foo->ctx; + Ctx *ctx = st3m_gfx_drawctx_free_get(ticks_to_wait); + if (!ctx) return NULL; + return ctx; } void st3m_overlay_clear(void) { @@ -526,19 +547,19 @@ void st3m_overlay_clear(void) { } Ctx *st3m_overlay_ctx(void) { - if (!_st3m_overlay_ctx) { - Ctx *ctx = _st3m_overlay_ctx = ctx_new_for_framebuffer( + if (!overlay_ctx) { + Ctx *ctx = overlay_ctx = ctx_new_for_framebuffer( st3m_overlay_fb, OVERLAY_WIDTH, OVERLAY_HEIGHT, OVERLAY_WIDTH * 4, CTX_FORMAT_RGBA8); ctx_translate(ctx, 120 - OVERLAY_X, 120 - OVERLAY_Y); memset(st3m_overlay_fb, 0, sizeof(st3m_overlay_fb)); } - return _st3m_overlay_ctx; + return overlay_ctx; } void st3m_ctx_end_frame(Ctx *ctx) { - xQueueSend(dctx_rastq, &st3m_dctx_no, portMAX_DELAY); + xQueueSend(user_ctx_rastq, &st3m_user_ctx_no, portMAX_DELAY); } void st3m_gfx_overlay_clip(int x0, int y0, int x1, int y1) { diff --git a/components/st3m/st3m_gfx.h b/components/st3m/st3m_gfx.h index 5b394dac2540eace90c4e0523fa58eb4a747313c..dd9ff51d29d1661cb07414f4116a06fce7e0b375 100644 --- a/components/st3m/st3m_gfx.h +++ b/components/st3m/st3m_gfx.h @@ -7,9 +7,38 @@ #include "ctx.h" // clang-format on -Ctx *st3m_overlay_ctx(void); +// There are three separate graphics modes that can be set, on application +// exit RGBA8_over_RGB565_BYTESWAPPED should be restored. +// +// The two other modes cause a scan-out of without compositing/clipping +typedef enum { + st3m_gfx_default = 0, + st3m_gfx_8bpp = 8, + st3m_gfx_16bpp = 16, + st3m_gfx_24bpp = 24, + st3m_gfx_32bpp = 32, + st3m_gfx_overlay = 128, +} st3m_gfx_mode; + +// sets the current graphics mode +void st3m_set_gfx_mode(st3m_gfx_mode mode); + +st3m_gfx_mode st3m_get_gfx_mode(void); + +uint8_t *st3m_gfx_fb(st3m_gfx_mode mode); + +void st3m_gfx_set_palette(uint8_t *pal_in, int count); + +// specifies the corners of the clipping rectangle +// for compositing overlay +void st3m_gfx_overlay_clip(int x0, int y0, int x1, int y1); + +// returns a running average of fps +float st3m_gfx_fps(void); + +Ctx *st3m_overlay_ctx(void); // XXX to be removed +Ctx *st3m_ctx(TickType_t ticks_to_wait); // XXX: will get mode as arg -Ctx *st3m_ctx(TickType_t ticks_to_wait); void st3m_ctx_end_frame(Ctx *ctx); // temporary, signature compatible // with ctx_end_frame() @@ -17,13 +46,6 @@ void st3m_ctx_end_frame(Ctx *ctx); // temporary, signature compatible // crtx/blitter pipeline. void st3m_gfx_init(void); -// A drawlist ctx descriptor, pointing to a drawlist-backed Ctx. -typedef struct { - // The numeric ID of this descriptor. - int num; - Ctx *ctx; -} st3m_ctx_desc_t; - // Returns true if the rasterizaiton pipeline submission would block. uint8_t st3m_gfx_drawctx_pipe_full(void); @@ -55,10 +77,3 @@ void st3m_gfx_splash(const char *text); // Draw the flow3r multi-coloured logo at coordinates x,y and with given // dimension (approx. bounding box size). void st3m_gfx_flow3r_logo(Ctx *ctx, float x, float y, float dim); - -// specifies the corners of the clipping rectangle -// for compositing overlay -void st3m_gfx_overlay_clip(int x0, int y0, int x1, int y1); - -// returns a running average of fps -float st3m_gfx_fps(void); diff --git a/python_payload/mypystubs/sys_display.pyi b/python_payload/mypystubs/sys_display.pyi index 38e4e6325c14990a7d108f61b09d7cf4a68a04c1..6cfc507c77257765c5a476b81286e5bcb8d66672 100644 --- a/python_payload/mypystubs/sys_display.pyi +++ b/python_payload/mypystubs/sys_display.pyi @@ -1,6 +1,6 @@ from ctx import Context -def get_ctx() -> Context: ... +def ctx() -> Context: ... def pipe_full() -> bool: ... def pipe_flush() -> None: ... def update(c: Context) -> None: ... diff --git a/python_payload/st3m/application.py b/python_payload/st3m/application.py index e80e1de6c9f3d0ddb4e880ebbe2445b61c0cc1a4..fd64c5108495340fb1ac8d026c2124b03cc766cb 100644 --- a/python_payload/st3m/application.py +++ b/python_payload/st3m/application.py @@ -16,6 +16,7 @@ import os import os.path import stat import sys +import sys_display import random log = Log(__name__) @@ -73,6 +74,9 @@ class Application(BaseView): if fully_exiting and self._wifi_preference is not None: st3m.wifi._onoff_wifi_update() super().on_exit() + # set the default graphics mode, this is a no-op if + # it is already set + sys_display.set_gfx_mode(0) def think(self, ins: InputState, delta_ms: int) -> None: super().think(ins, delta_ms) diff --git a/python_payload/st3m/reactor.py b/python_payload/st3m/reactor.py index 229ac48d9453085723b37a3b659655f0b90f4aed..9895334dd738f78ae2721c33d8fc177adf9044e8 100644 --- a/python_payload/st3m/reactor.py +++ b/python_payload/st3m/reactor.py @@ -157,7 +157,7 @@ class Reactor: # Draw! if self._ctx is None: - self._ctx = sys_display.get_ctx() + self._ctx = sys_display.ctx(0) if self._ctx is not None: if self._last_ctx_get is not None: diff = start - self._last_ctx_get diff --git a/python_payload/st3m/ui/elements/overlays.py b/python_payload/st3m/ui/elements/overlays.py index c3e64726a4f6c7209a78075e9be80b1e064ff490..f69791bcb08ae559edf4278165e263f0a49d8318 100644 --- a/python_payload/st3m/ui/elements/overlays.py +++ b/python_payload/st3m/ui/elements/overlays.py @@ -81,8 +81,13 @@ class Compositor(Responder): def think(self, ins: InputState, delta_ms: int) -> None: self.main.think(ins, delta_ms) + if sys_display.get_gfx_mode() != 0: + return if self._frame_skip <= 0: - if not settings.onoff_show_fps.value: + if ( + not settings.onoff_show_fps.value + and not sys_display.get_gfx_mode() != 0 + ): for overlay in self._enabled_overlays(): overlay.think(ins, delta_ms) @@ -91,10 +96,11 @@ class Compositor(Responder): global _clip_y0 global _clip_x1 global _clip_y1 - self.main.draw(ctx) + if sys_display.get_gfx_mode() != 0: + return if self._frame_skip <= 0: - octx = sys_display.get_overlay_ctx() + octx = sys_display.ctx(128) # add symbolic name or overlay if settings.onoff_show_fps.value: _clip_x0 = 110 _clip_y1 = 0 diff --git a/sim/fakes/sys_display.py b/sim/fakes/sys_display.py index 84897e7f0e5ca54fb00616888f444a2586874c1b..b3ea8d0510662b5c64172262a52ae8e004a91d2f 100644 --- a/sim/fakes/sys_display.py +++ b/sim/fakes/sys_display.py @@ -9,6 +9,18 @@ def overlay_clip(x0, y0, x1, y1): pass +def get_gfx_mode(): + return 0 + + +def set_gfx_mode(no): + pass + + update = _sim.display_update get_ctx = _sim.get_ctx get_overlay_ctx = _sim.get_overlay_ctx + + +def ctx(foo): + return _sim.get_ctx()