mirror of
https://github.com/lvgl/lvgl.git
synced 2024-11-23 09:43:41 +08:00
feat(opengl): texture caching (#6861)
Co-authored-by: JWBverheesen <31246830+JWBverheesen@users.noreply.github.com>
This commit is contained in:
parent
30193c1035
commit
0efa5f3758
5
Kconfig
5
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"
|
||||
|
@ -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 <GL/glew.h>
|
||||
@ -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.
|
||||
|
@ -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
|
||||
*=======================*/
|
||||
|
593
src/draw/opengles/lv_draw_opengles.c
Normal file
593
src/draw/opengles/lv_draw_opengles.c
Normal file
@ -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 <GL/glew.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#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*/
|
45
src/draw/opengles/lv_draw_opengles.h
Normal file
45
src/draw/opengles/lv_draw_opengles.h
Normal file
@ -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*/
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**********************
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
*=======================*/
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user