Chapter 99 · 离屏渲染与 GPU 管线

Offscreen 封装

Offscreen 将 EGL 上下文 + Render Target 打包为一个对象,简化离屏渲染的使用。

99

概念

Offscreen 是 EGL + RT 的高层封装。一次 Init 即创建独立的 GL 上下文和帧缓冲, 无需手动管理 EGL 细节。适合需要独立渲染环境的场景:缩略图生成、SVG 光栅化、纹理烘焙。

API 速查

函数说明
int xgeOffscreenInit(xge_offscreen pOffscreen, int iWidth, int iHeight)初始化离屏环境
void xgeOffscreenUnit(xge_offscreen pOffscreen)销毁离屏环境
xge_render_target xgeOffscreenRenderTarget(xge_offscreen pOffscreen)获取内部 RT
int xgeOffscreenReadPixels(xge_offscreen pOffscreen, void* pPixels, int iStride)回读像素

编译本章示例

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

示例:生成缩略图

// 创建 256×256 离屏环境
xge_offscreen_t os;
xgeOffscreenInit(&os, 256, 256);

// 获取 RT 进行绘制
xge_render_target rt = xgeOffscreenRenderTarget(&os);

xge_pass_t pass;
xgePassInit(&pass, rt, XGE_PASS_CLEAR_COLOR, 0x00000000);
xgePassBegin(&pass);

// 绘制缩略图内容
xgeDraw(&sceneTex, 0, 0);

xgePassEnd(&pass);

// 回读像素并保存
uint8_t pixels[256 * 256 * 4];
xgeOffscreenReadPixels(&os, pixels, 256 * 4);
xgeImageSavePNG("thumb.png", 256, 256,
    pixels, 256 * 4);

// 销毁
xgeOffscreenUnit(&os);

完整代码

将以下代码保存为 ch99_main.c,编译后运行即可看到离屏渲染生成缩略图效果:

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

static xge_offscreen_t g_os;
static xge_font g_font = NULL;
static float g_time = 0.0f;
static int g_saved = 0;

static int onFrame(void* pUser) {
    (void)pUser;
    xgeClear(0x081114FF);
    g_time += 0.016f;

    // 离屏渲染缩略图内容
    xge_render_target rt = xgeOffscreenRenderTarget(&g_os);
    xge_pass_t pass;
    xgePassInit(&pass, rt, XGE_PASS_CLEAR_COLOR, 0x222244FF);
    xgePassBegin(&pass);

    // 绘制动态场景
    float cx = 64.0f + 40.0f * cosf(g_time);
    float cy = 64.0f + 40.0f * sinf(g_time);
    xgeShapeCircleFill(cx, cy, 20.0f, 0xFF8844FF);
    xgeShapeRectStroke((xge_rect_t){10, 10, 108, 108}, 1.0f, 0x44FFFFFF)

    xgePassEnd(&pass);

    // 第一秒后保存一次缩略图
    if (!g_saved && g_time > 1.0f) {
        uint8_t pixels[128 * 128 * 4];
        xgeOffscreenReadPixels(&g_os, pixels, 128 * 4);
        xgeImageSavePNG("thumbnail.png", 128, 128,
            pixels, 128 * 4);
        g_saved = 1;
    }

    xgeTextDraw(g_font,
        g_saved ? "Thumbnail saved to thumbnail.png"
                : "Rendering offscreen...",
        50.0f, 300.0f, 0xFFFFFFFF)

    return XGE_OK;
}

int main(void) {
    xge_desc_t d = {0};
    d.iWidth  = 800;
    d.iHeight = 600;
    d.sTitle  = "ch99 - Offscreen Thumbnail";
    d.iFlags  = XGE_INIT_VSYNC;
    xgeInit(&d);

    xgeFontLoad(&g_font, "Roboto.ttf", 18.0f);

    // 创建 128x128 离屏环境
    xgeOffscreenInit(&g_os, 128, 128);

    xgeRun(onFrame, NULL);

    xgeOffscreenUnit(&g_os);
    xgeFontFree(g_font);
    xgeUnit();
    return 0;
}

代码讲解

  • OffscreenInit:创建独立离屏渲染环境,有自己的 GL 上下文,不与窗口冲突。
  • OffscreenRenderTarget:获取内部 RT,通过 Pass 绘制内容。
  • OffscreenReadPixels:回读离屏像素到 CPU,用于保存 PNG 或进一步处理。
  • 缩略图生成:先离屏渲染小尺寸场景,再回读保存,适合批量生成预览图。

要点与陷阱

  • 独立上下文:Offscreen 有自己的 GL 上下文,不与窗口冲突。
  • 线程:可在任意线程 Init/使用,但同一时间只能一个线程操作。
  • vs RT:如果只需离屏缓冲(共享窗口上下文),直接用 RenderTarget。
  • 性能:Init 有 EGL 初始化开销,避免频繁创建销毁。

进阶练习

  1. 实现 SVG 批量光栅化:Offscreen 逐个渲染 → 保存 PNG。
  2. 纹理烘焙:将 ShapeEx 场景渲染为纹理贴图。
Offscreen 封装效果
图:Offscreen 封装渲染效果