首页
/ OBS Studio 核心图形 API(graphics/graphics.h)完整参考指南

OBS Studio 核心图形 API(graphics/graphics.h)完整参考指南

2026-09-05 21:01:54作者:虞亚竹Luna

本文系统讲解 OBS Studio 底层渲染库 libobs 的 Core Graphics API——即 libobs/graphics/graphics.h 所定义的一套与具体图形后端无关的跨平台绘制接口。读完本篇后,你能完整掌握这套 API 的全部枚举类型、核心数据结构、上下文生命周期、交换链管理、渲染目标切换、纹理/顶点/索引缓冲区管理、混合与深度模板状态控制,以及各平台(Windows/macOS/Linux)专属的扩展能力(GDI 互操作、IOSurface、DMA-BUF 等),并理解其在 libobs/graphics/graphics.c 中的源码级实现方式,为开发 OBS 插件、自定义源(source)或滤镜打下坚实基础。

定位与设计:API 无关的图形子系统封装

libobs/graphics/graphics.h 开头的注释明确说明了它的设计目标:

This is an API-independent graphics subsystem wrapper. This allows the use of OpenGL and different Direct3D versions through one shared interface.

也就是说,gs_* 系列函数是 libobs 对底层图形 API 的统一抽象层。仓库中提供了三个后端实现:

头文件中定义了三种设备类型的常量,可用于运行时判断当前后端:

#define GS_DEVICE_OPENGL 1
#define GS_DEVICE_DIRECT3D_11 2
#define GS_DEVICE_METAL 3

同时 GS_MAX_TEXTURES 8(见 graphics.h)限定了同时可绑定的纹理单元上限。插件作者只需编写一次 gs_* 调用,即可在三种设备上运行,这是 OBS 能够跨 Windows/macOS/Linux 保持单一渲染代码路径的关键。

图形枚举类型(Graphics Enumerations)

以下是 API 参考文档 中定义的全部枚举,与 graphics.h 中的定义一一对应。

gs_draw_mode:图元绘制模式

enum gs_draw_mode {
	GS_POINTS,      // 绘制点
	GS_LINES,       // 绘制独立线段
	GS_LINESTRIP,   // 绘制线段条
	GS_TRIS,         // 绘制独立三角形
	GS_TRISTRIP,     // 绘制三角形条
};

该枚举是 gs_draw(draw_mode, start_vert, num_verts) 的第一个参数,决定顶点缓冲如何解释为图元。OBS 的 2D 场景合成几乎总是使用 GS_TRIS/GS_TRISTRIP

gs_color_format:颜色格式

枚举值 含义
GS_UNKNOWN 未知格式
GS_A8 8 位纯 Alpha 通道
GS_R8 8 位纯 Red 通道
GS_RGBA / GS_BGRX / GS_BGRA 每通道 8 位
GS_R10G10B10A2 RGB 各 10 位、Alpha 2 位
GS_RGBA16 / GS_R16 16 位整数
GS_RGBA16F / GS_RGBA32F 16/32 位浮点
GS_RG16F / GS_RG32F / GS_R16F / GS_R32F 浮点半精度/单精度子集通道
GS_DXT1 / GS_DXT3 / GS_DXT5 BC 系列压缩格式
GS_RGBA_UNORM / GS_BGRX_UNORM / GS_BGRA_UNORM 与对应 8 位格式相同,但不走 sRGB 采样别名
GS_RG16 RG 各 16 位

从源码结构看,头文件还额外提供了若干内联工具函数,帮助模块代码做格式判断(graphics.h):

  • gs_get_format_bpp(format):按格式返回每像素位数(DXT 系返回 4/8,8 位 RGBA 系返回 32,GS_RGBA32F 返回 128);
  • gs_is_compressed_format(format):判断是否为 DXT 压缩格式;
  • gs_is_srgb_format(format):仅 GS_RGBAGS_BGRXGS_BGRA 三种 8 位格式被视为 sRGB 格式;
  • gs_generalize_format(format):把 _UNORM 变体归一化为普通格式;
  • gs_get_format_from_space(space):HDR 色彩空间统一映射到 GS_RGBA16F(见下节)。

gs_color_space:色彩空间(HDR 支持的核心)

enum gs_color_space {
	GS_CS_SRGB,         /* SDR */
	GS_CS_SRGB_16F,     /* High-precision SDR */
	GS_CS_709_EXTENDED, /* Canvas, Mac EDR (HDR) */
	GS_CS_709_SCRGB,    /* 1.0 = 80 nits, Windows/Linux HDR */
};

这组枚举驱动 OBS 的 HDR 管线:交换链创建时依据显示器 HDR 状态选择空间,gs_set_render_target_with_color_space() 则在离屏渲染时显式指定空间。GS_CS_709_SCRGB 语义为 1.0 对应 80 nits,是 Windows/Linux 下 HDR 输出的标准空间。

