Chapter 02 · 引擎基础

第一个窗口:xgeInit 与 xge_desc_t

深入理解引擎初始化的每一个细节——xge_desc_t 的全部字段、初始化标志位组合,以及引擎生命周期管理。

02

概念:引擎生命周期

XGE 的生命周期极其简洁:xgeInit → xgeRun → xgeUnit。整个引擎是单例模式——一个进程只能初始化一次。初始化时通过 xge_desc_t 结构体一次性声明窗口尺寸、标题、运行模式和功能标志。

设计哲学:XGE 采用"描述符一次传入"模式,而非分散的 setter 调用。这保证了引擎在初始化后进入不可变配置状态,避免运行时切换模式带来的复杂性。

xge_desc_t 结构体详解

typedef struct xge_desc_t {
    int         iWidth;         // 窗口宽度(逻辑像素)
    int         iHeight;        // 窗口高度(逻辑像素)
    const char* sTitle;         // 窗口标题(UTF-8)
    int         iFlags;         // 初始化标志位(见下表)
    int         iRunMode;       // 运行模式:GAME_LOOP / MANUAL
    int         iTargetFPS;     // 目标帧率(0 = 不限制)
    void*       pNativeWindow;  // 嵌入已有窗口句柄(可选)
    void*       pUser;          // 用户自定义数据指针
} xge_desc_t;

字段说明

字段类型默认值说明
iWidthint800窗口客户区宽度。HIGHDPI 模式下为逻辑像素
iHeightint600窗口客户区高度
sTitleconst char*"XGE"窗口标题栏文本,支持 UTF-8 中文
iFlagsintXGE_INIT_WINDOW功能标志位组合(按位或)
iRunModeintXGE_RUN_GAME_LOOP帧驱动模式
iTargetFPSint0目标帧率上限,0 表示 VSync 或不限制
pNativeWindowvoid*NULL嵌入模式:传入 HWND / NSView 等原生句柄
pUservoid*NULL透传到帧回调的用户数据

初始化标志位

标志值说明
XGE_INIT_WINDOW0x0000普通窗口模式(默认)
XGE_INIT_FULLSCREEN0x0001全屏模式
XGE_INIT_NOFRAME0x0002无边框窗口
XGE_INIT_RESIZABLE0x0004允许用户拖拽调整大小
XGE_INIT_HIGHDPI0x0008高 DPI 感知(帧缓冲 = 逻辑尺寸 × DPI 缩放)
XGE_INIT_VSYNC0x0010启用垂直同步
XGE_INIT_OFFSCREEN0x0020离屏模式(不创建可见窗口)
XGE_INIT_ON_DEMAND0x0040按需渲染(仅在 InvalidateRect 时重绘)

编译本章示例

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

完整示例:多标志组合

// 创建一个高 DPI、可调大小、带 VSync 的窗口
#include "xge.h"
#include <stdio.h>

static int onFrame(void* pUser) {
    // 获取实际帧缓冲尺寸(HIGHDPI 下可能 > 逻辑尺寸)
    int w = xgeGetWidth();
    int h = xgeGetHeight();

    xgeClear(0x101820FF);

    // 在标题栏实时显示窗口尺寸
    char title[64];
    snprintf(title, sizeof(title), "XGE — %dx%d", w, h);
    xgeSetTitle(title);

    return XGE_OK;
}

int main(void) {
    xge_desc_t d = {0};
    d.iWidth     = 1280;
    d.iHeight    = 720;
    d.sTitle     = "XGE — 1280x720";
    d.iFlags     = XGE_INIT_WINDOW
                 | XGE_INIT_RESIZABLE
                 | XGE_INIT_HIGHDPI
                 | XGE_INIT_VSYNC;
    d.iRunMode   = XGE_RUN_GAME_LOOP;
    d.iTargetFPS = 0;  // VSync 已限制帧率

    int r = xgeInit(&d);
    if (r != XGE_OK) {
        printf("Init failed: %d\n", r);
        return 1;
    }

    xgeRun(onFrame, NULL);
    xgeUnit();
    return 0;
}

完整代码与可观察结果

可构建的权威源码为 examples/tutorial_capture/ch02_main1.c。它初始化窗口和离屏目标后,绘制窗口边框、标题栏与客户区标记;截图由该程序的 --capture 模式生成。

XGE initialization tutorial render target screenshot
图:第 2 章示例已完成初始化并绘制到离屏目标;边框、标题栏和中央客户区标记说明窗口和渲染路径均已建立。
执行效果

1280×720 可调整大小的窗口,标题栏实时显示当前尺寸。在 2x DPI 显示器上帧缓冲实际为 2560×1440。

错误码

xgeInit 返回 xge_result_t 枚举值:

返回值含义常见原因
XGE_OK (0)成功—
XGE_ERROR_ALREADY_INITIALIZED (-4)重复初始化调用了两次 xgeInit
XGE_ERROR_BACKEND_FAILED (-8)平台后端失败无法创建窗口/OpenGL 上下文
XGE_ERROR_GPU_FAILED (-9)GPU 初始化失败显卡不支持 GL 3.3
XGE_ERROR_OUT_OF_MEMORY (-5)内存不足极端情况

要点与陷阱

  • 必须用 {0} 初始化描述符——未赋值的字段应为 0/NULL,否则行为未定义。
  • HIGHDPI 的影响:启用后 xgeGetWidth() 返回逻辑尺寸,但帧缓冲实际像素 = 逻辑 × DPI。绘制坐标使用逻辑像素。
  • OFFSCREEN 模式不创建窗口,适合服务器端渲染或 CI 截图。此模式下 xgeRun 不会阻塞,需手动调用 xgeFrame()。
  • pNativeWindow:Windows 上传入 HWND,引擎将在该窗口内渲染而非创建新窗口。
  • xgeUnit 会释放一切——纹理、字体、音频、场景全部销毁。调用后不可再使用任何 XGE API。

进阶练习

  1. 创建一个 XGE_INIT_NOFRAME | XGE_INIT_RESIZABLE 的无边框窗口,在帧回调中检测 ESC 键并调用 xgeQuit() 退出。
  2. 使用 XGE_INIT_OFFSCREEN 模式初始化,调用 xgeFrame() 渲染 60 帧后退出(无需窗口出现)。
  3. 设置 iTargetFPS = 30,观察帧率被限制在 30 FPS。