XUI 性能策略:缓存复用、虚拟化列表、批渲染、脏矩形、按需渲染。
XUI 性能优化核心策略:
• 缓存复用:SUBTREE/TILED 缓存避免重复绘制(第 192 章)
• 虚拟化:ListView/TreeView 仅渲染可见项
• 批渲染:SpriteBatch/ShapeBatch 合并 Draw Call
• 脏矩形:仅重绘变化区域(XGE_INIT_ON_DEMAND)
• 按需渲染:无变化时跳过整帧渲染
• 层分离:高频更新控件独立层,避免牵连静态控件
| 策略 | 适用场景 | 效果 |
|---|---|---|
SUBTREE 缓存 | 静态面板/标题栏 | 跳过整棵子树绘制 |
TILED 缓存 | 大画布/文档 | 局部脏仅重建分块 |
虚拟化列表 | 万行数据 | O(可见数) 而非 O(总数) |
ShapeBatch | 大量图元 | 合并为单次 Draw Call |
ON_DEMAND | 工具类应用 | 空闲时 0% GPU |
状态缓存 | 按钮/开关 | 状态切换零重建 |
gcc -O2 -I. -o ch200.exe ch200_main.c src\xui_*.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch200.exe
// 1. 按需渲染模式
xge_desc_t tDesc = { 0 };
tDesc.iFlags = XGE_INIT_ON_DEMAND;
xgeInit(&tDesc);
// 2. 静态 UI 子树缓存
xui_cache_policy_t tCache = { 0 };
tCache.iPolicy = XUI_CACHE_POLICY_SUBTREE;
xuiWidgetSetCachePolicy(pHeader, &tCache);
xuiWidgetSetCachePolicy(pSidebar, &tCache);
// 3. 虚拟化列表(10 万行)
xui_list_view_desc_t tListDesc = { 0 };
tListDesc.bVirtualize = 1;
tListDesc.iItemCount = 100000;
// 4. 有变化时请求渲染
xgeInvalidateRect(tDirtyRect);
xgeRenderRequest();将以下代码保存为 ch200_main.c,编译后运行即可看到缓存优化与帧率统计效果:
#include "xge.h"
#include "xui.h"
#include <stdio.h>
static xui_context g_pCtx;
static xui_widget g_pFps;
static int onFrame(void* pUser) {
(void)pUser;
xgeClear(0x1a1a2e);
xui_vec2_t pos;
xgeMouseGet(&pos.x, &pos.y);
xuiInputPointerMove(g_pCtx, pos.x, pos.y);
if (xgeMouseDown(XGE_MOUSE_LEFT))
xuiInputPointerDown(g_pCtx, pos.x, pos.y, 0);
else
xuiInputPointerUp(g_pCtx, pos.x, pos.y, 0);
char buf[64];
sprintf(buf, "FPS: %.0f", 1.0f / xgeGetDelta());
xuiLabelSetText(g_pFps, buf);
xuiUpdate(g_pCtx, xgeGetDelta());
xuiLayout(g_pCtx);
xuiRender(g_pCtx, NULL, NULL, 0);
return XGE_OK;
}
int main(void) {
xge_desc_t d = {0};
d.iWidth = 640; d.iHeight = 480;
d.sTitle = "ch200 - Performance";
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, 640, 480);
xui_widget pRoot;
xuiPanelCreate(g_pCtx, &pRoot, NULL);
xuiWidgetSetRect(pRoot, (xui_rect_t){0, 0, 640, 480});
/* 静态面板 + SUBTREE 缓存 */
xui_widget pPanel;
xuiPanelCreate(g_pCtx, &pPanel, NULL);
xuiWidgetAddChild(pRoot, pPanel);
xuiWidgetSetRect(pPanel, (xui_rect_t){40, 60, 560, 300});
xuiWidgetSetRect(pPanel, (xui_rect_t){0, 0, 560, 300});
xui_cache_policy_t tCache = {0};
tCache.iPolicy = XUI_CACHE_POLICY_SUBTREE;
xuiWidgetSetCachePolicy(pPanel, &tCache);
/* 填充 50 个按钮 */
for (int i = 0; i < 50; i++) {
xui_widget pB;
xuiButtonCreate(g_pCtx, &pB, NULL);
xuiWidgetAddChild(pPanel, pB);
xuiWidgetSetRect(pB, (xui_rect_t){(i % 10) * 54 + 5, (i / 10) * 54 + 5, 48, 48});
xuiWidgetSetRect(pB, (xui_rect_t){0, 0, 48, 48});
}
g_pFps = xuiLabelCreate(g_pCtx, &g_pFps, NULL);
xuiWidgetAddChild(pRoot, g_pFps);
xuiLabelSetText(g_pFps, "FPS: --");
xuiWidgetSetRect(g_pFps, (xui_rect_t){20, 20, 120, 28});
xuiWidgetSetRect(g_pFps, (xui_rect_t){0, 0, 120, 28});
xgeRun(onFrame, NULL);
xuiDestroy(g_pCtx);
xgeUnit();
return 0;
}