首页 > 其他分享 >立即模式下的 Egui

立即模式下的 Egui

时间:2023-11-05 17:33:05浏览次数:35  
标签:Widget 场景 渲染 Egui 模式 立即 应用程序 egui

title: 
author: 阿东
keywords:
- Rust
- Rust Programming Language
- egui
- eframe
- Technique
description: Rust 优秀 GUI 库 egui 采用的立即模式到底有什么特点? 为什么选择了它?
author_email: [email protected]
created_at: "2023-03-31"
updated_at: "2023-03-31"
tags:
- Rust
- egui
permanent_link: egui-immediate-mode
renderer_params: 
- enable-toc

立即模式 (Immediate Mode) 和保留模式 (Retained Mode) 是两种不同逻辑的图形 API 实现方法. Windows 中 Direct2D 使用的是立即模式, 而 WPF 是用的是保留模式. Egui 本身使用的也是立即模式.

图形 API 的实现方式

立即模式

立即模式被称为过程性的 (Procedural)[1], 也就是说图形绘制命令的直接发出者是应用程序. 在立即模式下, 每一帧渲染的过程中:

  1. 应用程序 根据自身程序状态信息生成直接绘制指令
  2. 图形 API 收到指令后进行处理并将绘制指令渲染在屏幕上

图片出处 Wikipedia[2]

保留模式

保留模式被称为声明式的 (Declarative)[1:1], 意指应用程序只负责根据图元和逻辑声明一些场景和模型. 而图形库则负责保存和渲染这个场景的内容. 在保留模式下, 每一帧渲染的过程中:

  1. 应用程序 根据自身程序状态信息描述对象状态和场景内容
  2. 图形 API 根据场景内容和对象属性生成绘制指令并渲染在屏幕上

图片出处 Wikipedia[2:1]

比较

在保留模式下, 整个渲染场景中的所有对象全都由图形库进行管理. 应用程序可以改变每个对象的属性 (比如位置, 角度, 纹理等), 也可以向场景中增加或从其中删除某些对象. 但是保留模式的应用程序却无法决定什么要渲染, 什么不渲染, 这些工作是由图形库来执行的.

立即模式下则完全相反, 渲染场景是由应用程序进行管理维护的. 应用传输给图形库的就是每一帧需要渲染的内容. 因此立即模式的应用程序不仅仅要管理对象的属性, 或者增减对象, 还要根据场景的状态 (比如用户交互事件) 来决定是否要修改渲染对象的状态, 最终生成对应的绘制指令发送给图形库.

这样看来, 立即模式的图形 API 具备了极高的灵活度, 但是也相较保留模式增加了更多的复杂度. 另一方面, 立即模式由于跳过了由图形库管理场景的过程, 所以内存占用往往较小, 渲染速度也会更快.

Egui 的立即模式

Egui 完全基于立即模式进行设计[3], 所以开发者必须要在 update 函数之外存储应用程序的输入状态, 存储应用程序的交互结果, 以及定义各种标志位.

设计考量[4]

设计者在考虑使用立即模式实现 egui 时, 主要看中以下的优点:

  1. 从编码角度来看:
    1. 不需要大量的 on_click 一类的回调函数. 因为点击事件是在当前帧渲染的同时执行的, 而不是执行完毕后再修改对象状态, 等待后续帧渲染.
    2. 界面渲染函数可以非常简单, 就是一个函数, 直观而明确.
  2. 从性能和安全的角度来看:
    1. 不会出现回调函数中引用了某个资源 (如某个闭包中的变量), 但实际执行时此变量已经被修改或销毁的情况.
    2. 渲染的内容就是应用程序实时状态的准确反应, 不会出现渲染的内容已经过时消失的情况[5].
    3. 几乎所有类型的引用错误和所有权错误都可以在编译过程中发生, 杜绝运行时难以排查的内存溢出问题.

但是缺点也是显而易见的. 首先是编码难度随着灵活性显著提升, 这对于完全没有接触过立即模式的开发者来说简直是重温初学 Rust 时的痛苦. 其次是布局方面的天然缺陷, 因为立即模式的顺序逻辑不适合进行内外相互关联的布局, 比如 窗口布局网格布局[6]. 再者是 CPU 开销, 使用立即模式需要对每一帧 update 方法的执行时间进行严格的控制. 因此对于较多元素的渲染场景应该充分考虑部分渲染的方式进行优化.

实现方式

在较为常见的使用控件 (Widget) 的场景, egui 的每一个渲染帧 (每秒钟约 60 帧) 会执行这样一系列操作:

  1. Frame 结构体的 update 方法中, 对遇到的每一个 Widget trait 实例, 调用其 ui 方法, 计算出对应的布局尺寸和绘制位置.
  2. 检查是否存在用户交互事件与当前的 Widget 实例相关联, 如果有, 作相应标记.
  3. 判断完成后将对应的形状和文字 (如果有) 放入帧渲染列表中 (等待 update 方法调用结束后进行绘制).
  4. 返回 Response 对象, 供后续应用程序逻辑调用判断使用.
  5. 渲染形状和文字.

其中有一些关键的 trait 需要着重说明.

egui::Widget

所有需要对屏幕内容进行绘制的结构体都需要实现这个 trait. 同时, 签名如同 |ui: &mut egui::UI | -> egui::Response {} 的闭包也都实现了 Widget trait, 故而它们都可以用于 ui.add() 方法中.

当我们自行实现 Widget 时, 需要实现 Widget::ui 方法, 用于分配空间, 判断用户交互, 绘制并最终返回一个 Response. 值得一提的是, ui 方法会消耗当前类型的对象, 因此调用之后这个对象就失效了. 这时典型的 builder 设计模型[7]的例子, 我们所有的 Widget 都因为这一特殊的设计而不能对状态进行管理.