其他渲染状态枚举

  • gs_zstencil_format:深度模板缓冲格式——GS_ZS_NONE(无)、GS_Z16GS_Z24_S8GS_Z32FGS_Z32F_S8X24
  • gs_index_type:索引缓冲位宽——GS_UNSIGNED_SHORT(16 位)、GS_UNSIGNED_LONG(32 位);
  • gs_cull_mode:面剔除——GS_BACK(默认剔除背面)、GS_FRONTGS_NEITHER
  • gs_blend_type:混合因子,完整 11 项——GS_BLEND_ZEROGS_BLEND_ONEGS_BLEND_SRCCOLORGS_BLEND_INVSRCCOLORGS_BLEND_SRCALPHAGS_BLEND_INVSRCALPHAGS_BLEND_DSTCOLORGS_BLEND_INVDSTCOLORGS_BLEND_DSTALPHAGS_BLEND_INVDSTALPHAGS_BLEND_SRCALPHASAT
  • gs_depth_test:深度比较函数——GS_NEVERGS_LESSGS_LEQUALGS_EQUALGS_GEQUALGS_GREATERGS_NOTEQUALGS_ALWAYS
  • gs_stencil_side:模板测试作用面,注意显式赋值 GS_STENCIL_FRONT=1GS_STENCIL_BACKGS_STENCIL_BOTH
  • gs_stencil_op_type:模板操作——GS_KEEPGS_ZEROGS_REPLACEGS_INCRGS_DECRGS_INVERT
  • gs_cube_sides:立方体贴图 6 个面——GS_POSITIVE_XGS_NEGATIVE_XGS_POSITIVE_YGS_NEGATIVE_YGS_POSITIVE_ZGS_NEGATIVE_Z
  • gs_sample_filter:采样过滤——GS_FILTER_POINTGS_FILTER_LINEARGS_FILTER_ANISOTROPIC 及 6 种 min/mag/mip 组合;
  • gs_address_mode:纹理寻址——GS_ADDRESS_CLAMPGS_ADDRESS_WRAPGS_ADDRESS_MIRRORGS_ADDRESS_BORDERGS_ADDRESS_MIRRORONCE
  • gs_texture_type:纹理维度——GS_TEXTURE_2DGS_TEXTURE_3DGS_TEXTURE_CUBE

核心数据结构(Graphics Structures)

gs_monitor_info:显示器几何信息

struct gs_monitor_info {
	int  rotation_degrees;  // 旋转角度
	long x, y;              // 显示器左上角坐标
	long cx, cy;            // 宽/高
};

由 Windows 平台的 gs_get_duplicator_monitor_info() 填充,供显示器捕获类源使用。

gs_tvertarray 与 gs_vb_data:顶点数据描述

struct gs_tvertarray {
	size_t width;   // 每个顶点的纹理坐标分量数
	void *array;   // 纹理坐标数组
};

struct gs_vb_data {
	size_t num;
	struct vec3 *points;      // 顶点位置
	struct vec3 *normals;     // 法线
	struct vec3 *tangents;    // 切线
	uint32_t *colors;         // 顶点色(ARGB 打包)
	size_t num_tex;
	struct gs_tvertarray *tvarray;  // 多组纹理坐标
};

graphics.h 提供了两个内联配套函数:

  • gs_vbdata_create()bzalloc 一个零初始化的 gs_vb_data
  • gs_vbdata_destroy(data):释放 points/normals/tangents/colors/每组 tvarray 及结构体本身。

gs_vertexbuffer_create() 对缓冲所有权的约定正是围绕这两个函数:结构体及内部缓冲用 bmalloc()/bzalloc()/brealloc() 分配后,所有权移交给创建函数,调用方不再自行释放(除非使用 GS_DUP_BUFFER 标志)。

gs_sampler_info:采样器状态

struct gs_sampler_info {
	enum gs_sample_filter filter;
	enum gs_address_mode address_u, address_v, address_w;
	int max_anisotropy;      // 各向异性级别
	uint32_t border_color;   // GS_ADDRESS_BORDER 时的边界色
};

其他结构

  • gs_display_modewidth/height/bits/freq,描述分辨率与刷新率;
  • gs_rectx/y/cx/cy,视口与剪裁矩形统一用它;
  • gs_window:跨平台的原生窗口句柄,成员按平台条件编译(graphics.h):
struct gs_window {
#if defined(_WIN32)
	void *hwnd;                              // Windows: HWND
#elif defined(__APPLE__)
	__unsafe_unretained id view;             // macOS: NSView
#elif defined(__linux__) || defined(__FreeBSD__)
	uint32_t id;  void *display;             // Linux/X11: 窗口 ID 与 Display
#endif
};
  • gs_init_data:交换链初始化参数:
struct gs_init_data {
	struct gs_window window;        // 目标原生控件
	uint32_t cx, cy;                 // 初始尺寸
	uint32_t num_backbuffers;         // 后备缓冲数量
	enum gs_color_format format;    // 颜色格式
	enum gs_zstencil_format zsformat; // 深度模板格式
	uint32_t adapter;                // 适配器索引(gs_enum_adapters 得到)
};

