Chapter 200 · XUI 高级机制

性能优化

XUI 性能策略:缓存复用、虚拟化列表、批渲染、脏矩形、按需渲染。

200

概念

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;
}

代码讲解

  • SUBTREE 缓存:50 个按钮的静态面板缓存为单张纹理,避免每帧重绘。
  • FPS 统计:帧循环中计算并显示实时帧率,用于验证优化效果。
  • 度量优先:先用帧率/DrawCall 定位瓶颈,再选择对应优化策略。
  • 缓存代价:缓存占显存,频繁变化的控件缓存反而更慢。

要点与陷阱

  • 度量优先:使用 xgeFrameStatsGet 定位瓶颈再优化。
  • 缓存代价:缓存占显存,频繁变化的控件缓存反而更慢。
  • 虚拟化:确保 ItemHeight 固定或可快速计算。
  • 批渲染:同一纹理的图元连续提交,避免频繁切换纹理。
  • 层分离:动画控件放 FLOATING 层,避免 NORMAL 层缓存失效。

进阶练习

  1. 性能面板:实时显示 FPS/Draw Call/缓存命中率。
  2. 压力测试:10 万行 ListView + 1000 个按钮,优化到 60FPS。
性能优化效果
图:性能优化渲染效果