egui::Response

所有对 egui::Ui 添加 Widget 的操作都需要返回 Response 结构体的实例. 它保存了渲染上下文信息 (ctx), 渲染的图层信息 (layer_id), 渲染区域信息 (rect), 场景信息 (sense) 等. 并且这个实例还保存了刚才的 Widget 是否与用户产生了交互的信息, 可以通过其定义的各种方法 (如 clicked(), hovered()) 进行判断, 进而执行相应的逻辑.



  1. Retained Mode Versus Immediate Mode, Microsoft Learn ↩︎ ↩︎

  2. Retained Mode, Wikipedia ↩︎ ↩︎

  3. Understanding immediate mode, docs.rs ↩︎

  4. Why immediate mode, github.com ↩︎

  5. 这是由于保留模式下, 渲染场景负责管理和维护渲染对象. 但是当程序不再需要某个对象后, 该对象可能处于某些原因 (比如开发人员遗忘) 而继续存在于渲染场景内存中. 这种现象不仅会导致渲染错误, 还可能造成大量的内存占用和性能损失. ↩︎

  6. 这类布局的显著特征是: 内部元素的尺寸和位置依赖外部元素的尺寸和位置, 但是外部元素的尺寸和位置 (如内外都居中) 却又同时依赖内部元素的尺寸信息. 某些情况下可以通过前后帧调整来补足, 但这可能导致屏幕内容瞬闪的异常体验; 另一种途径是反复调用多次布局函数来计算合理的布局位置及尺寸, 但这样的缺点就是极高的时延和卡顿. ↩︎

  7. The builder pattern, docs.rust-lang.org ↩︎

标签:Widget,场景,渲染,Egui,模式,立即,应用程序,egui
From: https://www.cnblogs.com/zhongdongy/p/egui-immediate-mode.html

相关文章

  • redis的几种部署模式
     一,redis有哪几种部署模式Redis有几种常见的部署模式,包括单机模式、主从模式、哨兵模式和集群模式。单机模式:这是最简单的部署方式,仅需要在单个机器上启动Redis实例。这种模式适用于数据量较小、业务压力较小的场景。其优点是操作简单、成本低,适用于小型业务和开发测试环境。......
  • 设计模式-单例械
    //Seehttps://aka.ms/new-console-templateformoreinformation//设计模式-单例模式//目的:唯一性,内存资源,GCtffu//保证整个系统中一个类只有一个对象的实例usingSystem.Threading.Channels;Singleton.GetInstance().GetGuid();Singleton.GetInstance().GetGuid()......
  • 设计模式-策略模式
    策略模式:定义一系列的算法,将每个算法分别封装起来,让它们可以互相替换。策略模式用于算法的自由切换和扩展,它是使用较为广泛的设计模式之一。策略模式对应于解决某一问题的一个算法族,允许用户从该算法中任选一个算法解决某一问题,同时可以方便地更换算法或者增加新算法。策略模式......
  • 介绍LiteCVR安防视频平台地图视图模式的开发与设计
    随着AI技术的应用,视频监控系统也越来越智能。基于深度学习的智能视频安防系统可实现人脸精准识别与特征提取,支持对海量人脸数据的高效检索,动态布控,深度分析等,系统提供人像实时采集、人脸去重、实时动态布控、以脸搜脸、特征检索、人证核验、同行人分析、人员轨迹分析、异常人员徘徊......
  • 设计模式—结构型模式之适配器模式
    设计模式—结构型模式之适配器模式将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,适配器模式分为类结构型模式(继承)和对象结构型模式(组合)两种,前者(继承)类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少......
  • 企业集成模式-第二章
    二、集成模式2.1引言企业集成是指让不同的应用协同工作,提供一组统一的功能。这些应用可以是内部定制开发的,也可以从第三方开发商购买。它们可能运行在多台计算机上,分别有不同的平台,甚至在地理位置上也是分散的。有些应用可能由企业外的商业合作伙伴或客户运行。还有些应用在设......
  • 企业集成模式-第三章
    三、消息传递系统3.1引言1)消息传递的基本概念通道:是一个虚拟管道消息:能通过通道传送的一个原子数据包管道和过滤器:在最简单的情况下,消息传递系统会把消息直接从发送者的计算机传送给接收者的计算机。但是,在消息从最初的发送者那里发出,直到最后的接收者接收到以前,往往需要对......
  • 设计模式
    一、工厂模式1、简单工厂模式通过一个具体的工厂类,根据传入不同的参数,生成实际对象2、工厂方法模式在工厂方法模式中,不再由单一的工厂类生产产品,而是由工厂类的子类实现具体的产品创建。当增加一个产品时,只需增加一个相应的工厂类的子类,实现生产这种产品,从而解决简单工厂生产......
  • 软件设计实验7:单例模式
    实验7:单例模式本次实验属于模仿型实验,通过本次实验学生将掌握以下内容:1、理解单例模式的动机,掌握该模式的结构;2、能够利用单列模式解决实际问题。 [实验任务一]:学号的单一仿照课堂的身份证的例子,实现每个同学仅有一个学号这一问题。实验要求:1. 画出对应的类图;  2......
  • 软件设计实验6:原型模式
     实验6:原型模式本次实验属于模仿型实验,通过本次实验学生将掌握以下内容:1、理解原型模式的动机,掌握该模式的结构;2、能够利用原型模式解决实际问题。 [实验任务一]:向量的原型用C++完成数学中向量的封装,其中,用指针和动态申请支持向量长度的改变,使用浅克隆和深克隆复制向量......