常用标志位宏

graphics.h 还定义了若干按位或组合的标志常量,散见于纹理、缓冲创建函数:

用途
GS_BUILD_MIPMAPS 1 << 0 自动构建 mipmap(文档注明“未完全测试”)
GS_DYNAMIC 1 << 1 允许实时动态更新(纹理/顶点/索引缓冲通用)
GS_RENDER_TARGET 1 << 2 纹理可作为渲染目标
GS_DUP_BUFFER 1 << 4 创建时不转移缓冲所有权
GS_FLIP_U / GS_FLIP_V 1 << 0 / 1 << 1 gs_draw_sprite 系列的水平/垂直翻转
GS_CLEAR_COLOR / GS_CLEAR_DEPTH / GS_CLEAR_STENCIL 1<<0/1<<1/1<<2 gs_clear() 的清除标志

gs_create() 的返回值常量:GS_SUCCESS 0GS_ERROR_FAIL -1GS_ERROR_MODULE_NOT_FOUND -2GS_ERROR_NOT_SUPPORTED -3

初始化与上下文函数(Initialization Functions)

gs_create / gs_destroy:创建与销毁图形上下文

int gs_create(graphics_t **graphics, const char *module, uint32_t adapter);
void gs_destroy(graphics_t *graphics);

第二个参数 module 是要动态加载的后端子系统模块名。查看 graphics.c 的实现可以看到完整流程:

  1. bzalloc 一个 graphics_subsystem 并初始化互斥锁;
  2. os_dlopen(module) 加载后端模块,失败则返回 GS_ERROR_MODULE_NOT_FOUND
  3. load_graphics_imports() 解析后端导出的函数表(gs_exports);
  4. 调用后端 device_create(&device, adapter) 创建设备,非 GS_SUCCESS 即走错误路径;
  5. graphics_init() 完成子系统内部初始化。

任何一步失败都会 gs_destroy() 清理并返回对应错误码,调用方据此可尝试下一个后端或适配器。

gs_enum_adapters:枚举适配器

void gs_enum_adapters(bool (*callback)(void *param, const char *name, uint32_t id), void *param);

文档说明这主要适用于 Windows(多显卡选择)。从 graphics.c 看,当后端不支持适配器枚举时,会回退到调用一次 callback(param, "Default", 0)——因此插件不应假设枚举结果一定非平凡。

上下文锁:gs_enter_context / gs_leave_context / gs_get_context

void        gs_enter_context(graphics_t *graphics);
void        gs_leave_context(void);
graphics_t *gs_get_context(void);

这是线程安全模型的核心。graphics.c 中每个线程持有一个 THREAD_LOCAL graphics_t *thread_graphics,进入/离开上下文是可重入引用计数式的:

  • gs_enter_context() 检查当前线程的 thread_graphics;若已锁其他上下文则先全部退出,再 pthread_mutex_lock(&graphics->mutex)、调用后端 device_enter_context(),并 os_atomic_inc_long(&graphics->ref)
  • gs_leave_context() 原子递减引用计数,只有降到 0 时才真正调用后端 device_leave_context() 并释放互斥锁(graphics.c);
  • gs_get_context() 直接返回当前线程的 thread_graphics

因此插件的典型用法是成对调用:

gs_enter_context(my_graphics);
/* 所有 gs_* 资源操作与绘制 */
gs_leave_context();

而绝大多数绘制函数内部都先走 gs_valid() 检查:未在上下文中调用只会打印 LOG_DEBUG 日志("%s: called while not in a graphics context")并静默返回,不会崩溃(graphics.c)。

子系统初始化时的内部资源

graphics_init() 可以看到上下文创建时自动构建的内容,理解它们有助于调试绘制类问题:

  • 一个 512 顶点容量的立即模式顶点缓冲IMMEDIATE_COUNT 512GS_DYNAMIC 标志),供 gs_render_start()/gs_vertex2f() 系列使用;
  • 三套四边形 sprite 顶点缓冲(普通、子区域动态、UV 翻转),支撑 gs_draw_sprite()/gs_draw_sprite_subregion() 的高效实现;
  • 矩阵栈压入单位矩阵;
  • 默认混合状态设为“源 Alpha / 反源 Alpha”(RGB 用 GS_BLEND_SRCALPHA+GS_BLEND_INVSRCALPHA,Alpha 用 GS_BLEND_ONE+GS_BLEND_INVSRCALPHA,操作 GS_BLEND_OP_ADD),这正是文档所述 gs_reset_blend_state() 恢复的默认值。

矩阵栈函数(Matrix Stack Functions)

世界矩阵以栈形式管理,全部函数操作栈顶矩阵:

