feat(live2d): implement OpenGL ES 2.0 rendering backend with EGL support
- Add `gl.rs` for OpenGL ES 2.0 and EGL context management. - Introduce `mod.rs` to organize Live2D plugin modules. - Create `model.rs` for loading and managing Cubism models, including parameter handling and drawable data extraction. - Implement shader compilation and rendering context setup for Live2D models.
This commit is contained in:
736
src/plugins/live2d/gl.rs
Normal file
736
src/plugins/live2d/gl.rs
Normal file
@@ -0,0 +1,736 @@
|
||||
//! OpenGL ES 2.0 / EGL 渲染后端
|
||||
//!
|
||||
//! 使用 EGL 创建离屏 PBuffer 上下文(无窗口),用 OpenGL ES 2.0 渲染 Live2D 模型。
|
||||
//! 渲染结果通过 glReadPixels 读取,交给 video 插件或直接显示。
|
||||
//!
|
||||
//! 这里采用 X11 窗口方式渲染(设备有 X11 + kwin),直接显示到屏幕。
|
||||
//! 如需离屏渲染可改为 PBuffer。
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use std::ffi::c_void;
|
||||
use std::os::raw::{c_char, c_int, c_uint, c_void as c_void_raw};
|
||||
|
||||
// ============ EGL 类型与常量 ============
|
||||
|
||||
pub mod egl {
|
||||
pub const EGL_VERSION_1_5: c_int = 1;
|
||||
|
||||
pub const EGL_SURFACE_TYPE: c_int = 0x3033;
|
||||
pub const EGL_WINDOW_BIT: c_int = 0x0004;
|
||||
pub const EGL_PBUFFER_BIT: c_int = 0x0001;
|
||||
pub const EGL_BLUE_SIZE: c_int = 0x3022;
|
||||
pub const EGL_GREEN_SIZE: c_int = 0x3023;
|
||||
pub const EGL_RED_SIZE: c_int = 0x3024;
|
||||
pub const EGL_DEPTH_SIZE: c_int = 0x3025;
|
||||
pub const EGL_ALPHA_SIZE: c_int = 0x3021;
|
||||
pub const EGL_STENCIL_SIZE: c_int = 0x3026;
|
||||
pub const EGL_RENDERABLE_TYPE: c_int = 0x3040;
|
||||
pub const EGL_OPENGL_ES2_BIT: c_int = 0x0004;
|
||||
pub const EGL_NONE: c_int = 0x3038;
|
||||
pub const EGL_CONTEXT_CLIENT_VERSION: c_int = 0x3098;
|
||||
|
||||
pub const EGL_DISPLAY: usize = 0;
|
||||
pub const EGL_NO_DISPLAY: isize = 0;
|
||||
pub const EGL_NO_CONTEXT: isize = 0;
|
||||
pub const EGL_NO_SURFACE: isize = 0;
|
||||
|
||||
pub type EGLDisplay = *mut c_void;
|
||||
pub type EGLConfig = *mut c_void;
|
||||
pub type EGLContext = *mut c_void;
|
||||
pub type EGLSurface = *mut c_void;
|
||||
pub type NativeDisplayType = *mut c_void;
|
||||
pub type NativeWindowType = *mut c_void;
|
||||
pub type EGLBoolean = c_uint;
|
||||
pub type EGLint = c_int;
|
||||
}
|
||||
|
||||
// ============ X11 类型 ============
|
||||
|
||||
pub mod x11 {
|
||||
use std::os::raw::{c_long, c_uint, c_void};
|
||||
|
||||
pub type Display = c_void;
|
||||
pub type Window = c_long;
|
||||
pub type XID = c_long;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct XSetWindowAttributes {
|
||||
pub background_pixmap: c_long,
|
||||
pub background_pixel: c_long,
|
||||
pub border_pixmap: c_long,
|
||||
pub border_pixel: c_long,
|
||||
pub bit_gravity: c_int,
|
||||
pub win_gravity: c_int,
|
||||
pub backing_store: c_int,
|
||||
pub backing_planes: c_long,
|
||||
pub backing_pixel: c_long,
|
||||
pub save_under: c_int,
|
||||
pub event_mask: c_long,
|
||||
pub do_not_propagate_mask: c_long,
|
||||
pub override_redirect: c_int,
|
||||
pub colormap: c_long,
|
||||
pub cursor: c_long,
|
||||
}
|
||||
|
||||
pub const CWOverrideRedirect: c_long = 1 << 9;
|
||||
pub const CWEventMask: c_long = 1 << 11;
|
||||
pub const ExposureMask: c_long = 1 << 15;
|
||||
pub const StructureNotifyMask: c_long = 1 << 17;
|
||||
pub const KeyPressMask: c_long = 1 << 0;
|
||||
}
|
||||
|
||||
// ============ GLES2 类型与常量 ============
|
||||
|
||||
pub mod gl {
|
||||
use std::os::raw::{c_int, c_uint};
|
||||
|
||||
pub type GLenum = c_uint;
|
||||
pub type GLuint = c_uint;
|
||||
pub type GLint = c_int;
|
||||
pub type GLsizei = c_int;
|
||||
pub type GLbitfield = c_uint;
|
||||
pub type GLfloat = f32;
|
||||
|
||||
pub const GL_FLOAT: GLenum = 0x1406;
|
||||
pub const GL_FLOAT_VEC2: GLenum = 0x8B50;
|
||||
pub const GL_FLOAT_VEC4: GLenum = 0x8B52;
|
||||
pub const GL_FLOAT_MAT4: GLenum = 0x8B5C;
|
||||
pub const GL_SAMPLER_2D: GLenum = 0x8B5E;
|
||||
|
||||
pub const GL_ARRAY_BUFFER: GLenum = 0x8892;
|
||||
pub const GL_ELEMENT_ARRAY_BUFFER: GLenum = 0x8893;
|
||||
|
||||
pub const GL_TEXTURE_2D: GLenum = 0x0DE1;
|
||||
pub const GL_TEXTURE0: GLenum = 0x84C0;
|
||||
pub const GL_RGBA: GLenum = 0x1908;
|
||||
pub const GL_UNSIGNED_BYTE: GLenum = 0x1401;
|
||||
pub const GL_UNSIGNED_SHORT: GLenum = 0x1403;
|
||||
pub const GL_NEAREST: GLenum = 0x2600;
|
||||
pub const GL_LINEAR: GLenum = 0x2601;
|
||||
pub const GL_CLAMP_TO_EDGE: GLenum = 0x812F;
|
||||
pub const GL_TEXTURE_MIN_FILTER: GLenum = 0x2801;
|
||||
pub const GL_TEXTURE_MAG_FILTER: GLenum = 0x2800;
|
||||
pub const GL_TEXTURE_WRAP_S: GLenum = 0x2802;
|
||||
pub const GL_TEXTURE_WRAP_T: GLenum = 0x2803;
|
||||
|
||||
pub const GL_VERTEX_SHADER: GLenum = 0x8B31;
|
||||
pub const GL_FRAGMENT_SHADER: GLenum = 0x8B30;
|
||||
pub const GL_COMPILE_STATUS: GLenum = 0x8B81;
|
||||
pub const GL_LINK_STATUS: GLenum = 0x8B82;
|
||||
|
||||
pub const GL_TRIANGLES: GLenum = 0x0004;
|
||||
pub const GL_BLEND: GLenum = 0x0BE2;
|
||||
pub const GL_SRC_ALPHA: GLenum = 0x0302;
|
||||
pub const GL_ONE_MINUS_SRC_ALPHA: GLenum = 0x0303;
|
||||
pub const GL_ONE: GLenum = 1;
|
||||
pub const GL_ZERO: GLenum = 0;
|
||||
pub const GL_COLOR_BUFFER_BIT: GLbitfield = 0x4000;
|
||||
pub const GL_DEPTH_BUFFER_BIT: GLbitfield = 0x0100;
|
||||
pub const GL_BLEND_DST_RGB: GLenum = 0x80C8;
|
||||
pub const GL_BLEND_SRC_RGB: GLenum = 0x80C9;
|
||||
pub const GL_BLEND_DST_ALPHA: GLenum = 0x80CA;
|
||||
pub const GL_BLEND_SRC_ALPHA: GLenum = 0x80CB;
|
||||
pub const GL_BLEND_EQUATION: GLenum = 0x8009;
|
||||
pub const GL_FUNC_ADD: GLenum = 0x8006;
|
||||
pub const GL_FUNC_REVERSE_SUBTRACT: GLenum = 0x800B;
|
||||
pub const GL_STATIC_DRAW: GLenum = 0x88E4;
|
||||
pub const GL_FALSE: GLenum = 0;
|
||||
pub const GL_TRUE: GLenum = 1;
|
||||
pub const GL_NO_ERROR: GLenum = 0;
|
||||
}
|
||||
|
||||
/// 着色器源码(标准模式,支持 mask 和非 mask)
|
||||
pub const VERT_SHADER_SRC: &str = r#"#version 100
|
||||
attribute vec4 a_position;
|
||||
attribute vec2 a_texCoord;
|
||||
varying vec2 v_texCoord;
|
||||
uniform mat4 u_matrix;
|
||||
void main() {
|
||||
gl_Position = u_matrix * a_position;
|
||||
v_texCoord = a_texCoord;
|
||||
v_texCoord.y = 1.0 - v_texCoord.y;
|
||||
}
|
||||
"#;
|
||||
|
||||
pub const FRAG_SHADER_SRC: &str = r#"#version 100
|
||||
precision highp float;
|
||||
varying vec2 v_texCoord;
|
||||
uniform sampler2D s_texture0;
|
||||
uniform vec4 u_baseColor;
|
||||
uniform vec4 u_multiplyColor;
|
||||
uniform vec4 u_screenColor;
|
||||
void main() {
|
||||
vec4 texColor = texture2D(s_texture0, v_texCoord);
|
||||
texColor.rgb = texColor.rgb * u_multiplyColor.rgb;
|
||||
texColor.rgb = texColor.rgb + u_screenColor.rgb - (texColor.rgb * u_screenColor.rgb);
|
||||
vec4 color = texColor * u_baseColor;
|
||||
gl_FragColor = vec4(color.rgb * color.a, color.a);
|
||||
}
|
||||
"#;
|
||||
|
||||
/// OpenGL ES2 / EGL 动态库函数集合
|
||||
pub struct Gl {
|
||||
_lib_egl: libloading::Library,
|
||||
_lib_gl: libloading::Library,
|
||||
_lib_x11: libloading::Library,
|
||||
// EGL
|
||||
egl_get_display:
|
||||
unsafe extern "C" fn(disp: egl::NativeDisplayType) -> egl::EGLDisplay,
|
||||
egl_initialize:
|
||||
unsafe extern "C" fn(dpy: egl::EGLDisplay, major: *mut c_int, minor: *mut c_int) -> egl::EGLBoolean,
|
||||
egl_choose_config: unsafe extern "C" fn(
|
||||
dpy: egl::EGLDisplay,
|
||||
attrib_list: *const egl::EGLint,
|
||||
configs: *mut egl::EGLConfig,
|
||||
config_size: egl::EGLint,
|
||||
num_config: *mut egl::EGLint,
|
||||
) -> egl::EGLBoolean,
|
||||
egl_create_window_surface: unsafe extern "C" fn(
|
||||
dpy: egl::EGLDisplay,
|
||||
config: egl::EGLConfig,
|
||||
win: egl::NativeWindowType,
|
||||
attrib_list: *const egl::EGLint,
|
||||
) -> egl::EGLSurface,
|
||||
egl_create_context: unsafe extern "C" fn(
|
||||
dpy: egl::EGLDisplay,
|
||||
config: egl::EGLConfig,
|
||||
share_ctx: egl::EGLContext,
|
||||
attrib_list: *const egl::EGLint,
|
||||
) -> egl::EGLContext,
|
||||
egl_make_current: unsafe extern "C" fn(
|
||||
dpy: egl::EGLDisplay,
|
||||
draw: egl::EGLSurface,
|
||||
read: egl::EGLSurface,
|
||||
ctx: egl::EGLContext,
|
||||
) -> egl::EGLBoolean,
|
||||
egl_swap_buffers:
|
||||
unsafe extern "C" fn(dpy: egl::EGLDisplay, surface: egl::EGLSurface) -> egl::EGLBoolean,
|
||||
egl_terminate: unsafe extern "C" fn(dpy: egl::EGLDisplay) -> egl::EGLBoolean,
|
||||
|
||||
// GLES2
|
||||
gl_clear: unsafe extern "C" fn(mask: gl::GLbitfield),
|
||||
gl_clear_color: unsafe extern "C" fn(r: f32, g: f32, b: f32, a: f32),
|
||||
gl_enable: unsafe extern "C" fn(cap: gl::GLenum),
|
||||
gl_disable: unsafe extern "C" fn(cap: gl::GLenum),
|
||||
gl_blend_func: unsafe extern "C" fn(sfactor: gl::GLenum, dfactor: gl::GLenum),
|
||||
gl_blend_func_separate: unsafe extern "C" fn(
|
||||
src_rgb: gl::GLenum,
|
||||
dst_rgb: gl::GLenum,
|
||||
src_alpha: gl::GLenum,
|
||||
dst_alpha: gl::GLenum,
|
||||
),
|
||||
gl_viewport: unsafe extern "C" fn(x: gl::GLint, y: gl::GLint, w: gl::GLsizei, h: gl::GLsizei),
|
||||
gl_create_shader: unsafe extern "C" fn(t: gl::GLenum) -> gl::GLuint,
|
||||
gl_shader_source: unsafe extern "C" fn(
|
||||
shader: gl::GLuint,
|
||||
count: gl::GLsizei,
|
||||
string: *const *const c_char,
|
||||
length: *const gl::GLint,
|
||||
),
|
||||
gl_compile_shader: unsafe extern "C" fn(shader: gl::GLuint),
|
||||
gl_get_shaderiv: unsafe extern "C" fn(
|
||||
shader: gl::GLuint,
|
||||
pname: gl::GLenum,
|
||||
params: *mut gl::GLint,
|
||||
),
|
||||
gl_get_shader_info_log: unsafe extern "C" fn(
|
||||
shader: gl::GLuint,
|
||||
buf_size: gl::GLsizei,
|
||||
length: *mut gl::GLsizei,
|
||||
info_log: *mut c_char,
|
||||
),
|
||||
gl_create_program: unsafe extern "C" fn() -> gl::GLuint,
|
||||
gl_attach_shader: unsafe extern "C" fn(program: gl::GLuint, shader: gl::GLuint),
|
||||
gl_link_program: unsafe extern "C" fn(program: gl::GLuint),
|
||||
gl_get_programiv: unsafe extern "C" fn(
|
||||
program: gl::GLuint,
|
||||
pname: gl::GLenum,
|
||||
params: *mut gl::GLint,
|
||||
),
|
||||
gl_get_program_info_log: unsafe extern "C" fn(
|
||||
program: gl::GLuint,
|
||||
buf_size: gl::GLsizei,
|
||||
length: *mut gl::GLsizei,
|
||||
info_log: *mut c_char,
|
||||
),
|
||||
gl_use_program: unsafe extern "C" fn(program: gl::GLuint),
|
||||
gl_get_attrib_location:
|
||||
unsafe extern "C" fn(program: gl::GLuint, name: *const c_char) -> gl::GLint,
|
||||
gl_get_uniform_location:
|
||||
unsafe extern "C" fn(program: gl::GLuint, name: *const c_char) -> gl::GLint,
|
||||
gl_uniform_matrix4fv: unsafe extern "C" fn(
|
||||
location: gl::GLint,
|
||||
count: gl::GLsizei,
|
||||
transpose: gl::GLenum,
|
||||
value: *const f32,
|
||||
),
|
||||
gl_uniform1i: unsafe extern "C" fn(location: gl::GLint, v: gl::GLint),
|
||||
gl_uniform4f:
|
||||
unsafe extern "C" fn(location: gl::GLint, x: f32, y: f32, z: f32, w: f32),
|
||||
gl_gen_textures: unsafe extern "C" fn(n: gl::GLsizei, textures: *mut gl::GLuint),
|
||||
gl_bind_texture: unsafe extern "C" fn(target: gl::GLenum, texture: gl::GLuint),
|
||||
gl_tex_parameteri:
|
||||
unsafe extern "C" fn(target: gl::GLenum, pname: gl::GLenum, param: gl::GLint),
|
||||
gl_tex_image_2d: unsafe extern "C" fn(
|
||||
target: gl::GLenum,
|
||||
level: gl::GLint,
|
||||
internalformat: gl::GLenum,
|
||||
width: gl::GLsizei,
|
||||
height: gl::GLsizei,
|
||||
border: gl::GLint,
|
||||
format: gl::GLenum,
|
||||
type_: gl::GLenum,
|
||||
data: *const c_void_raw,
|
||||
),
|
||||
gl_active_texture: unsafe extern "C" fn(texture: gl::GLenum),
|
||||
gl_gen_buffers: unsafe extern "C" fn(n: gl::GLsizei, buffers: *mut gl::GLuint),
|
||||
gl_bind_buffer: unsafe extern "C" fn(target: gl::GLenum, buffer: gl::GLuint),
|
||||
gl_buffer_data: unsafe extern "C" fn(
|
||||
target: gl::GLenum,
|
||||
size: isize,
|
||||
data: *const c_void_raw,
|
||||
usage: gl::GLenum,
|
||||
),
|
||||
gl_enable_vertex_attrib_array: unsafe extern "C" fn(index: gl::GLuint),
|
||||
gl_vertex_attrib_pointer: unsafe extern "C" fn(
|
||||
index: gl::GLuint,
|
||||
size: gl::GLint,
|
||||
type_: gl::GLenum,
|
||||
normalized: gl::GLenum,
|
||||
stride: gl::GLsizei,
|
||||
pointer: *const c_void_raw,
|
||||
),
|
||||
gl_draw_elements: unsafe extern "C" fn(
|
||||
mode: gl::GLenum,
|
||||
count: gl::GLsizei,
|
||||
type_: gl::GLenum,
|
||||
indices: *const c_void_raw,
|
||||
),
|
||||
gl_get_error: unsafe extern "C" fn() -> gl::GLenum,
|
||||
gl_delete_textures: unsafe extern "C" fn(n: gl::GLsizei, textures: *const gl::GLuint),
|
||||
gl_delete_buffers: unsafe extern "C" fn(n: gl::GLsizei, buffers: *const gl::GLuint),
|
||||
|
||||
// X11
|
||||
x_open_display:
|
||||
unsafe extern "C" fn(name: *const c_char) -> *mut x11::Display,
|
||||
x_close_display: unsafe extern "C" fn(disp: *mut x11::Display) -> c_int,
|
||||
x_create_window: unsafe extern "C" fn(
|
||||
disp: *mut x11::Display,
|
||||
parent: x11::Window,
|
||||
x: c_int,
|
||||
y: c_int,
|
||||
width: c_uint,
|
||||
height: c_uint,
|
||||
border_width: c_uint,
|
||||
depth: c_int,
|
||||
class: c_int,
|
||||
visual: *mut c_void,
|
||||
valuemask: c_long,
|
||||
attributes: *mut x11::XSetWindowAttributes,
|
||||
) -> x11::Window,
|
||||
x_map_window: unsafe extern "C" fn(disp: *mut x11::Display, w: x11::Window) -> c_int,
|
||||
x_store_name:
|
||||
unsafe extern "C" fn(disp: *mut x11::Display, w: x11::Window, name: *const c_char) -> c_int,
|
||||
x_flush: unsafe extern "C" fn(disp: *mut x11::Display) -> c_int,
|
||||
}
|
||||
|
||||
impl Gl {
|
||||
/// 加载所有动态库函数
|
||||
pub fn load() -> Result<Self> {
|
||||
let lib_egl = unsafe { libloading::Library::new("libEGL.so.1")? };
|
||||
let lib_gl = unsafe { libloading::Library::new("libGLESv2.so.2")? };
|
||||
let lib_x11 = unsafe { libloading::Library::new("libX11.so.6")? };
|
||||
|
||||
unsafe {
|
||||
Ok(Self {
|
||||
egl_get_display: *lib_egl.get(b"eglGetDisplay\0")?,
|
||||
egl_initialize: *lib_egl.get(b"eglInitialize\0")?,
|
||||
egl_choose_config: *lib_egl.get(b"eglChooseConfig\0")?,
|
||||
egl_create_window_surface: *lib_egl.get(b"eglCreateWindowSurface\0")?,
|
||||
egl_create_context: *lib_egl.get(b"eglCreateContext\0")?,
|
||||
egl_make_current: *lib_egl.get(b"eglMakeCurrent\0")?,
|
||||
egl_swap_buffers: *lib_egl.get(b"eglSwapBuffers\0")?,
|
||||
egl_terminate: *lib_egl.get(b"eglTerminate\0")?,
|
||||
|
||||
gl_clear: *lib_gl.get(b"glClear\0")?,
|
||||
gl_clear_color: *lib_gl.get(b"glClearColor\0")?,
|
||||
gl_enable: *lib_gl.get(b"glEnable\0")?,
|
||||
gl_disable: *lib_gl.get(b"glDisable\0")?,
|
||||
gl_blend_func: *lib_gl.get(b"glBlendFunc\0")?,
|
||||
gl_blend_func_separate: *lib_gl.get(b"glBlendFuncSeparate\0")?,
|
||||
gl_viewport: *lib_gl.get(b"glViewport\0")?,
|
||||
gl_create_shader: *lib_gl.get(b"glCreateShader\0")?,
|
||||
gl_shader_source: *lib_gl.get(b"glShaderSource\0")?,
|
||||
gl_compile_shader: *lib_gl.get(b"glCompileShader\0")?,
|
||||
gl_get_shaderiv: *lib_gl.get(b"glGetShaderiv\0")?,
|
||||
gl_get_shader_info_log: *lib_gl.get(b"glGetShaderInfoLog\0")?,
|
||||
gl_create_program: *lib_gl.get(b"glCreateProgram\0")?,
|
||||
gl_attach_shader: *lib_gl.get(b"glAttachShader\0")?,
|
||||
gl_link_program: *lib_gl.get(b"glLinkProgram\0")?,
|
||||
gl_get_programiv: *lib_gl.get(b"glGetProgramiv\0")?,
|
||||
gl_get_program_info_log: *lib_gl.get(b"glGetProgramInfoLog\0")?,
|
||||
gl_use_program: *lib_gl.get(b"glUseProgram\0")?,
|
||||
gl_get_attrib_location: *lib_gl.get(b"glGetAttribLocation\0")?,
|
||||
gl_get_uniform_location: *lib_gl.get(b"glGetUniformLocation\0")?,
|
||||
gl_uniform_matrix4fv: *lib_gl.get(b"glUniformMatrix4fv\0")?,
|
||||
gl_uniform1i: *lib_gl.get(b"glUniform1i\0")?,
|
||||
gl_uniform4f: *lib_gl.get(b"glUniform4f\0")?,
|
||||
gl_gen_textures: *lib_gl.get(b"glGenTextures\0")?,
|
||||
gl_bind_texture: *lib_gl.get(b"glBindTexture\0")?,
|
||||
gl_tex_parameteri: *lib_gl.get(b"glTexParameteri\0")?,
|
||||
gl_tex_image_2d: *lib_gl.get(b"glTexImage2D\0")?,
|
||||
gl_active_texture: *lib_gl.get(b"glActiveTexture\0")?,
|
||||
gl_gen_buffers: *lib_gl.get(b"glGenBuffers\0")?,
|
||||
gl_bind_buffer: *lib_gl.get(b"glBindBuffer\0")?,
|
||||
gl_buffer_data: *lib_gl.get(b"glBufferData\0")?,
|
||||
gl_enable_vertex_attrib_array: *lib_gl.get(b"glEnableVertexAttribArray\0")?,
|
||||
gl_vertex_attrib_pointer: *lib_gl.get(b"glVertexAttribPointer\0")?,
|
||||
gl_draw_elements: *lib_gl.get(b"glDrawElements\0")?,
|
||||
gl_get_error: *lib_gl.get(b"glGetError\0")?,
|
||||
gl_delete_textures: *lib_gl.get(b"glDeleteTextures\0")?,
|
||||
gl_delete_buffers: *lib_gl.get(b"glDeleteBuffers\0")?,
|
||||
|
||||
x_open_display: *lib_x11.get(b"XOpenDisplay\0")?,
|
||||
x_close_display: *lib_x11.get(b"XCloseDisplay\0")?,
|
||||
x_create_window: *lib_x11.get(b"XCreateWindow\0")?,
|
||||
x_map_window: *lib_x11.get(b"XMapWindow\0")?,
|
||||
x_store_name: *lib_x11.get(b"XStoreName\0")?,
|
||||
x_flush: *lib_x11.get(b"XFlush\0")?,
|
||||
|
||||
_lib_egl: lib_egl,
|
||||
_lib_gl: lib_gl,
|
||||
_lib_x11: lib_x11,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// EGL/GLES2/X11 渲染上下文
|
||||
pub struct RenderContext {
|
||||
pub gl: Gl,
|
||||
display: egl::EGLDisplay,
|
||||
_config: egl::EGLConfig,
|
||||
context: egl::EGLContext,
|
||||
surface: egl::EGLSurface,
|
||||
x_display: *mut x11::Display,
|
||||
x_window: x11::Window,
|
||||
pub program: GLuint,
|
||||
pub attrib_position: GLint,
|
||||
pub attrib_tex_coord: GLint,
|
||||
pub uniform_matrix: GLint,
|
||||
pub uniform_texture: GLint,
|
||||
pub uniform_base_color: GLint,
|
||||
pub uniform_multiply_color: GLint,
|
||||
pub uniform_screen_color: GLint,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
}
|
||||
|
||||
use gl::*;
|
||||
|
||||
/// 创建并链接着色器程序
|
||||
fn compile_shader(gl: &Gl, shader_type: GLenum, src: &str) -> Result<GLuint> {
|
||||
unsafe {
|
||||
let shader = (gl.gl_create_shader)(shader_type);
|
||||
if shader == 0 {
|
||||
return Err(anyhow!("glCreateShader 返回 0"));
|
||||
}
|
||||
let src_c = std::ffi::CString::new(src)?;
|
||||
let src_ptr = src_c.as_ptr();
|
||||
(gl.gl_shader_source)(shader, 1, &src_ptr, std::ptr::null());
|
||||
(gl.gl_compile_shader)(shader);
|
||||
|
||||
let mut status = 0;
|
||||
(gl.gl_get_shaderiv)(shader, GL_COMPILE_STATUS, &mut status);
|
||||
if status == 0 {
|
||||
let mut log = vec![0i8; 1024];
|
||||
(gl.gl_get_shader_info_log)(
|
||||
shader,
|
||||
1024,
|
||||
std::ptr::null_mut(),
|
||||
log.as_mut_ptr(),
|
||||
);
|
||||
let log_str = String::from_utf8_lossy(
|
||||
std::slice::from_raw_parts(log.as_ptr() as *const u8, 1024),
|
||||
);
|
||||
return Err(anyhow!("着色器编译失败: {}", log_str));
|
||||
}
|
||||
Ok(shader)
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderContext {
|
||||
/// 创建全屏 X11 窗口 + EGL 上下文
|
||||
pub fn new_fullscreen(width: i32, height: i32) -> Result<Self> {
|
||||
let gl = Gl::load()?;
|
||||
|
||||
// X11 窗口
|
||||
let x_display = unsafe { (gl.x_open_display)(std::ptr::null()) };
|
||||
if x_display.is_null() {
|
||||
return Err(anyhow!("XOpenDisplay 失败(DISPLAY 环境变量是否设置?)"));
|
||||
}
|
||||
let root_window: x11::Window = 0; // DefaultRootWindow
|
||||
let mut attrs = x11::XSetWindowAttributes {
|
||||
background_pixmap: 0,
|
||||
background_pixel: 0,
|
||||
border_pixmap: 0,
|
||||
border_pixel: 0,
|
||||
bit_gravity: 0,
|
||||
win_gravity: 0,
|
||||
backing_store: 0,
|
||||
backing_planes: 0,
|
||||
backing_pixel: 0,
|
||||
save_under: 0,
|
||||
event_mask: x11::ExposureMask | x11::StructureNotifyMask,
|
||||
do_not_propagate_mask: 0,
|
||||
override_redirect: 1, // 全屏无装饰
|
||||
colormap: 0,
|
||||
cursor: 0,
|
||||
};
|
||||
let x_window = unsafe {
|
||||
(gl.x_create_window)(
|
||||
x_display,
|
||||
root_window,
|
||||
0,
|
||||
0,
|
||||
width as u32,
|
||||
height as u32,
|
||||
0,
|
||||
24, // depth
|
||||
1, // InputOutput
|
||||
std::ptr::null_mut(),
|
||||
x11::CWOverrideRedirect | x11::CWEventMask,
|
||||
&mut attrs,
|
||||
)
|
||||
};
|
||||
if x_window == 0 {
|
||||
return Err(anyhow!("XCreateWindow 失败"));
|
||||
}
|
||||
unsafe {
|
||||
let title = std::ffi::CString::new("ShowenV2 Live2D")?;
|
||||
(gl.x_store_name)(x_display, x_window, title.as_ptr());
|
||||
(gl.x_map_window)(x_display, x_window);
|
||||
(gl.x_flush)(x_display);
|
||||
}
|
||||
|
||||
// EGL
|
||||
let display = unsafe { (gl.egl_get_display)(egl::EGL_DEFAULT_DISPLAY_PTR()) };
|
||||
if display.is_null() {
|
||||
return Err(anyhow!("eglGetDisplay 失败"));
|
||||
}
|
||||
let mut major = 0;
|
||||
let mut minor = 0;
|
||||
let ok = unsafe { (gl.egl_initialize)(display, &mut major, &mut minor) };
|
||||
if ok == 0 {
|
||||
return Err(anyhow!("eglInitialize 失败"));
|
||||
}
|
||||
println!("[Live2D] EGL 版本: {}.{}", major, minor);
|
||||
|
||||
// 选配置
|
||||
let config_attrs = [
|
||||
egl::EGL_RED_SIZE, 8,
|
||||
egl::EGL_GREEN_SIZE, 8,
|
||||
egl::EGL_BLUE_SIZE, 8,
|
||||
egl::EGL_ALPHA_SIZE, 8,
|
||||
egl::EGL_SURFACE_TYPE, egl::EGL_WINDOW_BIT,
|
||||
egl::EGL_RENDERABLE_TYPE, egl::EGL_OPENGL_ES2_BIT,
|
||||
egl::EGL_NONE,
|
||||
];
|
||||
let mut config: egl::EGLConfig = std::ptr::null_mut();
|
||||
let mut num_config = 0;
|
||||
let ok = unsafe {
|
||||
(gl.egl_choose_config)(
|
||||
display,
|
||||
config_attrs.as_ptr(),
|
||||
&mut config,
|
||||
1,
|
||||
&mut num_config,
|
||||
)
|
||||
};
|
||||
if ok == 0 || num_config == 0 {
|
||||
return Err(anyhow!("eglChooseConfig 失败"));
|
||||
}
|
||||
|
||||
// 创建 surface
|
||||
let surface = unsafe {
|
||||
(gl.egl_create_window_surface)(
|
||||
display,
|
||||
config,
|
||||
x_window as egl::NativeWindowType,
|
||||
std::ptr::null(),
|
||||
)
|
||||
};
|
||||
if surface.is_null() {
|
||||
return Err(anyhow!("eglCreateWindowSurface 失败"));
|
||||
}
|
||||
|
||||
// 创建 context(GLES2)
|
||||
let ctx_attrs = [egl::EGL_CONTEXT_CLIENT_VERSION, 2, egl::EGL_NONE];
|
||||
let context = unsafe {
|
||||
(gl.egl_create_context)(display, config, std::ptr::null(), ctx_attrs.as_ptr())
|
||||
};
|
||||
if context.is_null() {
|
||||
return Err(anyhow!("eglCreateContext 失败"));
|
||||
}
|
||||
|
||||
// make current
|
||||
let ok = unsafe {
|
||||
(gl.egl_make_current)(display, surface, surface, context)
|
||||
};
|
||||
if ok == 0 {
|
||||
return Err(anyhow!("eglMakeCurrent 失败"));
|
||||
}
|
||||
|
||||
// 设置视口
|
||||
unsafe {
|
||||
(gl.gl_viewport)(0, 0, width, height);
|
||||
(gl.gl_clear_color)(0.0, 0.0, 0.0, 0.0);
|
||||
(gl.gl_enable)(GL_BLEND);
|
||||
(gl.gl_blend_func)(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
// 编译着色器
|
||||
let vert = compile_shader(&gl, GL_VERTEX_SHADER, VERT_SHADER_SRC)?;
|
||||
let frag = compile_shader(&gl, GL_FRAGMENT_SHADER, FRAG_SHADER_SRC)?;
|
||||
let program = unsafe {
|
||||
let p = (gl.gl_create_program)();
|
||||
(gl.gl_attach_shader)(p, vert);
|
||||
(gl.gl_attach_shader)(p, frag);
|
||||
(gl.gl_link_program)(p);
|
||||
let mut status = 0;
|
||||
(gl.gl_get_programiv)(p, GL_LINK_STATUS, &mut status);
|
||||
if status == 0 {
|
||||
let mut log = vec![0i8; 1024];
|
||||
(gl.gl_get_program_info_log)(
|
||||
p,
|
||||
1024,
|
||||
std::ptr::null_mut(),
|
||||
log.as_mut_ptr(),
|
||||
);
|
||||
let log_str = String::from_utf8_lossy(
|
||||
std::slice::from_raw_parts(log.as_ptr() as *const u8, 1024),
|
||||
);
|
||||
return Err(anyhow!("着色器链接失败: {}", log_str));
|
||||
}
|
||||
p
|
||||
};
|
||||
|
||||
unsafe { (gl.gl_use_program)(program); }
|
||||
|
||||
// 获取 attribute/uniform 位置
|
||||
let attrib_position = unsafe {
|
||||
let name = std::ffi::CString::new("a_position")?;
|
||||
(gl.gl_get_attrib_location)(program, name.as_ptr())
|
||||
};
|
||||
let attrib_tex_coord = unsafe {
|
||||
let name = std::ffi::CString::new("a_texCoord")?;
|
||||
(gl.gl_get_attrib_location)(program, name.as_ptr())
|
||||
};
|
||||
let uniform_matrix = unsafe {
|
||||
let name = std::ffi::CString::new("u_matrix")?;
|
||||
(gl.gl_get_uniform_location)(program, name.as_ptr())
|
||||
};
|
||||
let uniform_texture = unsafe {
|
||||
let name = std::ffi::CString::new("s_texture0")?;
|
||||
(gl.gl_get_uniform_location)(program, name.as_ptr())
|
||||
};
|
||||
let uniform_base_color = unsafe {
|
||||
let name = std::ffi::CString::new("u_baseColor")?;
|
||||
(gl.gl_get_uniform_location)(program, name.as_ptr())
|
||||
};
|
||||
let uniform_multiply_color = unsafe {
|
||||
let name = std::ffi::CString::new("u_multiplyColor")?;
|
||||
(gl.gl_get_uniform_location)(program, name.as_ptr())
|
||||
};
|
||||
let uniform_screen_color = unsafe {
|
||||
let name = std::ffi::CString::new("u_screenColor")?;
|
||||
(gl.gl_get_uniform_location)(program, name.as_ptr())
|
||||
};
|
||||
|
||||
println!(
|
||||
"[Live2D] GL 程序就绪: program={} pos={} tex={} mat={} tex_u={} base={} mult={} scr={}",
|
||||
program, attrib_position, attrib_tex_coord, uniform_matrix,
|
||||
uniform_texture, uniform_base_color, uniform_multiply_color, uniform_screen_color
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
gl,
|
||||
display,
|
||||
_config: config,
|
||||
context,
|
||||
surface,
|
||||
x_display,
|
||||
x_window,
|
||||
program,
|
||||
attrib_position,
|
||||
attrib_tex_coord,
|
||||
uniform_matrix,
|
||||
uniform_texture,
|
||||
uniform_base_color,
|
||||
uniform_multiply_color,
|
||||
uniform_screen_color,
|
||||
width,
|
||||
height,
|
||||
})
|
||||
}
|
||||
|
||||
/// 上传纹理到 GPU
|
||||
pub fn create_texture(&self, img: &super::assets::TextureImage) -> GLuint {
|
||||
unsafe {
|
||||
let mut tex = 0;
|
||||
(self.gl.gl_gen_textures)(1, &mut tex);
|
||||
(self.gl.gl_bind_texture)(GL_TEXTURE_2D, tex);
|
||||
(self.gl.gl_tex_parameteri)(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR as i32);
|
||||
(self.gl.gl_tex_parameteri)(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR as i32);
|
||||
(self.gl.gl_tex_parameteri)(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE as i32);
|
||||
(self.gl.gl_tex_parameteri)(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE as i32);
|
||||
(self.gl.gl_tex_image_2d)(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RGBA as i32,
|
||||
img.width as i32,
|
||||
img.height as i32,
|
||||
0,
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
img.rgba.as_ptr() as *const c_void,
|
||||
);
|
||||
let err = (self.gl.gl_get_error)();
|
||||
if err != GL_NO_ERROR {
|
||||
eprintln!("[Live2D] glTexImage2D 错误: 0x{:x}", err);
|
||||
}
|
||||
tex
|
||||
}
|
||||
}
|
||||
|
||||
/// 交换缓冲区(显示到屏幕)
|
||||
pub fn swap_buffers(&self) {
|
||||
unsafe {
|
||||
(self.gl.gl_clear)(GL_COLOR_BUFFER_BIT);
|
||||
// 注意:clear 在绘制前调用,这里 swap 只是提交
|
||||
// 实际渲染流程:clear → draw → swap
|
||||
(self.gl.egl_swap_buffers)(self.display, self.surface);
|
||||
}
|
||||
}
|
||||
|
||||
/// 清屏
|
||||
pub fn clear(&self) {
|
||||
unsafe { (self.gl.gl_clear)(GL_COLOR_BUFFER_BIT); }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RenderContext {
|
||||
fn drop(&mut self) {
|
||||
// 简单清理(EGL/X11 资源在进程退出时自动释放)
|
||||
unsafe {
|
||||
(self.gl.x_flush)(self.x_display);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EGL_DEFAULT_DISPLAY 常量
|
||||
impl egl {
|
||||
pub fn EGL_DEFAULT_DISPLAY_PTR() -> NativeDisplayType {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user