feat(nanovg): add draw 3D support (#9571)
Some checks failed
Arduino Lint / lint (push) Has been cancelled
Build Examples with C++ Compiler / build-examples (push) Has been cancelled
MicroPython CI / Build esp32 port (push) Has been cancelled
MicroPython CI / Build rp2 port (push) Has been cancelled
MicroPython CI / Build stm32 port (push) Has been cancelled
MicroPython CI / Build unix port (push) Has been cancelled
C/C++ CI / Build OPTIONS_16BIT - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_24BIT - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_FULL_32BIT - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_NORMAL_8BIT - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_SDL - Ubuntu (push) Has been cancelled
C/C++ CI / Build OPTIONS_16BIT - cl - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_16BIT - gcc - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_24BIT - cl - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_24BIT - gcc - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_FULL_32BIT - cl - Windows (push) Has been cancelled
C/C++ CI / Build OPTIONS_FULL_32BIT - gcc - Windows (push) Has been cancelled
C/C++ CI / Build ESP IDF ESP32S3 (push) Has been cancelled
C/C++ CI / Run tests with 32bit build (push) Has been cancelled
C/C++ CI / Run tests with 64bit build (push) Has been cancelled
BOM Check / bom-check (push) Has been cancelled
Verify that lv_conf_internal.h matches repository state / verify-conf-internal (push) Has been cancelled
Verify the widget property name / verify-property-name (push) Has been cancelled
Verify code formatting / verify-formatting (push) Has been cancelled
Compare file templates with file names / template-check (push) Has been cancelled
Build docs / build-and-deploy (push) Has been cancelled
Test API JSON generator / Test API JSON (push) Has been cancelled
Install LVGL using CMake / build-examples (push) Has been cancelled
Check Makefile / Build using Makefile (push) Has been cancelled
Check Makefile for UEFI / Build using Makefile for UEFI (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark - Script Check (scripts/perf/tests/benchmark_results_comment/test.sh) (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark - Script Check (scripts/perf/tests/filter_docker_logs/test.sh) (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark - Script Check (scripts/perf/tests/serialize_results/test.sh) (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark 32b - lv_conf_perf32b (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark 64b - lv_conf_perf64b (push) Has been cancelled
Emulated Performance Test / ARM Emulated Benchmark - Save PR Number (push) Has been cancelled
Hardware Performance Test / Hardware Performance Benchmark (push) Has been cancelled
Hardware Performance Test / HW Benchmark - Save PR Number (push) Has been cancelled
Performance Tests CI / Perf Tests OPTIONS_TEST_PERF_32B - Ubuntu (push) Has been cancelled
Performance Tests CI / Perf Tests OPTIONS_TEST_PERF_64B - Ubuntu (push) Has been cancelled
Port repo release update / run-release-branch-updater (push) Has been cancelled
Verify Font License / verify-font-license (push) Has been cancelled
Verify Kconfig / verify-kconfig (push) Has been cancelled

Signed-off-by: pengyiqiang <pengyiqiang@xiaomi.com>
This commit is contained in:
VIFEX
2026-01-27 16:11:05 +08:00
committed by GitHub
parent b5b73db5f7
commit d7b3e00300
8 changed files with 288 additions and 7 deletions

View File

@@ -204,6 +204,12 @@ static void draw_execute(lv_draw_nanovg_unit_t * u, lv_draw_task_t * t)
lv_draw_nanovg_vector(t, t->draw_dsc);
break;
#endif
#if LV_USE_3DTEXTURE
case LV_DRAW_TASK_TYPE_3D:
lv_draw_nanovg_3d(t, t->draw_dsc, &t->area);
break;
#endif
default:
LV_LOG_ERROR("unknown draw task type: %d", t->type);
break;
@@ -381,6 +387,9 @@ static int32_t draw_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task)
case LV_DRAW_TASK_TYPE_MASK_RECTANGLE:
#if LV_USE_VECTOR_GRAPHIC
case LV_DRAW_TASK_TYPE_VECTOR:
#endif
#if LV_USE_3DTEXTURE
case LV_DRAW_TASK_TYPE_3D:
#endif
break;

View File

@@ -0,0 +1,82 @@
/**
* @file lv_draw_nanovg_3d.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_draw_nanovg_private.h"
#if LV_USE_DRAW_NANOVG && LV_USE_3DTEXTURE
#include "../../draw/lv_draw_3d.h"
#include "../../drivers/opengles/lv_opengles_driver.h"
#include "../../drivers/opengles/lv_opengles_private.h"
#include "lv_nanovg_utils.h"
#include "lv_nanovg_fbo_cache.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_draw_nanovg_3d(lv_draw_task_t * t, const lv_draw_3d_dsc_t * dsc, const lv_area_t * coords)
{
LV_PROFILER_DRAW_BEGIN;
lv_draw_nanovg_unit_t * u = (lv_draw_nanovg_unit_t *)t->draw_unit;
/* End NanoVG frame temporarily to allow direct OpenGL rendering */
lv_nanovg_end_frame(u);
lv_layer_t * layer = t->target_layer;
/* Get target layer info */
int32_t layer_w = lv_area_get_width(&layer->buf_area);
int32_t layer_h = lv_area_get_height(&layer->buf_area);
/* Calculate destination area relative to layer */
lv_area_t dest_area = *coords;
lv_area_move(&dest_area, -layer->buf_area.x1, -layer->buf_area.y1);
/* Calculate clip area relative to layer */
lv_area_t clip_area = t->clip_area;
lv_area_move(&clip_area, -layer->buf_area.x1, -layer->buf_area.y1);
/* Reinitialize OpenGL ES driver state after NanoVG modified it */
lv_opengles_reinit_state();
/* Use LVGL's OpenGL ES rendering infrastructure */
lv_opengles_viewport(0, 0, layer_w, layer_h);
lv_opengles_render_texture_rbswap(dsc->tex_id, &dest_area, dsc->opa, layer_w, layer_h,
&clip_area, dsc->h_flip, !dsc->v_flip);
LV_PROFILER_DRAW_END;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /* LV_USE_DRAW_NANOVG && LV_USE_3DTEXTURE */

View File

@@ -22,6 +22,7 @@ extern "C" {
#include "../../draw/lv_draw_vector.h"
#include "../../draw/lv_draw_arc.h"
#include "../../draw/lv_draw_rect.h"
#include "../../draw/lv_draw_3d.h"
#include "../../draw/lv_draw_image.h"
#include "../../draw/lv_draw_label.h"
#include "../../draw/lv_draw_line.h"
@@ -84,6 +85,16 @@ typedef struct _lv_draw_nanovg_unit_t {
* GLOBAL PROTOTYPES
**********************/
#if LV_USE_3DTEXTURE
/**
* Draw 3D texture on a NanoVG context
* @param t pointer to a drawing task
* @param dsc pointer to a 3D draw descriptor
* @param coords the coordinates of the 3D texture
*/
void lv_draw_nanovg_3d(lv_draw_task_t * t, const lv_draw_3d_dsc_t * dsc, const lv_area_t * coords);
#endif
/**
* Draw arc on a NanoVG context
* @param t pointer to a drawing task

View File

@@ -239,6 +239,24 @@ void lv_opengles_viewport(int32_t x, int32_t y, int32_t w, int32_t h)
LV_PROFILER_DRAW_END;
}
void lv_opengles_reinit_state(void)
{
LV_PROFILER_DRAW_BEGIN;
/* Rebind VAO, VBO, IBO to restore state after NanoVG or other external GL operations */
lv_opengles_vertex_array_bind();
lv_opengles_vertex_buffer_bind();
lv_opengles_index_buffer_bind();
/* Re-setup vertex attributes since NanoVG may have modified them */
for(unsigned int i = 0; i < 2; i++) {
GL_CALL(glEnableVertexAttribArray(i));
GL_CALL(glVertexAttribPointer(i, 2, GL_FLOAT, GL_FALSE, 16, (const void *)(intptr_t)(i * 2 * 4)));
}
LV_PROFILER_DRAW_END;
}
void lv_opengles_render(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 h_flip, bool v_flip, lv_color_t fill_color, bool blend_opt, bool swap_red_blue)

View File

@@ -89,6 +89,12 @@ void lv_opengles_render_clear(void);
*/
void lv_opengles_viewport(int32_t x, int32_t y, int32_t w, int32_t h);
/**
* Reinitialize OpenGL state after external GL operations (e.g., NanoVG)
* This rebinds VAO, VBO, IBO and resets vertex attributes
*/
void lv_opengles_reinit_state(void);
/**********************
* MACROS
**********************/

View File

@@ -104,6 +104,33 @@ static inline GLsizei get_level_count(int32_t width, int32_t height)
return static_cast<GLsizei>(1 + floor(log2(width > height ? width : height)));
}
/**
* @brief Allocate immutable texture storage with fallback for GLES2
*
* glTexStorage2D (GL_EXT_texture_storage) may not be available on all GLES2 drivers.
* This function falls back to glTexImage2D when the extension is not available.
*/
static inline void tex_storage_2d_compat(GLenum target, GLsizei levels, GLenum internalformat,
GLsizei width, GLsizei height)
{
#ifdef glTexStorage2D
if(glad_glTexStorage2DEXT) {
glTexStorage2D(target, levels, internalformat, width, height);
return;
}
#endif
/* Fallback: use glTexImage2D for each mipmap level */
GLenum format = GL_RGBA;
if(internalformat == GL_RGB8) {
format = GL_RGB;
}
for(GLsizei level = 0; level < levels; level++) {
glTexImage2D(target, level, internalformat, width, height, 0, format, GL_UNSIGNED_BYTE, NULL);
width = (width > 1) ? (width / 2) : 1;
height = (height > 1) ? (height / 2) : 1;
}
}
static void load_mesh_texture_impl(lv_gltf_model_t * data, const fastgltf::TextureInfo & material_prop,
GLuint * primitive_tex_prop,
GLint * primitive_tex_uv_id);
@@ -490,7 +517,7 @@ bool injest_image(lv_opengl_shader_manager_t * shader_manager, lv_gltf_model_t *
LV_LOG_TRACE("Loading image: %s", image.name.c_str());
const std::string path(file_path.uri.path().begin(), file_path.uri.path().end());
unsigned char * data = stbi_load(path.c_str(), &width, &height, &nrChannels, 4);
glTexStorage2D(GL_TEXTURE_2D, get_level_count(width, height), GL_RGBA8, width, height);
tex_storage_2d_compat(GL_TEXTURE_2D, get_level_count(width, height), GL_RGBA8, width, height);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
},
@@ -502,7 +529,7 @@ bool injest_image(lv_opengl_shader_manager_t * shader_manager, lv_gltf_model_t *
unsigned char * data = stbi_load_from_memory(
reinterpret_cast<const stbi_uc *>(vector.bytes.data()),
static_cast<int32_t>(vector.bytes.size()), &width, &height, &nrChannels, 4);
glTexStorage2D(GL_TEXTURE_2D, get_level_count(width, height), GL_RGBA8, width, height);
tex_storage_2d_compat(GL_TEXTURE_2D, get_level_count(width, height), GL_RGBA8, width, height);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
},
@@ -562,7 +589,7 @@ static bool injest_image_from_buffer_view(lv_gltf_model_t * data, fastgltf::sour
return true;
}
LV_LOG_TRACE("[WEBP] width: %d height: %d", width, height);
glTexStorage2D(GL_TEXTURE_2D, get_level_count(width, height), GL_RGBA8, width, height);
tex_storage_2d_compat(GL_TEXTURE_2D, get_level_count(width, height), GL_RGBA8, width, height);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
return false;
@@ -580,7 +607,7 @@ static bool injest_image_from_buffer_view(lv_gltf_model_t * data, fastgltf::sour
uint8_t * unpacked = WebPDecodeRGBA(
reinterpret_cast<const uint8_t *>(vector.bytes.data() + buffer_view.byteOffset),
static_cast<std::size_t>(buffer_view.byteLength), &width, &height);
glTexStorage2D(GL_TEXTURE_2D, get_level_count(width, height), GL_RGBA8, width, height);
tex_storage_2d_compat(GL_TEXTURE_2D, get_level_count(width, height), GL_RGBA8, width, height);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE,
unpacked);
WebPFree(unpacked);
@@ -589,7 +616,7 @@ static bool injest_image_from_buffer_view(lv_gltf_model_t * data, fastgltf::sour
uint8_t * unpacked = WebPDecodeRGB(
reinterpret_cast<const uint8_t *>(vector.bytes.data() + buffer_view.byteOffset),
static_cast<std::size_t>(buffer_view.byteLength), &width, &height);
glTexStorage2D(GL_TEXTURE_2D, get_level_count(width, height), GL_RGB8, width, height);
tex_storage_2d_compat(GL_TEXTURE_2D, get_level_count(width, height), GL_RGB8, width, height);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE,
unpacked);
WebPFree(unpacked);

View File

@@ -104,10 +104,45 @@ typedef struct {
} lv_gltf_view_desc_t;
typedef struct {
/* Blend state */
GLboolean blend_enabled;
GLint blend_src;
GLint blend_dst;
GLint blend_equation;
/* Depth state */
GLboolean depth_test_enabled;
GLboolean depth_mask;
GLint depth_func;
/* Face culling state */
GLboolean cull_face_enabled;
GLint cull_face_mode;
GLint front_face;
/* Stencil state */
GLboolean stencil_test_enabled;
GLuint stencil_mask;
GLint stencil_func;
GLint stencil_ref;
GLuint stencil_value_mask;
/* Buffer bindings */
GLuint current_vao;
GLuint current_vbo;
GLuint current_ibo;
GLuint current_program;
/* Texture state */
GLint active_texture;
GLuint bound_texture_2d;
/* Viewport and scissor */
GLint viewport[4];
GLboolean scissor_test_enabled;
GLint scissor_box[4];
/* Clear values */
GLfloat clear_depth;
GLfloat clear_color[4];
} lv_opengl_state_t;

View File

@@ -168,17 +168,59 @@ GLuint lv_gltf_view_render(lv_gltf_t * viewer)
static void lv_gltf_view_push_opengl_state(lv_opengl_state_t * state)
{
/* Blend state */
GL_CALL(glGetBooleanv(GL_BLEND, &state->blend_enabled));
GL_CALL(glGetIntegerv(GL_BLEND_SRC_ALPHA, &state->blend_src));
GL_CALL(glGetIntegerv(GL_BLEND_DST_ALPHA, &state->blend_dst));
GL_CALL(glGetIntegerv(GL_BLEND_EQUATION, &state->blend_equation));
/* Depth state */
GL_CALL(glGetBooleanv(GL_DEPTH_TEST, &state->depth_test_enabled));
GL_CALL(glGetBooleanv(GL_DEPTH_WRITEMASK, &state->depth_mask));
GL_CALL(glGetIntegerv(GL_DEPTH_FUNC, &state->depth_func));
/* Face culling state */
GL_CALL(glGetBooleanv(GL_CULL_FACE, &state->cull_face_enabled));
GL_CALL(glGetIntegerv(GL_CULL_FACE_MODE, &state->cull_face_mode));
GL_CALL(glGetIntegerv(GL_FRONT_FACE, &state->front_face));
/* Stencil state */
GL_CALL(glGetBooleanv(GL_STENCIL_TEST, &state->stencil_test_enabled));
GL_CALL(glGetIntegerv(GL_STENCIL_WRITEMASK, (GLint *)&state->stencil_mask));
GL_CALL(glGetIntegerv(GL_STENCIL_FUNC, &state->stencil_func));
GL_CALL(glGetIntegerv(GL_STENCIL_REF, &state->stencil_ref));
GL_CALL(glGetIntegerv(GL_STENCIL_VALUE_MASK, (GLint *)&state->stencil_value_mask));
/* Buffer bindings */
#ifndef GL_VERTEX_ARRAY_BINDING
#ifdef GL_VERTEX_ARRAY_BINDING_OES
#define GL_VERTEX_ARRAY_BINDING GL_VERTEX_ARRAY_BINDING_OES
#else
#define GL_VERTEX_ARRAY_BINDING 0x85B5
#endif
#endif
GL_CALL(glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint *)&state->current_vao));
GL_CALL(glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint *)&state->current_vbo));
GL_CALL(glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, (GLint *)&state->current_ibo));
GL_CALL(glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)&state->current_program));
/* Texture state */
GL_CALL(glGetIntegerv(GL_ACTIVE_TEXTURE, &state->active_texture));
GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *)&state->bound_texture_2d));
/* Viewport and scissor */
GL_CALL(glGetIntegerv(GL_VIEWPORT, state->viewport));
GL_CALL(glGetBooleanv(GL_SCISSOR_TEST, &state->scissor_test_enabled));
GL_CALL(glGetIntegerv(GL_SCISSOR_BOX, state->scissor_box));
/* Clear values */
GL_CALL(glGetFloatv(GL_COLOR_CLEAR_VALUE, state->clear_color));
GL_CALL(glGetFloatv(GL_DEPTH_CLEAR_VALUE, &state->clear_depth));
}
static void lv_gltf_view_pop_opengl_state(const lv_opengl_state_t * state)
{
GL_CALL(glDisable(GL_CULL_FACE));
/* Restore blend state */
if(state->blend_enabled) {
GL_CALL(glEnable(GL_BLEND));
}
@@ -187,7 +229,58 @@ static void lv_gltf_view_pop_opengl_state(const lv_opengl_state_t * state)
}
GL_CALL(glBlendFunc(state->blend_src, state->blend_dst));
GL_CALL(glBlendEquation(state->blend_equation));
GL_CALL(glDepthMask(GL_TRUE));
/* Restore depth state */
if(state->depth_test_enabled) {
GL_CALL(glEnable(GL_DEPTH_TEST));
}
else {
GL_CALL(glDisable(GL_DEPTH_TEST));
}
GL_CALL(glDepthMask(state->depth_mask));
GL_CALL(glDepthFunc(state->depth_func));
/* Restore face culling state */
if(state->cull_face_enabled) {
GL_CALL(glEnable(GL_CULL_FACE));
}
else {
GL_CALL(glDisable(GL_CULL_FACE));
}
GL_CALL(glCullFace(state->cull_face_mode));
GL_CALL(glFrontFace(state->front_face));
/* Restore stencil state */
if(state->stencil_test_enabled) {
GL_CALL(glEnable(GL_STENCIL_TEST));
}
else {
GL_CALL(glDisable(GL_STENCIL_TEST));
}
GL_CALL(glStencilMask(state->stencil_mask));
GL_CALL(glStencilFunc(state->stencil_func, state->stencil_ref, state->stencil_value_mask));
/* Restore buffer bindings */
GL_CALL(glBindVertexArray(state->current_vao));
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, state->current_vbo));
GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, state->current_ibo));
GL_CALL(glUseProgram(state->current_program));
/* Restore texture state */
GL_CALL(glActiveTexture(state->active_texture));
GL_CALL(glBindTexture(GL_TEXTURE_2D, state->bound_texture_2d));
/* Restore viewport and scissor */
GL_CALL(glViewport(state->viewport[0], state->viewport[1], state->viewport[2], state->viewport[3]));
if(state->scissor_test_enabled) {
GL_CALL(glEnable(GL_SCISSOR_TEST));
}
else {
GL_CALL(glDisable(GL_SCISSOR_TEST));
}
GL_CALL(glScissor(state->scissor_box[0], state->scissor_box[1], state->scissor_box[2], state->scissor_box[3]));
/* Restore clear values */
GL_CALL(glClearColor(state->clear_color[0], state->clear_color[1], state->clear_color[2], state->clear_color[3]));
GL_CALL(glClearDepthf(state->clear_depth));
}