函数 作用
gs_matrix_push() 压栈,复制当前矩阵
gs_matrix_pop() 弹栈,恢复上一矩阵
gs_matrix_identity() 置为单位矩阵
gs_matrix_transpose() 转置当前矩阵
gs_matrix_set(const struct matrix4 *matrix) 直接设置
gs_matrix_get(struct matrix4 *dst) 读取当前矩阵
gs_matrix_mul(const struct matrix4 *matrix) 右乘指定矩阵
gs_matrix_rotquat(const struct quat *rot) 乘四元数旋转
gs_matrix_rotaa(const struct axisang *rot) / gs_matrix_rotaa4f(x, y, z, angle) 乘轴角旋转
gs_matrix_translate(const struct vec3 *pos) / gs_matrix_translate3f(x, y, z) 平移
gs_matrix_scale(const struct vec3 *scale) / gs_matrix_scale3f(x, y, z) 缩放

每个 push/pop 必须成对,OBS 场景树(scene item 的 T 矩阵)在每帧逐层 push/translate/rotate/scale/pop 来构建合成层级。matrix4quataxisangvec3 等数学类型同位于 libobs/graphics/ 目录(如 matrix4.hquat.h),可参考 matrix4 参考页

2D 绘制辅助函数(Draw Functions)

Sprite 系列

void gs_draw_sprite(gs_texture_t *tex, uint32_t flip, uint32_t width, uint32_t height);
void gs_draw_quadf(gs_texture_t *tex, uint32_t flip, float width, float height);
void gs_draw_sprite_subregion(gs_texture_t *tex, uint32_t flip,
                              uint32_t x, uint32_t y, uint32_t cx, uint32_t cy);
  • gs_draw_sprite():绘制 2D sprite,自动把当前 effect 的 "image" 参数设为该纹理并渲染一个四边形;tex 可为 NULL(配合纯色 effect 使用);width/height 传 0 时使用纹理原始尺寸;flip 可取 0GS_FLIP_U(水平翻转)、GS_FLIP_V(垂直翻转)或其组合;
  • gs_draw_quadf():同上,但宽高为浮点数,适合亚像素定位;
  • gs_draw_sprite_subregion():只绘制纹理的 [x, y, cx, cy] 子区域——OBS 的“裁剪区域”功能即基于此。

视口与投影模式

函数 作用
gs_reset_viewport() 视口重置为当前交换链尺寸
gs_set_2d_mode() 投影矩阵置为屏幕尺寸正交模式(2D 合成标准入口)
gs_set_3d_mode(double fovy, double znear, double zfar) 屏幕尺寸透视模式
gs_perspective(fovy, aspect, znear, zfar) 自定义透视投影(fovy 单位:度)
gs_ortho(left, right, top, bottom, znear, zfar) 正交投影
gs_frustum(left, right, top, bottom, znear, zfar) 视锥投影
gs_viewport_push() / gs_viewport_pop() 视口保存/恢复
gs_projection_push() / gs_projection_pop() 投影矩阵保存/恢复
gs_set_viewport(x, y, width, height) 设置视口(相对左上角)
gs_get_viewport(struct gs_rect *rect) 读取视口
gs_set_scissor_rect(const struct gs_rect *rect) 设置剪裁矩形,传 NULL 清除

混合状态栈

void gs_blend_state_push(void);   // 保存当前混合状态
void gs_blend_state_pop(void);    // 恢复
void gs_reset_blend_state(void);  // 恢复为默认:源 Alpha + 反源 Alpha

交换链(Swap Chains)

交换链是把渲染结果呈现到原生窗口控件的载体,OBS 主预览窗口、每个显示输出各自持有一条。

函数 说明
gs_swapchain_t *gs_swapchain_create(const struct gs_init_data *data) 创建交换链,失败返回 NULL
void gs_swapchain_destroy(gs_swapchain_t *swapchain) 销毁
void gs_resize(uint32_t cx, uint32_t cy) 调整当前活动交换链尺寸
void gs_update_color_space(void) 依据最近显示器的 HDR 状态更新交换链色彩空间
void gs_get_size(uint32_t *cx, uint32_t *cy) / gs_get_width() / gs_get_height() 查询当前交换链尺寸

gs_swapchain_create() 接受的是上文 gs_init_data,其中 window 成员必须已按平台填好(Windows 的 HWND、macOS 的 NSView、Linux 的窗口 ID+Display)。

资源加载(Resource Loading)

“加载”指把资源绑定到当前渲染状态,而不是创建资源本身:

函数 说明
gs_load_vertexbuffer(gs_vertbuffer_t *vertbuffer) 绑定顶点缓冲,NULL 为卸载
gs_load_indexbuffer(gs_indexbuffer_t *indexbuffer) 绑定索引缓冲,NULL 为卸载
gs_load_texture(gs_texture_t *tex, int unit) 绑定纹理到指定单元(一般不手动调用)
gs_load_samplerstate(gs_samplerstate_t *samplerstate, int unit) 绑定采样器(一般不手动调用)
gs_load_swapchain(gs_swapchain_t *swapchain) 绑定交换链,NULL 为卸载

纹理单元受 GS_MAX_TEXTURES 8 限制;effect 框架通常会自动完成纹理/采样器绑定,手动调用多出现在自定义渲染代码中。

渲染目标与逐帧绘制控制

