LAYOUT / STYLE / CACHE / RENDER / TREE 五种脏标记驱动增量更新,避免每帧全量重算。
XUI 使用脏标记(Dirty Flag)实现增量更新:
• LAYOUT (0x01):尺寸/位置变化,需重新布局
• STYLE (0x02):样式属性变化,需重新计算外观
• CACHE (0x04):缓存失效,需重建渲染缓存
• RENDER (0x08):需要重绘
• TREE (0x10):子节点增删,需重建渲染树
标记会向父节点传播,布局/渲染阶段只处理脏节点。
| 宏/函数 | 说明 |
|---|---|
XUI_WIDGET_DIRTY_LAYOUT | 布局脏 (0x01) |
XUI_WIDGET_DIRTY_STYLE | 样式脏 (0x02) |
XUI_WIDGET_DIRTY_CACHE | 缓存脏 (0x04) |
XUI_WIDGET_DIRTY_RENDER | 渲染脏 (0x08) |
XUI_WIDGET_DIRTY_TREE | 树结构脏 (0x10) |
XUI_WIDGET_DIRTY_ALL | 全部脏 (0x1F) |
int xuiInvalidateRect(xui_context pContext, xui_rect_i_t tRect) | 标记矩形区域脏 |
int xuiInvalidateAll(xui_context pContext) | 全部标记脏 |
int xuiHasDamage(xui_context pContext) | 是否有脏区域 |
gcc -O2 -I. -o ch124.exe ch124_main.c src\xui_*.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch124.exe
// 修改控件文本 → 自动标记脏
xuiLabelSetText(pLabel, "新文本");
// 内部自动:标记 LAYOUT|STYLE|CACHE|RENDER
// 并向父节点传播 LAYOUT
// 手动标记(自定义控件中)
xuiWidgetInvalidate(pWidget,
XUI_WIDGET_DIRTY_RENDER);
// 添加子控件 → TREE 脏
xuiWidgetAddChild(pPanel, pNewChild);
// 自动标记 TREE,下帧重建渲染树
// 帧末检查:是否有需要渲染的区域
if (xuiHasDamage(pCtx)) {
// 仅重绘脏区域
xuiRender(pCtx, NULL, rects, n);
} else {
// 无变化,跳过渲染(省电)
}
// 强制全量刷新(如主题切换)
xuiInvalidateAll(pCtx);将以下代码保存为 ch124_main.c,编译后运行即可观察脏标记驱动的按需渲染:
#include "xge.h"
#include "xui.h"
#include <stdio.h>
static xui_context g_pCtx;
static int g_nFrame = 0;
static int onFrame(void* pUser) {
(void)pUser;
xgeClear(0x081114FF);
xuiUpdate(g_pCtx, xgeGetDelta());
xuiLayout(g_pCtx);
// 仅在有脏区域时渲染
if (xuiHasDamage(g_pCtx)) {
xuiRender(g_pCtx, NULL, NULL, 0);
printf("Frame %d: rendered (dirty)\n", g_nFrame);
} else {
printf("Frame %d: skipped (clean)\n", g_nFrame);
}
// 每 120 帧强制全量刷新
if (++g_nFrame % 120 == 0) {
xuiInvalidateAll(g_pCtx);
printf(" -> InvalidateAll triggered\n");
}
return XGE_OK;
}
int main(void) {
xge_desc_t d = {0};
d.iWidth = 800;
d.iHeight = 600;
d.sTitle = "ch124 - Dirty Flags";
d.iFlags = XGE_INIT_VSYNC;
xgeInit(&d);
xuiCreate(&g_pCtx);
extern const xui_proxy_t tXgeProxy;
xui_proxy_t tXgeProxy = xuiProxyXge();
xuiSetProxy(g_pCtx, &tXgeProxy);
xuiSetViewportSize(g_pCtx, 800, 600);
xgeRun(onFrame, NULL);
xuiDestroy(g_pCtx);
xgeUnit();
return 0;
}