From 0efa5f37587b645ea059d0a4b4a2d5bbdb09c749 Mon Sep 17 00:00:00 2001 From: Liam <30486941+liamHowatt@users.noreply.github.com> Date: Sun, 13 Oct 2024 08:17:12 +0200 Subject: [PATCH] feat(opengl): texture caching (#6861) Co-authored-by: JWBverheesen <31246830+JWBverheesen@users.noreply.github.com> --- Kconfig | 5 + docs/integration/driver/opengles.rst | 35 +- lv_conf_template.h | 5 +- src/draw/opengles/lv_draw_opengles.c | 593 +++++++++++++++++++++++++ src/draw/opengles/lv_draw_opengles.h | 45 ++ src/draw/sdl/lv_draw_sdl.h | 1 - src/drivers/glfw/lv_glfw_window.c | 11 +- src/drivers/glfw/lv_opengles_debug.c | 4 +- src/drivers/glfw/lv_opengles_debug.h | 10 +- src/drivers/glfw/lv_opengles_driver.c | 172 +++++-- src/drivers/glfw/lv_opengles_driver.h | 16 +- src/drivers/glfw/lv_opengles_texture.c | 29 +- src/lv_conf_internal.h | 11 +- src/lv_init.c | 11 + 14 files changed, 882 insertions(+), 66 deletions(-) create mode 100644 src/draw/opengles/lv_draw_opengles.c create mode 100644 src/draw/opengles/lv_draw_opengles.h diff --git a/Kconfig b/Kconfig index a37e2c69b..9e2c46091 100644 --- a/Kconfig +++ b/Kconfig @@ -476,6 +476,11 @@ menu "LVGL configuration" if enabled, the user is required to call `lv_draw_dma2d_transfer_complete_interrupt_handler` upon receiving the DMA2D global interrupt + + config LV_USE_DRAW_OPENGLES + bool "Draw using cached OpenGLES textures" + default n + depends on LV_USE_OPENGLES endmenu menu "Feature Configuration" diff --git a/docs/integration/driver/opengles.rst b/docs/integration/driver/opengles.rst index daeef6390..1d00027c8 100644 --- a/docs/integration/driver/opengles.rst +++ b/docs/integration/driver/opengles.rst @@ -1,5 +1,5 @@ =============================== -OpenGL ES Display/Inputs driver +OpenGL ES Display/Inputs Driver =============================== Overview @@ -17,7 +17,7 @@ The OpenGL driver uses GLEW GLFW to access the OpenGL window manager. 1. Install GLEW and GLFW: ``sudo apt-get install libglew-dev libglfw3-dev`` -Configure OpenGL driver +Configure OpenGL Driver ----------------------- 1. Required linked libraries: -lGL -lGLEW -lglfw @@ -26,7 +26,7 @@ Configure OpenGL driver #define LV_USE_OPENGLES 1 -Basic usage +Basic Usage ----------- .. code-block:: c @@ -75,13 +75,13 @@ Basic usage return 0; } -Advanced usage +Advanced Usage -------------- The OpenGL driver can draw textures from the user. A third-party library could be used to add content to a texture and the driver will draw the texture in the window. -.. code:: c +.. code-block:: c #include "lvgl/lvgl.h" #include @@ -139,8 +139,7 @@ used to add content to a texture and the driver will draw the texture in the win /* create objects on the screen of the sub texture */ lv_display_set_default(sub_texture); - lv_obj_set_style_bg_color(lv_screen_active(), lv_color_black(), 0); - lv_example_anim_2(); + lv_example_keyboard_2(); lv_display_set_default(main_texture); /* position the sub texture within the window */ @@ -195,3 +194,25 @@ used to add content to a texture and the driver will draw the texture in the win /* the texture is drawn on by LVGL and can be used by anything that uses OpenGL textures */ third_party_lib_use_texture(sub_texture_id); } + +OpenGL Texture Caching Renderer +------------------------------- + +There is a renderer in LVGL which caches software-rendered areas as OpenGL textures. +The textures are retrieved from the cache and reused when there is a match. +The performance will be drastically improved in most cases. + +.. code-block:: c + + #define LV_USE_DRAW_OPENGLES 1 + +Known Limitations +~~~~~~~~~~~~~~~~~ + +- Performance will be the same or slightly worse if the drawn areas are never found in the cache + due to objects with continuously varying colors or shapes. One example is a label whose color + is set to a random value every frame, as in the "Multiple labels" scene of the benchmark demo. +- Layers with transparent pixels and an overall layer transparency will not blend correctly. + The effect can be observed in the "Containers with opa_layer" scene of the benchmark demo + in the border corners. +- Layers with rotation are not currently supported. Images with rotation are fine. diff --git a/lv_conf_template.h b/lv_conf_template.h index 1df77b528..66193d8f8 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -294,7 +294,7 @@ #define LV_VG_LITE_STROKE_CACHE_CNT 32 #endif -/* Accelerate blends, fills, etc. with STM32 DMA2D */ +/** Accelerate blends, fills, etc. with STM32 DMA2D */ #define LV_USE_DRAW_DMA2D 0 #if LV_USE_DRAW_DMA2D @@ -306,6 +306,9 @@ #define LV_USE_DRAW_DMA2D_INTERRUPT 0 #endif +/** Draw using cached OpenGLES textures */ +#define LV_USE_DRAW_OPENGLES 0 + /*======================= * FEATURE CONFIGURATION *=======================*/ diff --git a/src/draw/opengles/lv_draw_opengles.c b/src/draw/opengles/lv_draw_opengles.c new file mode 100644 index 000000000..fb8888f1e --- /dev/null +++ b/src/draw/opengles/lv_draw_opengles.c @@ -0,0 +1,593 @@ +/** + * @file lv_draw_opengles.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_opengles.h" +#if LV_USE_DRAW_OPENGLES +#include "../lv_draw_private.h" +#include "../../misc/cache/lv_cache_entry_private.h" +#include "../../drivers/glfw/lv_opengles_debug.h" +#include "../../drivers/glfw/lv_opengles_texture.h" +#include "../../drivers/glfw/lv_opengles_driver.h" +#include +#include +#include "../../draw/lv_draw_label.h" +#include "../../draw/lv_draw_rect.h" +#include "../../draw/lv_draw_arc.h" +#include "../../draw/lv_draw_image.h" +#include "../../draw/lv_draw_triangle.h" +#include "../../draw/lv_draw_line.h" +#include "../../core/lv_obj.h" +#include "../../core/lv_refr_private.h" +#include "../../display/lv_display_private.h" +#include "../../stdlib/lv_string.h" +#include "../../misc/lv_area_private.h" + +/********************* + * DEFINES + *********************/ + +#define DRAW_UNIT_ID_OPENGLES 6 + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_draw_unit_t base_unit; + lv_draw_task_t * task_act; + lv_cache_t * texture_cache; + unsigned int framebuffer; + lv_draw_buf_t render_draw_buf; +} lv_draw_opengles_unit_t; + +typedef struct { + lv_draw_dsc_base_t * draw_dsc; + int32_t w; + int32_t h; + unsigned int texture; +} cache_data_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static bool opengles_texture_cache_create_cb(cache_data_t * cached_data, void * user_data); +static void opengles_texture_cache_free_cb(cache_data_t * cached_data, void * user_data); +static lv_cache_compare_res_t opengles_texture_cache_compare_cb(const cache_data_t * lhs, const cache_data_t * rhs); + +static void blend_texture_layer(lv_draw_opengles_unit_t * u); +static void draw_from_cached_texture(lv_draw_opengles_unit_t * u); + +static void execute_drawing(lv_draw_opengles_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_opengles_unit_t * u, cache_data_t * cache_data); + +static unsigned int layer_get_texture(lv_layer_t * layer); +static unsigned int get_framebuffer(lv_draw_opengles_unit_t * u); +static unsigned int create_texture(int32_t w, int32_t h, const void * data); + +/********************** + * STATIC VARIABLES + **********************/ + +static lv_draw_opengles_unit_t * g_unit; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_opengles_init(void) +{ + lv_draw_opengles_unit_t * draw_opengles_unit = lv_draw_create_unit(sizeof(lv_draw_opengles_unit_t)); + draw_opengles_unit->base_unit.dispatch_cb = dispatch; + draw_opengles_unit->base_unit.evaluate_cb = evaluate; + draw_opengles_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)opengles_texture_cache_compare_cb, + .create_cb = (lv_cache_create_cb_t)opengles_texture_cache_create_cb, + .free_cb = (lv_cache_free_cb_t)opengles_texture_cache_free_cb, + }); + lv_cache_set_name(draw_opengles_unit->texture_cache, "OPENGLES_TEXTURE"); + + lv_draw_buf_init(&draw_opengles_unit->render_draw_buf, 0, 0, LV_COLOR_FORMAT_ARGB8888, LV_STRIDE_AUTO, NULL, 0); + + g_unit = draw_opengles_unit; +} + +void lv_draw_opengles_deinit(void) +{ + lv_free(g_unit->render_draw_buf.unaligned_data); + lv_cache_destroy(g_unit->texture_cache, g_unit); + if(g_unit->framebuffer != 0) { + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + GL_CALL(glDeleteFramebuffers(1, &g_unit->framebuffer)); + } + g_unit = NULL; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static bool opengles_texture_cache_create_cb(cache_data_t * cached_data, void * user_data) +{ + return draw_to_texture((lv_draw_opengles_unit_t *)user_data, cached_data); +} + +static void opengles_texture_cache_free_cb(cache_data_t * cached_data, void * user_data) +{ + LV_UNUSED(user_data); + + lv_free(cached_data->draw_dsc); + GL_CALL(glDeleteTextures(1, &cached_data->texture)); + cached_data->draw_dsc = NULL; + cached_data->texture = 0; +} + +static lv_cache_compare_res_t opengles_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; + } + + const uint8_t * left_draw_dsc = (const uint8_t *)lhs->draw_dsc; + const uint8_t * right_draw_dsc = (const uint8_t *)rhs->draw_dsc; + left_draw_dsc += sizeof(lv_draw_dsc_base_t); + right_draw_dsc += sizeof(lv_draw_dsc_base_t); + + int cmp_res = lv_memcmp(left_draw_dsc, right_draw_dsc, lhs->draw_dsc->dsc_size - sizeof(lv_draw_dsc_base_t)); + + if(cmp_res != 0) { + return cmp_res > 0 ? 1 : -1; + } + + return 0; +} + +static int32_t dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer) +{ + lv_draw_opengles_unit_t * draw_opengles_unit = (lv_draw_opengles_unit_t *) draw_unit; + + /*Return immediately if it's busy with a draw task*/ + if(draw_opengles_unit->task_act) return 0; + + lv_draw_task_t * t = NULL; + t = lv_draw_get_next_available_task(layer, NULL, DRAW_UNIT_ID_OPENGLES); + if(t == NULL) return -1; + + unsigned int texture = layer_get_texture(layer); + if(texture == 0) { + lv_display_t * disp = lv_refr_get_disp_refreshing(); + if(layer != disp->layer_head) { + void * buf = lv_draw_layer_alloc_buf(layer); + if(buf == NULL) return -1; + + int32_t w = lv_area_get_width(&layer->buf_area); + int32_t h = lv_area_get_height(&layer->buf_area); + + texture = create_texture(w, h, NULL); + + layer->user_data = (void *)(uintptr_t)texture; + } + else { + layer->user_data = (void *)(uintptr_t)lv_opengles_texture_get_texture_id(disp); + } + } + + t->state = LV_DRAW_TASK_STATE_IN_PROGRESS; + draw_opengles_unit->base_unit.target_layer = layer; + draw_opengles_unit->base_unit.clip_area = &t->clip_area; + draw_opengles_unit->task_act = t; + + execute_drawing(draw_opengles_unit); + + draw_opengles_unit->task_act->state = LV_DRAW_TASK_STATE_READY; + draw_opengles_unit->task_act = NULL; + + /*The draw unit is free now. Request a new dispatching as it can get a new task*/ + lv_draw_dispatch_request(); + return 1; +} + +static int32_t evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task) +{ + LV_UNUSED(draw_unit); + + /*If not refreshing the display probably it's a canvas rendering + *which his not support in SDL as it's not a texture.*/ + if(lv_refr_get_disp_refreshing() == NULL) return 0; + + if(((lv_draw_dsc_base_t *)task->draw_dsc)->user_data == NULL) { + task->preference_score = 0; + task->preferred_draw_unit_id = DRAW_UNIT_ID_OPENGLES; + } + return 0; +} + +static bool draw_to_texture(lv_draw_opengles_unit_t * u, cache_data_t * cache_data) +{ + lv_draw_task_t * task = u->task_act; + + lv_layer_t dest_layer; + lv_memzero(&dest_layer, sizeof(dest_layer)); + + int32_t texture_w = lv_area_get_width(&task->_real_area); + int32_t texture_h = lv_area_get_height(&task->_real_area); + + if(NULL == lv_draw_buf_reshape(&u->render_draw_buf, LV_COLOR_FORMAT_ARGB8888, texture_w, texture_h, LV_STRIDE_AUTO)) { + uint8_t * data = u->render_draw_buf.unaligned_data; + uint32_t data_size = LV_DRAW_BUF_SIZE(texture_w, texture_h, LV_COLOR_FORMAT_ARGB8888); + data = lv_realloc(data, data_size); + LV_ASSERT_MALLOC(data); + lv_result_t init_result = lv_draw_buf_init(&u->render_draw_buf, texture_w, texture_h, LV_COLOR_FORMAT_ARGB8888, + LV_STRIDE_AUTO, data, data_size); + LV_ASSERT(init_result == LV_RESULT_OK); + } + + dest_layer.draw_buf = &u->render_draw_buf; + dest_layer.color_format = LV_COLOR_FORMAT_ARGB8888; + + dest_layer.buf_area = task->_real_area; + dest_layer._clip_area = task->_real_area; + dest_layer.phy_clip_area = task->_real_area; + lv_memzero(u->render_draw_buf.data, lv_area_get_size(&task->_real_area) * 4); + + lv_display_t * disp = lv_refr_get_disp_refreshing(); + + lv_obj_t * obj = ((lv_draw_dsc_base_t *)task->draw_dsc)->obj; + bool original_send_draw_task_event = false; + if(obj) { + original_send_draw_task_event = lv_obj_has_flag(obj, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS); + lv_obj_remove_flag(obj, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS); + } + + switch(task->type) { + case LV_DRAW_TASK_TYPE_FILL: { + lv_draw_fill_dsc_t * fill_dsc = task->draw_dsc; + lv_draw_rect_dsc_t rect_dsc; + lv_draw_rect_dsc_init(&rect_dsc); + rect_dsc.base.user_data = (void *)(uintptr_t)1; + rect_dsc.bg_color = fill_dsc->color; + rect_dsc.bg_grad = fill_dsc->grad; + rect_dsc.radius = fill_dsc->radius; + rect_dsc.bg_opa = fill_dsc->opa; + + lv_draw_rect(&dest_layer, &rect_dsc, &task->area); + } + break; + case LV_DRAW_TASK_TYPE_BORDER: { + lv_draw_border_dsc_t * border_dsc = task->draw_dsc; + lv_draw_rect_dsc_t rect_dsc; + lv_draw_rect_dsc_init(&rect_dsc); + rect_dsc.base.user_data = (void *)(uintptr_t)1; + rect_dsc.bg_opa = LV_OPA_TRANSP; + rect_dsc.radius = border_dsc->radius; + rect_dsc.border_color = border_dsc->color; + rect_dsc.border_opa = border_dsc->opa; + rect_dsc.border_side = border_dsc->side; + rect_dsc.border_width = border_dsc->width; + lv_draw_rect(&dest_layer, &rect_dsc, &task->area); + break; + } + case LV_DRAW_TASK_TYPE_BOX_SHADOW: { + lv_draw_box_shadow_dsc_t * box_shadow_dsc = task->draw_dsc; + lv_draw_rect_dsc_t rect_dsc; + lv_draw_rect_dsc_init(&rect_dsc); + rect_dsc.base.user_data = (void *)(uintptr_t)1; + rect_dsc.bg_opa = LV_OPA_0; + rect_dsc.radius = box_shadow_dsc->radius; + rect_dsc.bg_color = box_shadow_dsc->color; + rect_dsc.shadow_opa = box_shadow_dsc->opa; + rect_dsc.shadow_width = box_shadow_dsc->width; + rect_dsc.shadow_spread = box_shadow_dsc->spread; + rect_dsc.shadow_offset_x = box_shadow_dsc->ofs_x; + rect_dsc.shadow_offset_y = box_shadow_dsc->ofs_y; + lv_draw_rect(&dest_layer, &rect_dsc, &task->area); + break; + } + case LV_DRAW_TASK_TYPE_LABEL: { + lv_draw_label_dsc_t label_dsc; + lv_memcpy(&label_dsc, task->draw_dsc, sizeof(label_dsc)); + label_dsc.base.user_data = (void *)(uintptr_t)1; + lv_draw_label(&dest_layer, &label_dsc, &task->area); + } + break; + case LV_DRAW_TASK_TYPE_ARC: { + lv_draw_arc_dsc_t arc_dsc; + lv_memcpy(&arc_dsc, task->draw_dsc, sizeof(arc_dsc)); + arc_dsc.base.user_data = (void *)(uintptr_t)1; + lv_draw_arc(&dest_layer, &arc_dsc); + } + break; + case LV_DRAW_TASK_TYPE_LINE: { + lv_draw_line_dsc_t line_dsc; + lv_memcpy(&line_dsc, task->draw_dsc, sizeof(line_dsc)); + line_dsc.base.user_data = (void *)(uintptr_t)1; + lv_draw_line(&dest_layer, &line_dsc); + } + break; + case LV_DRAW_TASK_TYPE_TRIANGLE: { + lv_draw_triangle_dsc_t triangle_dsc; + lv_memcpy(&triangle_dsc, task->draw_dsc, sizeof(triangle_dsc)); + triangle_dsc.base.user_data = (void *)(uintptr_t)1; + lv_draw_triangle(&dest_layer, &triangle_dsc); + } + break; + case LV_DRAW_TASK_TYPE_IMAGE: { + lv_draw_image_dsc_t image_dsc; + lv_memcpy(&image_dsc, task->draw_dsc, sizeof(image_dsc)); + image_dsc.base.user_data = (void *)(uintptr_t)1; + lv_draw_image(&dest_layer, &image_dsc, &task->area); + break; + } + default: + return false; + } + + while(dest_layer.draw_task_head) { + lv_draw_dispatch_layer(disp, &dest_layer); + if(dest_layer.draw_task_head) { + lv_draw_dispatch_wait_for_request(); + } + } + + unsigned int texture = create_texture(texture_w, texture_h, u->render_draw_buf.data); + + lv_draw_dsc_base_t * base_dsc = task->draw_dsc; + + cache_data->draw_dsc = lv_malloc(base_dsc->dsc_size); + lv_memcpy((void *)cache_data->draw_dsc, base_dsc, base_dsc->dsc_size); + cache_data->w = texture_w; + cache_data->h = texture_h; + cache_data->texture = texture; + + if(obj) { + lv_obj_update_flag(obj, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS, original_send_draw_task_event); + } + + return true; +} + +static void blend_texture_layer(lv_draw_opengles_unit_t * u) +{ + lv_area_t clip_area = *u->base_unit.clip_area; + + lv_draw_task_t * t = u->task_act; + lv_draw_image_dsc_t * draw_dsc = t->draw_dsc; + + lv_area_t area; + area.x1 = -draw_dsc->pivot.x; + area.y1 = -draw_dsc->pivot.y; + area.x1 = (area.x1 * draw_dsc->scale_x) / 256; + area.y1 = (area.y1 * draw_dsc->scale_y) / 256; + area.x1 += t->area.x1 + draw_dsc->pivot.x; + area.y1 += t->area.y1 + draw_dsc->pivot.y; + lv_area_set_width(&area, lv_area_get_width(&t->area) * draw_dsc->scale_x / 256); + lv_area_set_height(&area, lv_area_get_height(&t->area) * draw_dsc->scale_y / 256); + + lv_layer_t * src_layer = (lv_layer_t *)draw_dsc->src; + unsigned int src_texture = layer_get_texture(src_layer); + + + lv_layer_t * dest_layer = u->base_unit.target_layer; + unsigned int target_texture = layer_get_texture(dest_layer); + LV_ASSERT(target_texture != 0); + int32_t targ_tex_w = lv_area_get_width(&dest_layer->buf_area); + int32_t targ_tex_h = lv_area_get_height(&dest_layer->buf_area); + + GL_CALL(glBindTexture(GL_TEXTURE_2D, target_texture)); + + unsigned int framebuffer = get_framebuffer(u); + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer)); + GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target_texture, 0)); + + lv_opengles_viewport(0, 0, targ_tex_w, targ_tex_h); + // TODO rotation + lv_opengles_render_texture(src_texture, &area, draw_dsc->opa, targ_tex_w, targ_tex_h, &clip_area, false); + + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + + + GL_CALL(glBindTexture(GL_TEXTURE_2D, 0)); + GL_CALL(glDeleteTextures(1, &src_texture)); +} + +static void draw_from_cached_texture(lv_draw_opengles_unit_t * u) +{ + lv_draw_task_t * t = u->task_act; + + cache_data_t data_to_find; + data_to_find.draw_dsc = (lv_draw_dsc_base_t *)t->draw_dsc; + + data_to_find.w = lv_area_get_width(&t->_real_area); + data_to_find.h = lv_area_get_height(&t->_real_area); + data_to_find.texture = 0; + + /*user_data stores the renderer to differentiate it from SW rendered tasks. + *However the cached texture is independent from the renderer so use NULL user_data*/ + void * user_data_saved = data_to_find.draw_dsc->user_data; + data_to_find.draw_dsc->user_data = NULL; + + /*img_dsc->image_area is an absolute coordinate so it's different + *for the same image on a different position. So make it relative before using for cache. */ + lv_area_t a = t->area; + if(t->type == LV_DRAW_TASK_TYPE_IMAGE) { + lv_draw_image_dsc_t * img_dsc = (lv_draw_image_dsc_t *)data_to_find.draw_dsc; + lv_area_move(&img_dsc->image_area, -t->area.x1, -t->area.y1); + } + else if(t->type == LV_DRAW_TASK_TYPE_TRIANGLE) { + lv_draw_triangle_dsc_t * tri_dsc = (lv_draw_triangle_dsc_t *)data_to_find.draw_dsc; + tri_dsc->p[0].x -= t->area.x1; + tri_dsc->p[0].y -= t->area.y1; + tri_dsc->p[1].x -= t->area.x1; + tri_dsc->p[1].y -= t->area.y1; + tri_dsc->p[2].x -= t->area.x1; + tri_dsc->p[2].y -= t->area.y1; + } + else if(t->type == LV_DRAW_TASK_TYPE_LINE) { + lv_draw_line_dsc_t * line_dsc = (lv_draw_line_dsc_t *)data_to_find.draw_dsc; + line_dsc->p1.x -= t->area.x1; + line_dsc->p1.y -= t->area.y1; + line_dsc->p2.x -= t->area.x1; + line_dsc->p2.y -= t->area.y1; + } + else if(t->type == LV_DRAW_TASK_TYPE_ARC) { + lv_draw_arc_dsc_t * arc_dsc = (lv_draw_arc_dsc_t *)data_to_find.draw_dsc; + arc_dsc->center.x -= t->area.x1; + arc_dsc->center.y -= t->area.y1; + } + + lv_area_move(&t->area, -a.x1, -a.y1); + lv_area_move(&t->_real_area, -a.x1, -a.y1); + + lv_cache_entry_t * entry_cached = lv_cache_acquire_or_create(u->texture_cache, &data_to_find, u); + + lv_area_move(&t->area, a.x1, a.y1); + lv_area_move(&t->_real_area, a.x1, a.y1); + + if(!entry_cached) { + return; + } + + + data_to_find.draw_dsc->user_data = user_data_saved; + + cache_data_t * data_cached = lv_cache_entry_get_data(entry_cached); + unsigned int texture = data_cached->texture; + + lv_layer_t * dest_layer = u->base_unit.target_layer; + + unsigned int target_texture = layer_get_texture(dest_layer); + LV_ASSERT(target_texture != 0); + int32_t targ_tex_w = lv_area_get_width(&dest_layer->buf_area); + int32_t targ_tex_h = lv_area_get_height(&dest_layer->buf_area); + + GL_CALL(glBindTexture(GL_TEXTURE_2D, target_texture)); + + unsigned int framebuffer = get_framebuffer(u); + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer)); + GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target_texture, 0)); + + lv_opengles_viewport(0, 0, targ_tex_w, targ_tex_h); + lv_area_t clip_area = *u->base_unit.clip_area; + lv_area_move(&clip_area, -dest_layer->buf_area.x1, -dest_layer->buf_area.y1); + lv_area_t render_area = t->_real_area; + lv_area_move(&render_area, -dest_layer->buf_area.x1, -dest_layer->buf_area.y1); + lv_opengles_render_texture(texture, &render_area, 0xff, targ_tex_w, targ_tex_h, &clip_area, true); + + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + + lv_cache_release(u->texture_cache, entry_cached, u); + + /*Do not cache non static (const) texts as the text's pointer can be freed/reallocated + *at any time resulting in a wild pointer in the cached draw dsc. */ + if(t->type == LV_DRAW_TASK_TYPE_LABEL) { + lv_draw_label_dsc_t * label_dsc = t->draw_dsc; + if(!label_dsc->text_static) { + lv_cache_drop(u->texture_cache, &data_to_find, u); + } + } +} + +static void execute_drawing(lv_draw_opengles_unit_t * u) +{ + lv_draw_task_t * t = u->task_act; + + if(t->type == LV_DRAW_TASK_TYPE_FILL) { + lv_draw_fill_dsc_t * fill_dsc = t->draw_dsc; + if(fill_dsc->radius == 0 && fill_dsc->grad.dir == LV_GRAD_DIR_NONE) { + lv_layer_t * layer = u->base_unit.target_layer; + lv_area_t fill_area = t->area; + lv_area_intersect(&fill_area, &fill_area, u->base_unit.clip_area); + lv_area_move(&fill_area, -layer->buf_area.x1, -layer->buf_area.y1); + + unsigned int target_texture = layer_get_texture(layer); + LV_ASSERT(target_texture != 0); + int32_t targ_tex_w = lv_area_get_width(&layer->buf_area); + int32_t targ_tex_h = lv_area_get_height(&layer->buf_area); + + unsigned int framebuffer = get_framebuffer(u); + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer)); + GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target_texture, 0)); + + lv_opengles_viewport(0, 0, targ_tex_w, targ_tex_h); + lv_opengles_render_fill(fill_dsc->color, &fill_area, fill_dsc->opa, targ_tex_w, targ_tex_h); + + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + + return; + } + } + + if(t->type == LV_DRAW_TASK_TYPE_LAYER) { + blend_texture_layer(u); + return; + } + + draw_from_cached_texture(u); +} + +static unsigned int layer_get_texture(lv_layer_t * layer) +{ + return (unsigned int)(uintptr_t)layer->user_data; +} + +static unsigned int get_framebuffer(lv_draw_opengles_unit_t * u) +{ + if(u->framebuffer == 0) { + GL_CALL(glGenFramebuffers(1, &u->framebuffer)); + } + return u->framebuffer; +} + +static unsigned int create_texture(int32_t w, int32_t h, const void * data) +{ + unsigned int texture; + + GL_CALL(glGenTextures(1, &texture)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, texture)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + + /* LV_COLOR_DEPTH 32, 24, 16 are supported but the cached textures will always + * have full ARGB pixels since the alpha channel is required for blending. + */ + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data)); + + glGenerateMipmap(GL_TEXTURE_2D); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 20); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + return texture; +} + +#endif /*LV_USE_DRAW_OPENGLES*/ diff --git a/src/draw/opengles/lv_draw_opengles.h b/src/draw/opengles/lv_draw_opengles.h new file mode 100644 index 000000000..1285c93ec --- /dev/null +++ b/src/draw/opengles/lv_draw_opengles.h @@ -0,0 +1,45 @@ +/** + * @file lv_draw_opengles.h + * + */ + +#ifndef LV_DRAW_OPENGLES_H +#define LV_DRAW_OPENGLES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" +#if LV_USE_DRAW_OPENGLES + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_draw_opengles_init(void); +void lv_draw_opengles_deinit(void); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_DRAW_OPENGLES*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_OPENGLES_H*/ diff --git a/src/draw/sdl/lv_draw_sdl.h b/src/draw/sdl/lv_draw_sdl.h index d9b061de1..cb370a1bb 100644 --- a/src/draw/sdl/lv_draw_sdl.h +++ b/src/draw/sdl/lv_draw_sdl.h @@ -40,7 +40,6 @@ extern "C" { typedef struct { lv_draw_unit_t base_unit; lv_draw_task_t * task_act; - uint32_t texture_cache_data_type; lv_cache_t * texture_cache; } lv_draw_sdl_unit_t; diff --git a/src/drivers/glfw/lv_glfw_window.c b/src/drivers/glfw/lv_glfw_window.c index 849cbf58b..d6287999a 100644 --- a/src/drivers/glfw/lv_glfw_window.c +++ b/src/drivers/glfw/lv_glfw_window.c @@ -110,7 +110,7 @@ void lv_glfw_window_delete(lv_glfw_window_t * window) if(window->use_indev) { lv_glfw_texture_t * texture; LV_LL_READ(&window->textures, texture) { - lv_indev_delete(texture->indev); + if(texture->indev != NULL) lv_indev_delete(texture->indev); } } lv_ll_clear(&window->textures); @@ -300,7 +300,14 @@ static void window_update_handler(lv_timer_t * t) lv_refr_now(texture_disp); } - lv_opengles_render_texture(texture->texture_id, &texture->area, texture->opa, window->hor_res, window->ver_res); + lv_area_t clip_area = texture->area; +#if LV_USE_DRAW_OPENGLES + lv_opengles_render_texture(texture->texture_id, &texture->area, texture->opa, window->hor_res, window->ver_res, + &clip_area, texture_disp == NULL); +#else + lv_opengles_render_texture(texture->texture_id, &texture->area, texture->opa, window->hor_res, window->ver_res, + &clip_area, true); +#endif } /* Swap front and back buffers */ diff --git a/src/drivers/glfw/lv_opengles_debug.c b/src/drivers/glfw/lv_opengles_debug.c index 5bf6d0227..1b098be6b 100644 --- a/src/drivers/glfw/lv_opengles_debug.c +++ b/src/drivers/glfw/lv_opengles_debug.c @@ -41,14 +41,12 @@ void GLClearError() while(glGetError() != GL_NO_ERROR); } -bool GLLogCall(const char * function, const char * file, int line) +void GLLogCall(const char * function, const char * file, int line) { GLenum error; while((error = glGetError()) != GL_NO_ERROR) { LV_LOG_ERROR("[OpenGL Error] (%d) %s %s:%d", error, function, file, line); - return false; } - return true; } /********************** diff --git a/src/drivers/glfw/lv_opengles_debug.h b/src/drivers/glfw/lv_opengles_debug.h index ddde0eb53..aa8bbebe9 100644 --- a/src/drivers/glfw/lv_opengles_debug.h +++ b/src/drivers/glfw/lv_opengles_debug.h @@ -19,12 +19,14 @@ extern "C" { void GLClearError(void); -bool GLLogCall(const char * function, const char * file, int line); +void GLLogCall(const char * function, const char * file, int line); #if LV_USE_OPENGLES_DEBUG -#define GL_CALL(x) GLClearError();\ - x;\ - GLLogCall(#x, __FILE__, __LINE__) +#define GL_CALL(x) do {\ + GLClearError();\ + x;\ + GLLogCall(#x, __FILE__, __LINE__);\ + } while(0) #else #define GL_CALL(x) x #endif diff --git a/src/drivers/glfw/lv_opengles_driver.c b/src/drivers/glfw/lv_opengles_driver.c index 110e7140f..1f46f63b8 100644 --- a/src/drivers/glfw/lv_opengles_driver.c +++ b/src/drivers/glfw/lv_opengles_driver.c @@ -7,6 +7,7 @@ * INCLUDES *********************/ #include "../../display/lv_display.h" +#include "../../misc/lv_area_private.h" #if LV_USE_OPENGLES @@ -17,6 +18,8 @@ * DEFINES *********************/ +#define LV_OPENGLES_VERTEX_BUFFER_LEN 16 + /********************** * TYPEDEFS **********************/ @@ -24,6 +27,8 @@ /********************** * STATIC PROTOTYPES **********************/ +static void lv_opengles_render_internal(unsigned int texture, const lv_area_t * texture_area, lv_opa_t opa, + int32_t disp_w, int32_t disp_h, const lv_area_t * texture_clip_area, bool flip, lv_color_t fill_color); static void lv_opengles_enable_blending(void); static void lv_opengles_vertex_buffer_init(const void * data, unsigned int size); static void lv_opengles_vertex_buffer_deinit(void); @@ -49,7 +54,9 @@ static int lv_opengles_shader_get_uniform_location(const char * name); static void lv_opengles_shader_set_uniform1i(const char * name, int value); static void lv_opengles_shader_set_uniformmatrix3fv(const char * name, int count, bool transpose, const float * values); static void lv_opengles_shader_set_uniform1f(const char * name, float value); +static void lv_opengles_shader_set_uniform3f(const char * name, float value_0, float value_1, float value_2); static void lv_opengles_render_draw(void); +static float lv_opengles_map_float(float x, float min_in, float max_in, float min_out, float max_out); /*********************** * GLOBAL PROTOTYPES @@ -69,8 +76,8 @@ static unsigned int index_buffer_count = 0; static unsigned int shader_id; -static const char * shader_names[] = { "u_Texture", "u_ColorDepth", "u_VertexTransform", "u_Opa" }; -static int shader_location[] = { 0, 0, 0, 0 }; +static const char * shader_names[] = { "u_Texture", "u_ColorDepth", "u_VertexTransform", "u_Opa", "u_IsFill", "u_FillColor" }; +static int shader_location[] = { 0, 0, 0, 0, 0, 0 }; static const char * vertex_shader = "#version 300 es\n" @@ -91,7 +98,7 @@ static const char * vertex_shader = static const char * fragment_shader = "#version 300 es\n" "\n" - "precision mediump float;\n" + "precision lowp float;\n" "\n" "layout(location = 0) out vec4 color;\n" "\n" @@ -100,15 +107,23 @@ static const char * fragment_shader = "uniform sampler2D u_Texture;\n" "uniform int u_ColorDepth;\n" "uniform float u_Opa;\n" + "uniform bool u_IsFill;\n" + "uniform vec3 u_FillColor;\n" "\n" "void main()\n" "{\n" - " vec4 texColor = texture(u_Texture, v_TexCoord);\n" + " vec4 texColor;\n" + " if (u_IsFill) {\n" + " texColor = vec4(u_FillColor, 1.0f);\n" + " } else {\n" + " texColor = texture(u_Texture, v_TexCoord);\n" + " }\n" " if (u_ColorDepth == 8) {\n" " float gray = texColor.r;\n" " color = vec4(gray, gray, gray, u_Opa);\n" " } else {\n" - " color = vec4(texColor.rgb, texColor.a * u_Opa);\n" + " float combinedAlpha = texColor.a * u_Opa;\n" + " color = vec4(texColor.rgb * combinedAlpha, combinedAlpha);\n" " }\n" "};\n"; @@ -126,19 +141,12 @@ void lv_opengles_init(void) lv_opengles_enable_blending(); - float positions[] = { - -1.0f, 1.0f, 0.0f, 0.0f, - 1.0f, 1.0f, 1.0f, 0.0f, - 1.0f, -1.0f, 1.0f, 1.0f, - -1.0f, -1.0f, 0.0f, 1.0f - }; - unsigned int indices[] = { 0, 1, 2, 2, 3, 0 }; - lv_opengles_vertex_buffer_init(positions, sizeof(positions)); + lv_opengles_vertex_buffer_init(NULL, sizeof(float) * LV_OPENGLES_VERTEX_BUFFER_LEN); lv_opengles_vertex_array_init(); lv_opengles_vertex_array_add_buffer(); @@ -170,27 +178,14 @@ void lv_opengles_deinit(void) } void lv_opengles_render_texture(unsigned int texture, const lv_area_t * texture_area, lv_opa_t opa, int32_t disp_w, - int32_t disp_h) + int32_t disp_h, const lv_area_t * texture_clip_area, bool flip) { - GL_CALL(glActiveTexture(GL_TEXTURE0)); - GL_CALL(glBindTexture(GL_TEXTURE_2D, texture)); + lv_opengles_render_internal(texture, texture_area, opa, disp_w, disp_h, texture_clip_area, flip, lv_color_black()); +} - float hor_scale = (float)lv_area_get_width(texture_area) / (float)disp_w; - float ver_scale = (float)lv_area_get_height(texture_area) / (float)disp_h; - float hor_translate = (float)texture_area->x1 / (float)disp_w * 2.0f - (1.0f - hor_scale); - float ver_translate = -((float)texture_area->y1 / (float)disp_h * 2.0f - (1.0f - ver_scale)); - float matrix[9] = { - hor_scale, 0.0f, hor_translate, - 0.0f, ver_scale, ver_translate, - 0.0f, 0.0f, 1.0f - }; - - lv_opengles_shader_bind(); - lv_opengles_shader_set_uniform1i("u_ColorDepth", LV_COLOR_DEPTH); - lv_opengles_shader_set_uniform1i("u_Texture", 0); - lv_opengles_shader_set_uniformmatrix3fv("u_VertexTransform", 1, true, matrix); - lv_opengles_shader_set_uniform1f("u_Opa", (float)opa / (float)LV_OPA_100); - lv_opengles_render_draw(); +void lv_opengles_render_fill(lv_color_t color, const lv_area_t * area, lv_opa_t opa, int32_t disp_w, int32_t disp_h) +{ + lv_opengles_render_internal(0, area, opa, disp_w, disp_h, area, false, color); } void lv_opengles_render_clear(void) @@ -207,22 +202,79 @@ void lv_opengles_viewport(int32_t x, int32_t y, int32_t w, int32_t h) * STATIC FUNCTIONS **********************/ +static void lv_opengles_render_internal(unsigned int texture, const lv_area_t * texture_area, lv_opa_t opa, + int32_t disp_w, int32_t disp_h, const lv_area_t * texture_clip_area, bool flip, lv_color_t fill_color) +{ + lv_area_t intersection; + if(!lv_area_intersect(&intersection, texture_area, texture_clip_area)) return; + + GL_CALL(glActiveTexture(GL_TEXTURE0)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, texture)); + + float tex_w = (float)lv_area_get_width(&intersection); + float tex_h = (float)lv_area_get_height(&intersection); + + float hor_scale = tex_w / (float)disp_w; + float ver_scale = tex_h / (float)disp_h; + float hor_translate = (float)intersection.x1 / (float)disp_w * 2.0f - (1.0f - hor_scale); + float ver_translate = -((float)intersection.y1 / (float)disp_h * 2.0f - (1.0f - ver_scale)); + if(flip) ver_scale = -ver_scale; + float matrix[9] = { + hor_scale, 0.0f, hor_translate, + 0.0f, ver_scale, ver_translate, + 0.0f, 0.0f, 1.0f + }; + + if(texture != 0) { + float x_coef = 1.0f / (float)(2 * lv_area_get_width(texture_area)); + float y_coef = 1.0f / (float)(2 * lv_area_get_height(texture_area)); + float tex_clip_x1 = lv_opengles_map_float(texture_clip_area->x1, texture_area->x1, texture_area->x2, x_coef, + 1.0f - x_coef); + float tex_clip_x2 = lv_opengles_map_float(texture_clip_area->x2, texture_area->x1, texture_area->x2, x_coef, + 1.0f - x_coef); + float tex_clip_y1 = lv_opengles_map_float(texture_clip_area->y1, texture_area->y1, texture_area->y2, y_coef, + 1.0f - y_coef); + float tex_clip_y2 = lv_opengles_map_float(texture_clip_area->y2, texture_area->y1, texture_area->y2, y_coef, + 1.0f - y_coef); + + float positions[LV_OPENGLES_VERTEX_BUFFER_LEN] = { + -1.0f, 1.0f, tex_clip_x1, tex_clip_y2, + 1.0f, 1.0f, tex_clip_x2, tex_clip_y2, + 1.0f, -1.0f, tex_clip_x2, tex_clip_y1, + -1.0f, -1.0f, tex_clip_x1, tex_clip_y1 + }; + lv_opengles_vertex_buffer_init(positions, sizeof(positions)); + } + + lv_opengles_shader_bind(); + lv_opengles_shader_set_uniform1i("u_ColorDepth", LV_COLOR_DEPTH); + lv_opengles_shader_set_uniform1i("u_Texture", 0); + lv_opengles_shader_set_uniformmatrix3fv("u_VertexTransform", 1, true, matrix); + lv_opengles_shader_set_uniform1f("u_Opa", (float)opa / (float)LV_OPA_100); + lv_opengles_shader_set_uniform1i("u_IsFill", texture == 0); + lv_opengles_shader_set_uniform3f("u_FillColor", (float)fill_color.red / 255.0f, (float)fill_color.green / 255.0f, + (float)fill_color.blue / 255.0f); + lv_opengles_render_draw(); +} + static void lv_opengles_enable_blending(void) { glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } static void lv_opengles_vertex_buffer_init(const void * data, unsigned int size) { - GL_CALL(glGenBuffers(1, &vertex_buffer_id)); + if(vertex_buffer_id == 0) GL_CALL(glGenBuffers(1, &vertex_buffer_id)); GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id)); - GL_CALL(glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW)); + GL_CALL(glBufferData(GL_ARRAY_BUFFER, size, data, GL_DYNAMIC_DRAW)); } static void lv_opengles_vertex_buffer_deinit(void) { + if(vertex_buffer_id == 0) return; GL_CALL(glDeleteBuffers(1, &vertex_buffer_id)); + vertex_buffer_id = 0; } static void lv_opengles_vertex_buffer_bind(void) @@ -237,12 +289,14 @@ static void lv_opengles_vertex_buffer_unbind(void) static void lv_opengles_vertex_array_init(void) { - GL_CALL(glGenVertexArrays(1, &vertex_array_id)); + if(vertex_array_id == 0) GL_CALL(glGenVertexArrays(1, &vertex_array_id)); } static void lv_opengles_vertex_array_deinit(void) { + if(vertex_array_id == 0) return; GL_CALL(glDeleteVertexArrays(1, &vertex_array_id)); + vertex_array_id = 0; } static void lv_opengles_vertex_array_bind(void) @@ -271,7 +325,7 @@ static void lv_opengles_vertex_array_add_buffer(void) static void lv_opengles_index_buffer_init(const unsigned int * data, unsigned int count) { index_buffer_count = count; - GL_CALL(glGenBuffers(1, &index_buffer_id)); + if(index_buffer_id == 0) GL_CALL(glGenBuffers(1, &index_buffer_id)); GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_id)); @@ -280,7 +334,9 @@ static void lv_opengles_index_buffer_init(const unsigned int * data, unsigned in static void lv_opengles_index_buffer_deinit(void) { + if(index_buffer_id == 0) return; GL_CALL(glDeleteBuffers(1, &index_buffer_id)); + index_buffer_id = 0; } static unsigned int lv_opengles_index_buffer_get_count(void) @@ -300,7 +356,8 @@ static void lv_opengles_index_buffer_unbind(void) static unsigned int lv_opengles_shader_compile(unsigned int type, const char * source) { - GL_CALL(unsigned int id = glCreateShader(type)); + unsigned int id; + GL_CALL(id = glCreateShader(type)); const char * src = source; GL_CALL(glShaderSource(id, 1, &src, NULL)); GL_CALL(glCompileShader(id)); @@ -323,7 +380,8 @@ static unsigned int lv_opengles_shader_compile(unsigned int type, const char * s static unsigned int lv_opengles_shader_create(const char * vertexShader, const char * fragmentShader) { - GL_CALL(unsigned int program = glCreateProgram()); + unsigned int program; + GL_CALL(program = glCreateProgram()); unsigned int vs = lv_opengles_shader_compile(GL_VERTEX_SHADER, vertexShader); unsigned int fs = lv_opengles_shader_compile(GL_FRAGMENT_SHADER, fragmentShader); @@ -340,12 +398,14 @@ static unsigned int lv_opengles_shader_create(const char * vertexShader, const c static void lv_opengles_shader_init(void) { - shader_id = lv_opengles_shader_create(vertex_shader, fragment_shader); + if(shader_id == 0) shader_id = lv_opengles_shader_create(vertex_shader, fragment_shader); } static void lv_opengles_shader_deinit(void) { + if(shader_id == 0) return; GL_CALL(glDeleteProgram(shader_id)); + shader_id = 0; } static void lv_opengles_shader_bind(void) @@ -374,7 +434,8 @@ static int lv_opengles_shader_get_uniform_location(const char * name) return shader_location[id]; } - GL_CALL(int location = glGetUniformLocation(shader_id, name)); + int location; + GL_CALL(location = glGetUniformLocation(shader_id, name)); if(location == -1) LV_LOG_WARN("Warning: uniform '%s' doesn't exist!", name); @@ -397,6 +458,11 @@ static void lv_opengles_shader_set_uniform1f(const char * name, float value) GL_CALL(glUniform1f(lv_opengles_shader_get_uniform_location(name), value)); } +static void lv_opengles_shader_set_uniform3f(const char * name, float value_0, float value_1, float value_2) +{ + GL_CALL(glUniform3f(lv_opengles_shader_get_uniform_location(name), value_0, value_1, value_2)); +} + static void lv_opengles_render_draw(void) { lv_opengles_shader_bind(); @@ -406,4 +472,28 @@ static void lv_opengles_render_draw(void) GL_CALL(glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, NULL)); } +/** + * Copied from `lv_map` in lv_math.h to operate on floats + */ +static float lv_opengles_map_float(float x, float min_in, float max_in, float min_out, float max_out) +{ + if(max_in >= min_in && x >= max_in) return max_out; + if(max_in >= min_in && x <= min_in) return min_out; + + if(max_in <= min_in && x <= max_in) return max_out; + if(max_in <= min_in && x >= min_in) return min_out; + + /** + * The equation should be: + * ((x - min_in) * delta_out) / delta in) + min_out + * To avoid rounding error reorder the operations: + * (x - min_in) * (delta_out / delta_min) + min_out + */ + + float delta_in = max_in - min_in; + float delta_out = max_out - min_out; + + return ((x - min_in) * delta_out) / delta_in + min_out; +} + #endif /* LV_USE_OPENGLES */ diff --git a/src/drivers/glfw/lv_opengles_driver.h b/src/drivers/glfw/lv_opengles_driver.h index e539febb9..34bda861d 100644 --- a/src/drivers/glfw/lv_opengles_driver.h +++ b/src/drivers/glfw/lv_opengles_driver.h @@ -49,11 +49,21 @@ void lv_opengles_deinit(void); * @param texture OpenGL texture ID * @param texture_area the area in the window to render the texture in * @param opa opacity to blend the texture with existing contents - * @param disp_w width of the window being rendered to - * @param disp_h height of the window being rendered to + * @param disp_w width of the window/framebuffer being rendered to + * @param disp_h height of the window/framebuffer being rendered to */ void lv_opengles_render_texture(unsigned int texture, const lv_area_t * texture_area, lv_opa_t opa, int32_t disp_w, - int32_t disp_h); + int32_t disp_h, const lv_area_t * texture_clip_area, bool flip); + +/** + * Render a fill + * @param color the color of the fill + * @param area the area in the window to render the fill + * @param opa opacity to blend the fill with existing contents + * @param disp_w width of the window/framebuffer being rendered to + * @param disp_h height of the window/framebuffer being rendered to + */ +void lv_opengles_render_fill(lv_color_t color, const lv_area_t * area, lv_opa_t opa, int32_t disp_w, int32_t disp_h); /** * Clear the window/display diff --git a/src/drivers/glfw/lv_opengles_texture.c b/src/drivers/glfw/lv_opengles_texture.c index 0a68296c1..a719a7846 100644 --- a/src/drivers/glfw/lv_opengles_texture.c +++ b/src/drivers/glfw/lv_opengles_texture.c @@ -71,12 +71,32 @@ lv_display_t * lv_opengles_texture_create(int32_t w, int32_t h) GL_CALL(glGenTextures(1, &dsc->texture_id)); GL_CALL(glBindTexture(GL_TEXTURE_2D, dsc->texture_id)); - GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + /* set the dimensions and format to complete the texture */ + /* Color depth: 8 (L8), 16 (RGB565), 24 (RGB888), 32 (XRGB8888) */ +#if LV_COLOR_DEPTH == 8 + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, disp->hor_res, disp->ver_res, 0, GL_RED, GL_UNSIGNED_BYTE, NULL)); +#elif LV_COLOR_DEPTH == 16 + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, disp->hor_res, disp->ver_res, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, + NULL)); +#elif LV_COLOR_DEPTH == 24 + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, disp->hor_res, disp->ver_res, 0, GL_BGR, GL_UNSIGNED_BYTE, NULL)); +#elif LV_COLOR_DEPTH == 32 + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, disp->hor_res, disp->ver_res, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL)); +#else +#error("Unsupported color format") +#endif + + glGenerateMipmap(GL_TEXTURE_2D); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 20); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + lv_display_set_buffers(disp, dsc->fb1, NULL, buf_size, LV_DISPLAY_RENDER_MODE_DIRECT); lv_display_set_flush_cb(disp, flush_cb); lv_display_set_driver_data(disp, dsc); @@ -115,6 +135,7 @@ static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_m LV_UNUSED(area); LV_UNUSED(px_map); +#if !LV_USE_DRAW_OPENGLES if(lv_display_flush_is_last(disp)) { lv_opengles_texture_t * dsc = lv_display_get_driver_data(disp); @@ -125,7 +146,7 @@ static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_m GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, stride / lv_color_format_get_size(cf))); - /*Color depth: 8 (A8), 16 (RGB565), 24 (RGB888), 32 (XRGB8888)*/ + /*Color depth: 8 (L8), 16 (RGB565), 24 (RGB888), 32 (XRGB8888)*/ #if LV_COLOR_DEPTH == 8 GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, disp->hor_res, disp->ver_res, 0, GL_RED, GL_UNSIGNED_BYTE, dsc->fb1)); #elif LV_COLOR_DEPTH == 16 @@ -139,6 +160,7 @@ static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_m #error("Unsupported color format") #endif } +#endif /* !LV_USE_DRAW_OPENGLES */ lv_display_flush_ready(disp); } @@ -148,6 +170,7 @@ static void release_disp_cb(lv_event_t * e) lv_display_t * disp = lv_event_get_user_data(e); lv_opengles_texture_t * dsc = lv_display_get_driver_data(disp); free(dsc->fb1); + GL_CALL(glDeleteTextures(1, &dsc->texture_id)); lv_free(dsc); } diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index fe29364b4..1bbd365e5 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -831,7 +831,7 @@ #endif #endif -/* Accelerate blends, fills, etc. with STM32 DMA2D */ +/** Accelerate blends, fills, etc. with STM32 DMA2D */ #ifndef LV_USE_DRAW_DMA2D #ifdef CONFIG_LV_USE_DRAW_DMA2D #define LV_USE_DRAW_DMA2D CONFIG_LV_USE_DRAW_DMA2D @@ -861,6 +861,15 @@ #endif #endif +/** Draw using cached OpenGLES textures */ +#ifndef LV_USE_DRAW_OPENGLES + #ifdef CONFIG_LV_USE_DRAW_OPENGLES + #define LV_USE_DRAW_OPENGLES CONFIG_LV_USE_DRAW_OPENGLES + #else + #define LV_USE_DRAW_OPENGLES 0 + #endif +#endif + /*======================= * FEATURE CONFIGURATION *=======================*/ diff --git a/src/lv_init.c b/src/lv_init.c index 0939dd8a5..14bb4f749 100644 --- a/src/lv_init.c +++ b/src/lv_init.c @@ -63,6 +63,9 @@ #if LV_USE_DRAW_DMA2D #include "draw/dma2d/lv_draw_dma2d.h" #endif +#if LV_USE_DRAW_OPENGLES + #include "draw/opengles/lv_draw_opengles.h" +#endif #if LV_USE_WINDOWS #include "drivers/windows/lv_windows_context.h" #endif @@ -228,6 +231,10 @@ void lv_init(void) lv_draw_dma2d_init(); #endif +#if LV_USE_DRAW_OPENGLES + lv_draw_opengles_init(); +#endif + #if LV_USE_WINDOWS lv_windows_platform_init(); #endif @@ -421,6 +428,10 @@ void lv_deinit(void) lv_draw_dma2d_deinit(); #endif +#if LV_USE_DRAW_OPENGLES + lv_draw_opengles_deinit(); +#endif + #if LV_USE_DRAW_SW lv_draw_sw_deinit(); #endif