渲染目标切换

enum gs_color_space gs_get_color_space(void);
gs_texture_t *gs_get_render_target(void);
gs_zstencil_t *gs_get_zstencil_target(void);
void gs_set_render_target(gs_texture_t *tex, gs_zstencil_t *zstencil);
void gs_set_render_target_with_color_space(gs_texture_t *tex, gs_zstencil_t *zstencil,
                                           enum gs_color_space space);
void gs_set_cube_render_target(gs_texture_t *cubetex, int side, gs_zstencil_t *zstencil);
  • gs_set_render_target() 等价于以隐式 GS_CS_SRGB 空间设置目标——OBS 的每帧画布(canvas)渲染、滤镜链每一级都是切换渲染目标 + 切换视口 + 切换 effect 的组合;
  • HDR 滤镜需要用 gs_set_render_target_with_color_space() 显式指定 GS_CS_SRGB_16F/GS_CS_709_* 空间;
  • gs_set_cube_render_target() 允许渲染到立方体贴图的某个面(sidegs_cube_sides),zstencil 可为 NULL

纹理拷贝与暂存

void gs_copy_texture(gs_texture_t *dst, gs_texture_t *src);
void gs_stage_texture(gs_stagesurf_t *dst, gs_texture_t *src);

gs_stage_texture() 把 GPU 纹理拷入 staging 表面再读回 RAM,文档提示“最好隔一帧处理以防阻塞(stalling)”。

场景开始/结束与 gs_draw

void gs_begin_scene(void);
void gs_end_scene(void);
void gs_draw(enum gs_draw_mode draw_mode, uint32_t start_vert, uint32_t num_verts);

文档明确 gs_begin_scene()/gs_end_scene() 由 libobs 自动调用,插件无需手动调用gs_draw() 是最低层的绘制入口:按 draw_mode 解释从 start_vert 开始的 num_verts 个顶点。

清除、呈现与刷新

void gs_clear(uint32_t clear_flags, const struct vec4 *color, float depth, uint8_t stencil);
void gs_present(void);   // 把渲染结果提交到屏幕
void gs_flush(void);     // 强制刷新 GPU 命令

clear_flags 可组合 GS_CLEAR_COLOR | GS_CLEAR_DEPTH | GS_CLEAR_STENCIL

状态开关与混合/深度/模板函数

函数 说明
gs_set_cull_mode(enum gs_cull_mode mode) / gs_get_cull_mode() 设置/读取面剔除模式
gs_enable_blending(bool enable) 开关混合
gs_enable_depth_test(bool enable) 开关深度测试
gs_enable_stencil_test(bool enable) 开关模板测试
gs_enable_stencil_write(bool enable) 开关模板写入
gs_enable_color(red, green, blue, alpha) 逐通道开关写入
gs_blend_function(src, dest) 统一设置 RGB+Alpha 混合因子
gs_blend_function_separate(src_c, dest_c, src_a, dest_a) RGB 与 Alpha 分别设置
gs_blend_op(enum gs_blend_op_type op) 混合方程操作(ADD/SUBTRACT/REVERSE_SUBTRACT/MIN/MAX)
gs_depth_function(enum gs_depth_test test) 设置深度比较函数
gs_stencil_function(side, test) 模板比较函数,sideGS_STENCIL_FRONT/BACK/BOTH
gs_stencil_op(side, fail, zfail, zpass) 模板测试失败/深度失败/深度通过三态操作

典型 3D 源(如 3D 文本)会:开深度测试 → gs_depth_function(GS_LESS) → 绘制 → 关深度测试;2D 叠加层则通常保持混合开启、剔除关闭。

纹理函数(Texture Functions)

创建与查询

gs_texture_t *gs_texture_create(uint32_t width, uint32_t height,
                                enum gs_color_format color_format,
                                uint32_t levels, const uint8_t **data, uint32_t flags);
gs_texture_t *gs_texture_create_from_file(const char *file);
void   gs_texture_destroy(gs_texture_t *tex);
uint32_t gs_texture_get_width(const gs_texture_t *tex);
uint32_t gs_texture_get_height(const gs_texture_t *tex);
enum gs_color_format gs_texture_get_color_format(const gs_texture_t *tex);
  • levels:总 mip 级数,1 表示无 mipmap。头文件还提供了 gs_get_total_levels(width, height, depth) 内联函数(graphics.h),按最大边长逐次折半计算所需级数,可直接用于正确传参;
  • flags 按位或组合 GS_BUILD_MIPMAPS(文档注明“未完全测试”)、GS_DYNAMICGS_RENDER_TARGET
  • gs_texture_create_from_file() 从图像文件加载;文档特别提示:动画 GIF 不建议用它,应改用 image-file 辅助器(image-file 参考页,实现位于 libobs/graphics/image-file.c)。

映射与更新

