Chapter 04 · 引擎基础

手动帧驱动:xgeBegin / xgeEnd / xgeFrame

当你需要完全控制渲染时机——离屏渲染、CI 截图、嵌入式集成——手动模式让你逐帧精确驱动引擎。

04

概念:何时需要手动驱动

默认的 XGE_RUN_GAME_LOOP 模式适合大多数实时应用。但以下场景需要你自行控制帧节奏:

  • 离屏渲染 / CI 截图:无窗口环境,渲染 N 帧后保存图像退出
  • 嵌入式集成:将 XGE 嵌入已有事件循环(如 Qt、GTK)
  • 小程序 / WASM:由浏览器 requestAnimationFrame 驱动
  • 按需渲染:仅在内容变化时绘制(配合 XGE_INIT_ON_DEMAND)

API 速查

函数说明
int xgeBegin(void)开始一帧:轮询事件、计算 Delta
int xgeEnd(void)结束一帧:提交渲染命令
int xgeFrame(void)便捷函数 = xgeBegin + xgeEnd(一帧完整流程)
int xgeRender(void)仅执行渲染提交(不含事件轮询)
int xgeFlush(void)刷新 GL 命令队列
void xgePresent(void)交换前后帧缓冲(呈现到屏幕)
void xgeRenderRequest(void)标记需要重绘(ON_DEMAND 模式)

帧驱动层次

// 从高层到底层:
xgeFrame()          // 最简:一帧完整流程
  ├── xgeBegin()    // 事件轮询 + Delta 计算
  └── xgeEnd()      // 渲染 + 呈现
        ├── xgeRender()   // 提交绘制命令到 GPU
        ├── xgeFlush()    // 刷新命令队列
        └── xgePresent()  // SwapBuffers

编译本章示例

gcc -O2 -I. -o ch04.exe ch04_main.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch04.exe

示例 1:离屏渲染 60 帧后截图

#include "xge.h"
#include <stdio.h>

int main(void) {
    xge_desc_t d = {0};
    d.iWidth  = 1920;
    d.iHeight = 1080;
    d.iFlags  = XGE_INIT_OFFSCREEN;  // 不创建窗口
    d.iRunMode = XGE_RUN_MANUAL;

    xgeInit(&d);

    // 手动驱动 60 帧
    for (int i = 0; i < 60; i++) {
        xgeBegin();

        xgeClear(0x081114FF);
        // ... 绘制场景 ...
        xgeShapeCircleFill(960, 540, 200, 0xFFB454FF);

        xgeEnd();
    }

    // 保存最后一帧为 PNG(第 92 章详解)
    // xgeImageSavePNG("output.png", ...);

    xgeUnit();
    printf("Rendered 60 frames offscreen.\n");
    return 0;
}

示例 2:嵌入已有事件循环

// 伪代码:将 XGE 嵌入自定义主循环
xgeInit(&desc);  // iRunMode = XGE_RUN_MANUAL

while (my_app_running) {
    myProcessEvents();    // 你自己的事件系统
    myUpdateLogic();     // 你自己的逻辑更新

    xgeBegin();          // 让 XGE 处理其内部事件
    xgeClear(0x000000FF);
    myDrawScene();       // 调用 XGE 绘制 API
    xgeEnd();            // 提交 + 呈现

    mySleep(16);         // ~60 FPS 限制
}

xgeUnit();

示例 3:xgeFrame 最简用法

// 使用 xgeFrame() 一步完成(等价于 Begin+End)
for (int i = 0; i < 360; i++) {
    xgeClear(0x0C181CFF);
    // 绘制...
    xgeFrame();  // 一帧完成
}
提示:在 XGE_RUN_MANUAL 模式下,xgeRun(proc, user) 仍然可用——它会调用一次 proc 然后立即返回(不循环)。你也可以完全不调用 xgeRun,直接用 xgeFrame() 驱动。

要点与陷阱

  • Begin/End 必须配对——不配对的调用会导致渲染状态错乱。
  • OFFSCREEN 模式下无 VSync——帧率完全由你的循环速度决定。如需限制,使用 xgeSleep()。
  • xgeGetDelta() 在手动模式下:返回两次 xgeBegin() 之间的真实时间差。如果你用固定步长,Delta 仍然反映真实时间。
  • 不要在 Begin 之前调用绘制 API——xgeClear、xgeShape* 等必须在 Begin/End 之间调用。

进阶练习

  1. 用手动模式渲染 360 帧,每帧旋转一个矩形 1°,最终得到完整一圈动画。
  2. 在手动循环中加入 xgeSleep(33) 限制为 ~30 FPS,用 xgeGetFPS() 验证。
  3. 结合 XGE_INIT_ON_DEMAND,仅在鼠标移动时调用 xgeRenderRequest() + xgeFrame()。

完整代码与可观察结果

权威源码为 examples/tutorial_capture/ch04_main1.c。它使用统一的离屏渲染模板创建 render target、完成 pass 并读回 PNG;因此截图验证的是 Begin/Pass/End 到读回的完整基本路径。

XGE offscreen render target result
图:第 4 章示例真实生成的离屏结果。外框代表 render target 的边界,中心圆和同心线来自同一次 render pass。