ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Slint OpenGL Underlay 示例深度解析:用 `set_rendering_notifier` 在 Slint 场景下层叠加 OpenGL 特效

Slint OpenGL Underlay 示例深度解析:用 `set_rendering_notifier` 在 Slint 场景下层叠加 OpenGL 特效 Slint OpenGL Underlay 示例深度解析用set_rendering_notifier在 Slint 场景下层叠加 OpenGL 特效【免费下载链接】slintSlint is an open-source declarative GUI toolkit to build native user interfaces for Rust, C, JavaScript, or Python apps.项目地址: https://gitcode.com/GitHub_Trending/sl/slint导读本文基于 Slint 仓库中的examples/opengl_underlay示例系统讲解如何在 Slint 窗口中实现双层场景渲染底层用原生 OpenGL 代码绘制持续动画的图形特效underlay上层叠加普通 Slint 元素构成的 UI。其核心技术是slint::Window::set_rendering_notifier渲染阶段回调机制本文将从 API 语义、Rust/C 双语言完整实现、渲染状态机与 GL 上下文状态管理四个层面展开并给出可复制、可运行的构建与运行方案帮助读者掌握自定义渲染内容 Slint UI混合架构的标准姿势。一、示例概述两层场景叠加架构examples/opengl_underlay/README.md开篇即点明该示例的核心目标在同一个窗口中叠加渲染两层场景底层underlay使用底层 OpenGL 代码渲染的图形特效一个不断旋转、波动的菱形网格着色器动画上层由 Slint 元素构成的 UI 场景标题文字、说明、复选框浮在特效之上。整个机制不依赖任何自定义渲染器或外部合成层而是通过slint::Window上的set_rendering_notifier函数实现该函数接收一个回调回调会在渲染流程的不同阶段被调用。示例中的用法是在setup 阶段RenderingSetup准备好后续 OpenGL 渲染所需的管线着色器程序、缓冲对象、顶点数组等在BeforeRendering阶段用 OpenGL 绘制特效随后Slint 将元素场景渲染到同一块 back-buffer即先前 OpenGL 代码绘制的内容之上。由于特效是连续动画回调中还会调用slint::Window::request_redraw()主动请求下一帧重绘。该示例同时提供了 Rustmain.rs、Cmain.cpp与 WebAssemblyindex.html三套前端场景文件为scene.slint构建配置见Cargo.toml与CMakeLists.txt。二、核心 APIset_rendering_notifier与渲染状态机2.1 API 签名与语义Rust 侧slint::Window::set_rendering_notifier接收一个闭包FnMut(RenderingState, GraphicsAPI)返回Result(), SetRenderingNotifierError见 api/rs/slint/lib.rs 中关于 WGPU 集成的文档示例。C 侧slint::Window::set_rendering_notifier是一个模板函数接受任意std::invocableRenderingState, GraphicsAPI回调成功时返回空的std::optional失败时返回错误码见 slint_window.h 与同文件 #L408-L422 的文档。2.2RenderingState四个阶段渲染状态枚举定义在 internal/core/api.rs阶段触发时机典型用途RenderingSetup窗口已创建、图形适配器/上下文初始化完成创建 GL 程序、缓冲对象、查询 uniform/attrib 位置BeforeRendering场景即将渲染back-buffer 仍为上一帧或空白用 OpenGL 绘制 underlay 内容AfterRendering场景已渲染但 back-buffer 尚未送显如尚未 swap buffers绘制 overlay、屏幕后处理RenderingTeardown窗口即将销毁、图形资源需释放删除 GL 程序、VAO、VBOSetRenderingNotifierError枚举同文件 #L374-L379只有两个变体Unsupported当前渲染后端不支持渲染通知器AlreadySet已经注册过一个通知器不支持多个并存。该枚举为#[non_exhaustive]意味着未来版本可能新增错误类型外部代码匹配时应保留通配分支。2.3 上下文状态约束关键安全提示在 internal/core/api.rs 的文档注释中明确强调使用 OpenGL 渲染时回调执行期间GL 上下文是 current 的可以安全调用 OpenGL 函数但必须完整保存并恢复上下文状态例如TEXTURE_BINDING_2D、ARRAY_BUFFER_BINDING等绑定否则会破坏 Slint 自身的渲染状态。示例源码正是这一约束的忠实实践详见第四节。三、场景定义scene.slintscene.slint定义了叠加在特效之上的 UI 层import { CheckBox, VerticalBox, ... } from std-widgets.slint; export component App inherits Window { in-out property bool rotation-enabled apply-rotation.checked; preferred-width: 1280px; preferred-height: 720px; title: Slint OpenGL Underlay Example; icon: image-url(../../logo/slint-logo-small-light.png); Rectangle { x: 0; width: 33%; background: #1B1B1Fcc; VerticalBox { padding-top: 55px; y: 0; padding: 37px; alignment: start; spacing: 15px; // 标题、说明文字…… apply-rotation : CheckBox { checked: true; text: Enable shader rotation; } } } }要点窗口固定 1280×720左侧 33% 宽度为一个半透明深色面板#1B1B1Fcccc为 80% 不透明度叠加在底层 GL 动画之上面板内含 Slint Logo、标题、说明文字与一个复选框关键交互rotation-enabled通过双向绑定到复选框apply-rotation.checked。C/Rust 侧通过get_rotation_enabled()读取该属性从而控制底层着色器动画是否旋转。由此可以看到Slint UI 属性 ↔ 原生 GL 代码的通信通道UI 状态变化在BeforeRendering回调中被读取并作用于 GL uniform。四、Rust 实现逐段拆解4.1 后端选择强制 OpenGL ESslint::BackendSelector::new() .require_opengl_es() .select() .expect(Unable to create Slint backend with OpenGL ES renderer);在创建应用前示例强制选择带 OpenGL ES 渲染器的后端。这是 underlay 方案的前提——只有 GL 后端才会触发set_rendering_notifier回调并提供GraphicsAPI::NativeOpenGL原生或GraphicsAPI::WebGLWASM的上下文信息。4.2 注册渲染通知器app.window() .set_rendering_notifier(move |state, graphics_api| { match state { slint::RenderingState::RenderingSetup { let context match graphics_api { slint::GraphicsAPI::NativeOpenGL { get_proc_address } unsafe { glow::Context::from_loader_function_cstr(|s| get_proc_address(s)) }, slint::GraphicsAPI::WebGL { canvas_element_id, context_type } { /* WebGL2 上下文 */ } _ return, }; underlay Some(EGLUnderlay::new(context)) } slint::RenderingState::BeforeRendering { if let (Some(underlay), Some(app)) (underlay.as_mut(), app_weak.upgrade()) { underlay.render(app.get_rotation_enabled()); app.window().request_redraw(); // 连续动画的关键 } } slint::RenderingState::AfterRendering {} slint::RenderingState::RenderingTeardown { drop(underlay.take()); // 释放 GL 资源 } _ {} } }) .expect(Unable to set rendering notifier);结构设计要点underlay: OptionEGLUnderlay通过Option管理生命周期RenderingSetup时创建RenderingTeardown时take()取出并 dropapp_weak弱引用用于在回调中避免强引用循环BeforeRendering末尾调用request_redraw()请求下一帧驱动 GL 侧动画持续运行示例使用glowcrate 包装 GL 调用通过get_proc_address函数指针获取原生 GL 入口或通过 WebGL2 上下文构造WASM 分支。4.3 GL 资源管理着色器程序与顶点数据EGLUnderlay::new中完成编译链接顶点着色器#version 100的 ES 2.0 语法纯 2D 全屏三角形带与片元着色器菱形网格 波动高光动画并在编译/链接失败时panic!输出日志查询effect_time、rotation_time两个 uniform 位置和positionattribute 位置创建 VBO 存储 4 个顶点{-1,1, -1,-1, 1,1, 1,-1}覆盖整个视口的TRIANGLE_STRIP创建并配置 VAO绑定 attribute 指针。Drop实现中删除 program、VAO、VBO保证 teardown 时资源被释放。4.4render()渲染与状态保存/恢复fn render(mut self, rotation_enabled: bool) { unsafe { // 保存旧的 buffer / vao 绑定WASM 下暂不支持查询置为 None let old_buffer ...; // ARRAY_BUFFER_BINDING let old_vao ...; // VERTEX_ARRAY_BINDING gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vbo)); gl.bind_vertex_array(Some(self.vao)); let elapsed self.start_time.elapsed().as_millis() as f32; gl.uniform_1_f32(Some(self.effect_time_location), elapsed); // 处理 UI 复选框驱动的旋转开关与暂停偏移 if rotation_enabled { if !self.last_rotation_enabled { self.rotation_pause_offset elapsed - self.rotation_time; } self.rotation_time elapsed - self.rotation_pause_offset; } gl.uniform_1_f32(Some(self.rotation_time_location), self.rotation_time); self.last_rotation_enabled rotation_enabled; gl.draw_arrays(glow::TRIANGLE_STRIP, 0, 4); // 恢复上下文状态保证 Slint 渲染不被破坏 gl.bind_buffer(glow::ARRAY_BUFFER, old_buffer); gl.bind_vertex_array(old_vao); gl.use_program(None); } }这是对 2.3 节保存/恢复上下文状态约束的直接落实绘制完毕后恢复ARRAY_BUFFER_BINDING、VERTEX_ARRAY_BINDING并解绑 program。源码注释还说明WASM 下glow暂时无法查询当前绑定依赖 glow#190因此old_buffer/old_vao在 wasm32 目标下取None——目前仅影响 femtovg 后端尚无实际问题。rotation_pause_offset机制值得留意当复选框取消勾选时记录暂停时刻的偏移量重新勾选后从暂停处继续旋转实现冻结—续播的无缝衔接避免时间跳变。4.5 着色器特效双语言共享同一 GLSL 代码顶点着色器仅将 2D 位置透传为gl_Position片元着色器实现完整动画带透视强度的坐标变换perspective_strength 0.09基于rotation_time / 1700.0的正弦摆动幅度 ±10°MAX_ANGLE_DEGREES生成旋转矩阵将坐标放大 6 倍形成网格用floor/fract计算格内相对坐标曼哈顿距离生成菱形diamond填充与描边发光遮罩effect_time / 300.0驱动高光色COLOR_ACCENT沿网格正弦波动。Rust 版与 C 版main.cpp 中#version 100字符串的 GLSL 完全一致保证了两个语言示例视觉效果等价。五、C 实现同一架构的不同写法C 侧核心是一个重载了operator()的类签名与 Rust 闭包一一对应class OpenGLUnderlay { public: void operator()(slint::RenderingState state, slint::GraphicsAPI) { switch (state) { case slint::RenderingState::RenderingSetup: setup(); break; case slint::RenderingState::BeforeRendering: if (auto app app_weak.lock()) { render((*app)-get_rotation_enabled()); (*app)-window().request_redraw(); } break; case slint::RenderingState::AfterRendering: break; case slint::RenderingState::RenderingTeardown: teardown(); break; } } // setup()编译着色器、链接 program、查询位置 // render()设置顶点、更新 uniform、glDrawArrays // teardown()glDeleteProgram };主函数中的注册与错误处理展示了SetRenderingNotifierError的实战用法auto app App::create(); if (auto error app-window().set_rendering_notifier(OpenGLUnderlay(app))) { if (*error slint::SetRenderingNotifierError::Unsupported) { fprintf(stderr, This example requires the use of a GL renderer. Please run with the environment variable SLINT_BACKENDwinit-femtovg set.\n); } else { fprintf(stderr, Unknown error calling set_rendering_notifier\n); } exit(EXIT_FAILURE); } app-run();注意当后端不支持渲染通知器时示例给出的兜底方案是设置环境变量SLINT_BACKENDwinit-femtovg运行——femtovg 渲染器支持该回调机制。C 版直接使用原生 GLES2 头文件GLES2/gl2.h而非 glow并在compile_shader辅助函数中打印编译日志后exit(1)。C 版同样实现了rotation_pause_offset暂停续播逻辑行为与 Rust 版完全一致。六、构建与运行6.1 Rust / Cargocargo run --release -p opengl_underlay依赖见 Cargo.tomlslintdefault-features falsecompat-1-18、std、glowworkspace 版本、web-timebuild.rs由slint-build负责把scene.slint编译为include_modules!()引用的 Rust 代码。6.2 C / CMakeCMakeLists.txt 的核心cmake_minimum_required(VERSION 3.21) project(opengl_cpp_underlay LANGUAGES CXX) if (NOT TARGET Slint::Slint) find_package(Slint REQUIRED) endif() add_executable(opengl_underlay main.cpp) target_link_libraries(opengl_underlay PRIVATE Slint::Slint OpenGLES2::OpenGLES2) slint_target_sources(opengl_underlay scene.slint)需注意链接的是OpenGLES2::OpenGLES2OpenGL ES 2.0与着色器中的#version 100一致。若运行时报Unsupported错误按第五节提示设置SLINT_BACKENDwinit-femtovg再运行。6.3 WebAssemblyindex.html 说明了 WASM 构建流程取消Cargo.toml中以#wasm#注释的 lib/cdylib 及 wasm-bindgen、web-sys 依赖行CI 脚本用sed s/#wasm# //生成 wasm 构建在本目录执行wasm-pack build --release --target web由index.html加载./pkg/opengl_underlay.js启动页面通过data-slint-auto-resize-to-preferredtrue的 canvas 承载渲染。WASM 分支在RenderingSetup阶段从GraphicsAPI::WebGL { canvas_element_id, context_type }中取回 canvas通过web_sys转换为 WebGL2 上下文再构造glow::Context。七、可复用的通用模式总结从该示例提炼出的GL underlay 混合渲染标准模式适用于任何需要在 Slint 场景下层或上层插入原生图形渲染的场景选对后端BackendSelector::require_opengl_es()或 C 侧接受Unsupported错误并提示设置SLINT_BACKEND状态机驱动生命周期RenderingSetup建管线 →BeforeRendering绘制 underlay →AfterRendering可做 overlay →RenderingTeardown释放资源同一 back-buffer 合成underlay 绘制后 Slint 场景直接渲染在其上无需额外合成步骤状态保存与恢复每次回调结束前恢复 GL 绑定buffer/VAO/program 等这是文档明确强调的正确性要求动画驱动在BeforeRendering末尾调用request_redraw()持续请求重绘UI ↔ GL 通信通过 Slint 属性如rotation-enabled在回调中读取将 UI 交互映射为 uniform 参数。对于进一步研究可对照阅读渲染状态枚举与错误枚举的定义internal/core/api.rs、C API 封装slint_window.h以及 WGPU 集成文档中关于set_rendering_notifier在GraphicsAPI::WGPU30下获取 device/queue 的用法api/rs/slint/lib.rs——同样的回调机制也可以服务于 WGPU 纹理集成等更现代的渲染管线。【免费下载链接】slintSlint is an open-source declarative GUI toolkit to build native user interfaces for Rust, C, JavaScript, or Python apps.项目地址: https://gitcode.com/GitHub_Trending/sl/slint创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表