bool gs_texture_map(gs_texture_t *tex, uint8_t **ptr, uint32_t *linesize);
void gs_texture_unmap(gs_texture_t *tex);
void gs_texture_set_image(gs_texture_t *tex, const uint8_t *data, uint32_t linesize, bool invert);
  • gs_texture_map()/gs_texture_unmap():把动态纹理映射到 CPU 可写内存,linesize(pitch)作为出参返回——写入时务必按返回的 pitch 而非 width*bpp 逐行拷贝;
  • gs_texture_set_image():批量更新动态纹理,invert=true 表示垂直翻转图像。

Linux/FreeBSD/DragonFly 专属:DMA-BUF 支持

gs_texture_t *gs_texture_create_from_dmabuf(unsigned int width, unsigned int height,
                                             uint32_t drm_format, enum gs_color_format color_format,
                                             uint32_t n_planes, const int *fds,
                                             const uint32_t *strides, const uint32_t *offsets,
                                             const uint64_t *modifiers);

文档要点:

  • 交换 DMA-BUF 因多平面特性而繁琐(YUV 每个平面可作一个颜色通道,或监视器缓冲把光标放在独立平面);
  • 该函数分开处理 OBS 色彩格式与 DRM 格式,因此可用着色器转换不支持的格式(如 YUV)再创建纹理;但必须确保两者匹配正确,否则纹理可能创建或渲染失败;
  • modifiers 数组内所有值必须相等,逐平面不同 modifier 不受支持;
  • 各平面参数(fds/strides/offsets/modifiers)数组长度均为 n_planes

配套的能力查询与同步对象 API(同样仅 Linux/FreeBSD/DragonFly):

函数 说明
gs_query_dmabuf_capabilities(&flags, &drm_formats, &n_formats) 查询是否支持隐式 modifier(GS_DMABUF_FLAG_IMPLICIT_MODIFIERS_SUPPORTED)及支持的 DRM 格式;调用方需 bfree() 释放 drm_formats
gs_query_dmabuf_modifiers_for_format(drm_format, &modifiers, &n_modifiers) 查询某格式支持的显式 modifier;同样需 bfree() 释放
gs_query_sync_capabilities() 检查是否支持同步对象
gs_sync_create() 在命令流中插入 fence,创建在 fence 执行后 signal 的同步对象
gs_sync_create_from_syncobj_timeline_point(syncobj_fd, timeline_point) 从 DRM syncobj 时间线点创建
gs_sync_destroy(sync) 销毁;若仍在使用则标记为延迟销毁
gs_sync_export_syncobj_timeline_point(sync, syncobj_fd, timeline_point) 导出为 DRM syncobj 时间线点
gs_sync_signal_syncobj_timeline_point(syncobj_fd, timeline_point) 直接 signal 某时间线点
gs_sync_wait(sync) 阻塞命令流直至同步对象 signal

这套 API 支撑 OBS 在 Wayland/X11 下的零拷贝屏幕/窗口捕获:捕获插件(如 plugins/linux-capture/plugins/linux-pipewire/)把帧的 DMA-BUF 直接交给 GPU,配合 sync 对象完成 CPU/GPU 同步。

macOS 专属:IOSurface

gs_texture_t *gs_texture_create_from_iosurface(void *iosurf);
bool gs_texture_rebind_iosurface(gs_texture_t *texture, void *iosurf);

从系统 IOSurface 创建纹理(macOS 窗口/屏幕捕获路径),以及把已有纹理重绑到另一张 IOSurface,避免重建 GPU 资源。

Windows 专属:GDI 互操作与共享纹理

gs_texture_t *gs_texture_create_gdi(uint32_t width, uint32_t height);
void *gs_texture_get_dc(gs_texture_t *gdi_tex);
void gs_texture_release_dc(gs_texture_t *gdi_tex);
gs_texture_t *gs_texture_open_shared(uint32_t handle);
bool gs_gdi_texture_available(void);
bool gs_shared_texture_available(void);
  • gs_texture_create_gdi() 创建 GDI 可锁定的互操作纹理,gs_texture_get_dc() 取出 HDC 绘制后必须用 gs_texture_release_dc() 释放;
  • gs_texture_open_shared() 从共享句柄导入纹理,配合 gs_gdi_texture_available() / gs_shared_texture_available() 先行探测能力。

从源码结构看,Windows 路径还有 keyed mutex 同步(gs_texture_acquire_sync()/gs_texture_release_sync())等跨设备纹理交换接口(graphics.h),供游戏/窗口捕获在多个设备间安全传递帧。

立方体贴图函数(Cube Texture Functions)

gs_texture_t *gs_cubetexture_create(uint32_t size, enum gs_color_format color_format,
                                    uint32_t levels, const uint8_t **data, uint32_t flags);
void     gs_cubetexture_destroy(gs_texture_t *cubetex);
uint32_t gs_cubetexture_get_size(const gs_texture_t *cubetex);
enum gs_color_format gs_cubetexture_get_color_format(const gs_texture_t *cubetex);
void gs_cubetexture_set_image(gs_texture_t *cubetex, uint32_t side,
                              const void *data, uint32_t linesize, bool invert);

