EUI-NEO

渲染流程

EUI-NEO 技术文档。

渲染流程

本文说明当前 main.cppapp/dsl_app.hcore::dsl::Runtime 的真实行为:主动帧率节流、按需渲染、脏区缓存、blur 的特殊处理。

文件职责

主循环

当前主循环不是无脑满帧刷新。

静止时:

glfwWaitEvents()

有动画时:

按窗口所在显示器 refreshRate 计算下一帧时间
等待到下一帧时间点
update
如果有 dirty/full redraw:
    render
    glfwSwapBuffers
glfwPollEvents

当前使用主动节流:

gallerycalculator 当前默认 fps = 90.0gallery 的 Settings 中打开 Unlock 90 FPS limit 后会把 fps 同步为 0.0,动画节流回到屏幕刷新率。

App 生命周期

底层入口在 app/app.h

namespace app {

const char* windowTitle();
bool showFrameCountInTitle();
double frameRateLimit();
int initialWindowWidth();
int initialWindowHeight();
bool initialize(GLFWwindow* window);
bool update(GLFWwindow* window,
            float deltaSeconds,
            int windowWidth,
            int windowHeight,
            float dpiScale,
            float pointerScale);
bool isAnimating();
void render(int windowWidth, int windowHeight, float dpiScale);
void shutdown();

}

DSL app 一般不直接实现这些函数,而是包含 app/dsl_app.h,然后实现:

const DslAppConfig& dslAppConfig();
void compose(core::dsl::Ui& ui, const core::dsl::Screen& screen);

DSL App 更新流程

app/dsl_app.h 当前不会每轮循环都重新 compose。

它只在这些情况 compose:

流程大致是:

如果首次或逻辑尺寸变化:
    composeFrame()

changed = runtime.update(...)

如果 runtime.needsCompose():
    composeFrame()
    runtime.update(..., deltaSeconds = 0)
    runtime.markFullRedraw()
    changed = true

return changed

点击后多做一次 deltaSeconds = 0 的 Runtime update,是为了让新 DSL 树和 Runtime 缓存实例马上同步。否则可能出现“新声明 + 旧实例坐标”混在同一帧里,导致文本或控件短暂错位。

脏区渲染

当前 Runtime 使用一个离屏 framebuffer cache 做保守脏区渲染。

核心策略:

1. Runtime 按 id 缓存每个 Rect / Text 的动画值和 primitive。 2. 元素视觉值变化时,计算变化前后的 visual rect。 3. 对旧 rect 和新 rect 取 union,加入 dirty rect 列表。 4. render 时先渲染到离屏 cache。 5. full redraw 时重画整棵 UI。 6. 局部变化时:

当前 dirty rect 已考虑:

Blur 与脏区

blur 是特殊图元,不等同于普通半透明 rect。

当前 blur 的实现方式是 backdrop blur:

1. 从当前 framebuffer/cache 中取 blur rect 附近的 backdrop。 2. 用 framebuffer blit 复制到 1/2 尺寸的 backdrop texture。 3. fragment shader 多次采样这块降采样 texture。 4. 把采样结果和自身颜色混合。

这意味着 blur 依赖“它背后的已绘制内容”。如果只做局部 dirty patch,某些情况下 cache 里可能是半旧半新的:

这时 blur 会把“半旧半新的 cache”采进去,就可能出现竖条、污染、错位的模糊背景。

为保证正确性,Runtime 当前做了保守处理:

保护范围目前按 blur rect 中心放大 1.2 倍计算。它不是完整 blur 半径的精确范围,而是性能和正确性之间的折中:减少 blur 周围普通交互触发 full redraw 的次数,同时避免最明显的半旧 cache 采样污染。

代价是:blur 附近交互会比普通 rect 更贵。gallery 默认关闭 Glass/blur,也是为了默认功耗更稳。

Full Redraw 触发

这些情况会触发或升级为 full redraw:

当前限制