size 是立方体的宽/高/深统一边长;flags 与 2D 纹理相同(GS_BUILD_MIPMAPS/GS_DYNAMIC/GS_RENDER_TARGET)。sidegs_cube_sides 六面之一。立方体贴图可用于 3D 环境的背景板(backdrop)渲染,头文件中还有配套的 gs_draw_cube_backdrop() 辅助函数(graphics.h)。

暂存表面函数(Staging Surface Functions)

Staging surface 用于把纹理从 VRAM 高效拷回 RAM:

函数 说明
gs_stagesurf_t *gs_stagesurface_create(width, height, color_format) 创建
void gs_stagesurface_destroy(gs_stagesurf_t *stagesurf) 销毁
gs_stagesurface_get_width/height(stagesurf) 查询尺寸
gs_stagesurface_get_color_format(stagesurf) 查询格式
gs_stagesurface_map(stagesurf, &data, &linesize) 映射读取;完成后必须 gs_stagesurface_unmap()
void gs_stagesurface_unmap(gs_stagesurf_t *stagesurf) 解除映射

完整“GPU→CPU”回读流程:gs_stagesurface_create() → 每帧 gs_stage_texture(stagesurf, src_tex)gs_stagesurface_map() 读像素 → gs_stagesurface_unmap()。OBS 的视频回放、预览截图功能依赖这条链路。

Z-Stencil、采样器、顶点与索引缓冲

Z-Stencil

gs_zstencil_t *gs_zstencil_create(uint32_t width, uint32_t height, enum gs_zstencil_format format);
void gs_zstencil_destroy(gs_zstencil_t *zstencil);

作为 gs_set_render_target() 的第二参数使用,为离屏渲染提供深度/模板缓冲。

采样器状态

gs_samplerstate_t *gs_samplerstate_create(const struct gs_sampler_info *info);
void gs_samplerstate_destroy(gs_samplerstate_t *samplerstate);

info 即上文 gs_sampler_info;创建后可通过 effect 参数 gs_effect_set_next_sampler() 或手动 gs_load_samplerstate() 绑定。

顶点缓冲

gs_vertbuffer_t *gs_vertexbuffer_create(struct gs_vb_data *data, uint32_t flags);
void gs_vertexbuffer_destroy(gs_vertbuffer_t *vertbuffer);
void gs_vertexbuffer_flush(gs_vertbuffer_t *vertbuffer);
void gs_vertexbuffer_flush_direct(gs_vertbuffer_t *vertbuffer, const struct gs_vb_data *data);
struct gs_vb_data *gs_vertexbuffer_get_data(const gs_vertbuffer_t *vertbuffer);

关键约定(均与文档一致):

  • data 应由 gs_vbdata_create() 创建,各成员用 bmalloc()/bzalloc()/brealloc() 分配;默认所有权移交给创建函数,调用方不得再释放;
  • flags 可组合 GS_DYNAMIC(支持实时动态更新)与 GS_DUP_BUFFER(不转移所有权,适合共享静态四边形等小缓冲——libobs 内部初始化 sprite 缓冲时正是这样用的,见 graphics.c);
  • gs_vertexbuffer_flush():把内部 gs_vb_data 的修改刷入 GPU(仅动态缓冲);
  • gs_vertexbuffer_flush_direct(vertbuffer, data):直接刷入指定数据,无需刷新的组件可留 NULL
  • gs_vertexbuffer_get_data():取得内部数据以就地修改,再配 flush() 更新(仅动态缓冲)。

索引缓冲

gs_indexbuffer_t *gs_indexbuffer_create(enum gs_index_type type, void *indices, size_t num, uint32_t flags);
void     gs_indexbuffer_destroy(gs_indexbuffer_t *indexbuffer);
void     gs_indexbuffer_flush(gs_indexbuffer_t *indexbuffer);
void     gs_indexbuffer_flush_direct(gs_indexbuffer_t *indexbuffer, const void *data);
void    *gs_indexbuffer_get_data(const gs_indexbuffer_t *indexbuffer);
size_t   gs_indexbuffer_get_num_indices(const gs_indexbuffer_t *indexbuffer);
enum gs_index_type gs_indexbuffer_get_type(const gs_indexbuffer_t *indexbuffer);
  • indices 必须用 bmalloc()/bzalloc()/bralloc() 分配,所有权移交给索引缓冲对象GS_DUP_BUFFER 除外);
  • typeGS_UNSIGNED_SHORT(顶点数 ≤ 65535 时推荐,更省内存)或 GS_UNSIGNED_LONG
  • flush/flush_direct/get_data 语义与顶点缓冲完全对称,且仅限 GS_DYNAMIC 缓冲使用。

显示器复制器(Windows Only)与显示器函数

Display Duplicator 基于 Windows 8+ 输出复制 API,用于显示器捕获(屏幕捕获源的首选路径):

gs_duplicator_t *gs_duplicator_create(int monitor_idx);
void  gs_duplicator_destroy(gs_duplicator_t *duplicator);
bool  gs_duplicator_update_frame(gs_duplicator_t *duplicator);
gs_texture_t *gs_duplicator_get_texture(gs_duplicator_t *duplicator);
bool  gs_get_duplicator_monitor_info(int monitor_idx, struct gs_monitor_info *monitor_info);

用法闭环:按索引创建 → 每帧 update_frame() 拉取新帧 → get_texture() 拿到 GPU 纹理直接喂给场景,全程无 CPU 拷贝。gs_get_duplicator_monitor_info() 返回 false 表示索引处无显示器。

另外,跨平台的 gs_is_monitor_hdr(void *monitor) 用于查询某显示器是否处于 HDR 模式,是 gs_update_color_space() 决策与 HDR 输出链路的基础。从源码结构看,Windows 路径还能查询复制器的色彩空间与 SDR 白点(gs_duplicator_get_color_space()/gs_duplicator_get_sdr_white_level()graphics.h),支撑 HDR 屏幕捕获的亮度映射。

立即模式渲染辅助(Render Helper Functions)

这套函数提供 OpenGL 风格的“顶点流”接口,底层写入前文提到的 512 顶点动态立即模式缓冲(graphics.c):

void gs_render_start(bool b_new);          // 开始录制顶点;b_new=true 清空已有数据
void gs_render_stop(enum gs_draw_mode mode); // 结束并按 mode 绘制
gs_vertbuffer_t *gs_render_save(void);      // 结束并返回顶点缓冲对象(不立即绘制)

void gs_vertex2f(float x, float y);
void gs_vertex3f(float x, float y, float z);
void gs_normal3f(float x, float y, float z);
void gs_color(uint32_t color);              // ARGB 打包顶点色
void gs_texcoord(float x, float y, int unit);
void gs_vertex2v(const struct vec2 *v);
void gs_vertex3v(const struct vec3 *v);
void gs_normal3v(const struct vec3 *v);
void gs_color4v(const struct vec4 *v);
void gs_texcoord2v(const struct vec2 *v, int unit);

一个典型的动态线条绘制片段(如自绘 UI 指示器):

gs_render_start(true);
gs_vertex2f(x0, y0);  gs_color(0xFF00FF00);
gs_vertex2f(x1, y1);
gs_render_stop(GS_LINES);

gs_render_save()gs_render_stop() 的区别在于前者把顶点“存成”一个 gs_vertbuffer_t 供之后反复绑定绘制,适合几何不变、位置矩阵每帧变化的图形。

不透明类型总览(Graphics Types)

文档末尾列出的 gs_* 不透明类型(graphics.h 中的 typedef)与各自的结构体名对应关系如下,供在 C++ 代码与文档间对照:

typedef 底层结构
gs_texture_t struct gs_texture
gs_stagesurf_t struct gs_stage_surface
gs_zstencil_t struct gs_zstencil_buffer
gs_vertbuffer_t struct gs_vertex_buffer
gs_indexbuffer_t struct gs_index_buffer
gs_samplerstate_t struct gs_sampler_state
gs_swapchain_t struct gs_swap_chain
gs_texrender_t struct gs_texture_render
gs_shader_t / gs_sparam_t struct gs_shader / struct gs_shader_param
gs_effect_t struct gs_effect
gs_device_t struct gs_device
graphics_t struct graphics_subsystem
gs_sync_t void(平台相关不透明类型)
gs_duplicator_t struct gs_duplicator(Windows)

实践要点小结

  1. 生命周期:插件获取 graphics_t 后,所有资源操作必须包在 gs_enter_context()/gs_leave_context() 内;上下文计数归零才真正解锁,嵌套调用是安全的。
  2. 所有权规则gs_vertexbuffer_create()/gs_indexbuffer_create() 默认接管 bmalloc 系分配的缓冲;共享静态缓冲用 GS_DUP_BUFFER 防止被释放。
  3. 2D 场景三板斧gs_set_2d_mode()gs_set_viewport() → effect 参数 + gs_draw_sprite()gs_render_start()/gs_render_stop(),这是绝大多数 OBS 源/滤镜的合成写法。
  4. 格式选择:SDR 用 GS_RGBA(sRGB);HDR/高精度路径用 GS_CS_* 空间对应的 GS_RGBA16Fgs_get_format_from_space() 的映射逻辑可佐证);压缩纹理仅 DXT1/3/5 三选。
  5. 平台特化:Windows 用 Duplicator/GDI/共享纹理,macOS 用 IOSurface,Linux 系用 DMA-BUF + sync 对象实现零拷贝捕获——编写捕获类插件时应先用各平台的“available/capabilities”查询函数探测能力,再降级到 CPU 路径。
  6. 完整文档交叉引用:本主题原文档为 Core Graphics API 参考;图像文件加载见 image-file 参考;math 类型(vec3/matrix4/quat/axisang)分别见 vec3matrix4quataxisang 各参考页;整体渲染管线背景可参考 backend-designgraphics
登录后查看全文
热门项目推荐
相关项目推荐