OpenGL ES 调试
适用于 Android NDK + LLDB 运行时调试环境。
所有表达式可直接复制到 LLDB 调试窗口运行,临时变量已预定义。
1. GL 状态 Token 数值速查表
Android LLDB 表达式求值器不识别 GL 常量名,必须使用十六进制数值。
1.1 启用/禁用状态查询 (glIsEnabled)
| 常量名 | 十六进制值 | 十进制值 | 说明 |
|---|---|---|---|
GL_BLEND |
0x0BE2 |
3042 | 混合 |
GL_SCISSOR_TEST |
0x0C11 |
3089 | 裁剪测试 |
GL_DEPTH_TEST |
0x0B71 |
2929 | 深度测试 |
GL_STENCIL_TEST |
0x0B90 |
2960 | 模板测试 |
GL_CULL_FACE |
0x0B44 |
2884 | 背面剔除 |
GL_DITHER |
0x0BD0 |
3024 | 抖动 |
GL_POLYGON_OFFSET_FILL |
0x8037 |
32823 | 多边形偏移填充 |
GL_SAMPLE_ALPHA_TO_COVERAGE |
0x809E |
32926 | 采样alpha转覆盖 |
1.2 整数值查询 (glGetIntegerv)
| 常量名 | 十六进制值 | 十进制值 | 说明 |
|---|---|---|---|
GL_VIEWPORT |
0x0BA2 |
2978 | 视口 x, y, w, h |
GL_SCISSOR_BOX |
0x0C12 |
3090 | 裁剪框 x, y, w, h |
GL_BLEND_EQUATION |
0x8009 |
32777 | 混合方程(RGB) |
GL_BLEND_EQUATION_ALPHA |
0x883D |
34877 | 混合方程(Alpha) |
GL_BLEND_SRC_RGB |
0x80C9 |
32969 | 源RGB混合因子 |
GL_BLEND_DST_RGB |
0x80CA |
32970 | 目标RGB混合因子 |
GL_BLEND_SRC_ALPHA |
0x80CB |
32971 | 源Alpha混合因子 |
GL_BLEND_DST_ALPHA |
0x80CC |
32972 | 目标Alpha混合因子 |
GL_ARRAY_BUFFER_BINDING |
0x8894 |
34964 | VBO绑定 |
GL_ELEMENT_ARRAY_BUFFER_BINDING |
0x8895 |
34965 | IBO/EBO绑定 |
GL_VERTEX_ARRAY_BINDING |
0x85B5 |
34229 | VAO绑定 |
GL_CURRENT_PROGRAM |
0x8B8D |
35789 | 当前着色器程序 |
GL_ACTIVE_TEXTURE |
0x84C0 |
33984 | 活跃纹理单元 |
GL_TEXTURE_BINDING_2D |
0x8B82 |
35778 | 2D纹理绑定 |
GL_TEXTURE_BINDING_3D |
0x8B83 |
35779 | 3D纹理绑定 |
GL_TEXTURE_BINDING_CUBE_MAP |
0x8514 |
34068 | 立方体贴图绑定 |
GL_FRAMEBUFFER_BINDING |
0x8CA6 |
36006 | FBO绑定 |
GL_RENDERBUFFER_BINDING |
0x8CA7 |
36007 | RBO绑定 |
GL_PIXEL_STORE_PACK_ALIGNMENT |
0x0D05 |
3333 | 像素存储对齐 |
GL_PIXEL_STORE_UNPACK_ALIGNMENT |
0x0CF5 |
3317 | 像素解包对齐 |
GL_MAX_TEXTURE_SIZE |
0x8473 |
33907 | 最大纹理尺寸 |
GL_MAX_VIEWPORT_DIMS |
0x0BA3 |
2979 | 最大视口尺寸 |
1.3 浮点值查询 (glGetFloatv)
| 常量名 | 十六进制值 | 说明 |
|---|---|---|
GL_LINE_WIDTH |
0x0B22 |
线宽 |
GL_POINT_SIZE |
0x0B11 |
点大小 |
1.4 混合方程值 (BLEND_EQUATION 返回值)
| 常量名 | 十六进制值 | 说明 |
|---|---|---|
GL_FUNC_ADD |
0x8006 |
加法(默认) |
GL_FUNC_SUBTRACT |
0x8003 |
减法 |
GL_FUNC_REVERSE_SUBTRACT |
0x8004 |
反向减法 |
GL_MIN |
0x8007 |
最小值 |
GL_MAX |
0x8008 |
最大值 |
1.5 混合因子值 (BLEND_SRC/DST 返回值)
| 常量名 | 十六进制值 | 说明 |
|---|---|---|
GL_ZERO |
0x0000 |
0 |
GL_ONE |
0x0001 |
1 |
GL_SRC_COLOR |
0x0300 |
源颜色 |
GL_ONE_MINUS_SRC_COLOR |
0x0301 |
1-源颜色 |
GL_SRC_ALPHA |
0x0302 |
源Alpha |
GL_ONE_MINUS_SRC_ALPHA |
0x0303 |
1-源Alpha |
GL_DST_ALPHA |
0x0304 |
目标Alpha |
GL_ONE_MINUS_DST_ALPHA |
0x0305 |
1-目标Alpha |
GL_DST_COLOR |
0x0306 |
目标颜色 |
GL_ONE_MINUS_DST_COLOR |
0x0307 |
1-目标颜色 |
GL_SRC_ALPHA_SATURATE |
0x0308 |
源Alpha饱和 |
1.6 纹理相关
| 常量名 | 十六进制值 | 说明 |
|---|---|---|
GL_TEXTURE_2D |
0x0DE1 |
2D纹理目标 |
GL_TEXTURE_3D |
0x802F |
3D纹理目标 |
GL_TEXTURE_CUBE_MAP |
0x8513 |
立方体贴图目标 |
GL_TEXTURE0 |
0x84C0 |
纹理单元0 |
GL_TEXTURE1 |
0x84C1 |
纹理单元1 |
GL_TEXTURE2 |
0x84C2 |
纹理单元2 |
GL_TEXTURE31 |
0x84DF |
纹理单元31 |
GL_TEXTURE_MIN_FILTER |
0x2801 |
缩小过滤 |
GL_TEXTURE_MAG_FILTER |
0x2800 |
放大过滤 |
GL_TEXTURE_WRAP_S |
0x2802 |
S方向环绕 |
GL_TEXTURE_WRAP_T |
0x2803 |
T方向环绕 |
1.7 帧缓冲相关
| 常量名 | 十六进制值 | 说明 |
|---|---|---|
GL_FRAMEBUFFER |
0x8D40 |
帧缓冲目标 |
GL_RENDERBUFFER |
0x8D41 |
渲染缓冲目标 |
GL_COLOR_ATTACHMENT0 |
0x8CE0 |
颜色附件0 |
GL_DEPTH_ATTACHMENT |
0x8D00 |
深度附件 |
GL_STENCIL_ATTACHMENT |
0x8D20 |
模板附件 |
GL_DEPTH_STENCIL_ATTACHMENT |
0x821A |
深度模板附件 |
GL_FRAMEBUFFER_COMPLETE |
0x8CD5 |
FBO完整状态 |
2. LLDB 调试表达式(可直接复制运行)
2.1 语法规则
由于 LLDB 表达式求值器不识别 GL 头文件定义,所有 GL 函数调用必须使用强制类型转换:
cpp
// glGetIntegerv --- 必须用 (void(*)(unsigned int, int*)) 转换
((void(*)(unsigned int, int*))glGetIntegerv)(TOKEN, &VAR)
// glIsEnabled --- 必须用 (int(*)(unsigned int)) 转换
((int(*)(unsigned int))glIsEnabled)(TOKEN)
// glEnable / glDisable --- 必须用 (void(*)(unsigned int)) 转换
((void(*)(unsigned int))glEnable)(TOKEN)
((void(*)(unsigned int))glDisable)(TOKEN)
2.2 单状态查询(每条可独立复制,末尾变量名会自动显示值)
Scissor 启用状态
cpp
int en=((int(*)(unsigned int))glIsEnabled)(0x0C11); en
Scissor 矩形
cpp
GLint sc[4]={0}; ((void(*)(unsigned int, int*))glGetIntegerv)(0x0C12, sc); sc
Blend 启用状态
cpp
int en=((int(*)(unsigned int))glIsEnabled)(0x0BE2); en
Blend 方程 (RGB)
cpp
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8009, &v); v
Blend 方程 (Alpha)
cpp
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x883D, &v); v
Blend 源因子 (RGB)
cpp
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x80C9, &v); v
Blend 目标因子 (RGB)
cpp
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x80CA, &v); v
Blend 源因子 (Alpha)
cpp
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x80CB, &v); v
Blend 目标因子 (Alpha)
cpp
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x80CC, &v); v
Viewport
cpp
GLint vp[4]={0}; ((void(*)(unsigned int, int*))glGetIntegerv)(0x0BA2, vp); vp
Current Program
cpp
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8B8D, &v); v
VAO 绑定
cpp
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x85B5, &v); v
VBO 绑定
cpp
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8894, &v); v
IBO/EBO 绑定
cpp
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8895, &v); v
FBO 绑定
cpp
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8CA6, &v); v
活跃纹理单元
cpp
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x84C0, &v); v
2D 纹理绑定
cpp
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8B82, &v); v
3D 纹理绑定
cpp
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8B83, &v); v
Depth Test 启用状态
cpp
int en=((int(*)(unsigned int))glIsEnabled)(0x0B71); en
Stencil Test 启用状态
cpp
int en=((int(*)(unsigned int))glIsEnabled)(0x0B90); en
Cull Face 启用状态
cpp
int en=((int(*)(unsigned int))glIsEnabled)(0x0B44); en
2.3 一键全量状态快照(复制整个代码块运行,所有变量在最后列出可查看)
cpp
// === GL 状态全量快照 ===
GLint sc[4]={0}; ((void(*)(unsigned int, int*))glGetIntegerv)(0x0C12, sc); sc
GLint vp[4]={0}; ((void(*)(unsigned int, int*))glGetIntegerv)(0x0BA2, vp); vp
GLint beq=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8009, &beq); beq
GLint beqa=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x883D, &beqa); beqa
GLint srgb=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x80C9, &srgb); srgb
GLint drgb=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x80CA, &drgb); drgb
GLint salp=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x80CB, &salp); salp
GLint dalp=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x80CC, &dalp); dalp
GLint prog=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8B8D, &prog); prog
GLint vao=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x85B5, &vao); vao
GLint vbo=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8894, &vbo); vbo
GLint ibo=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8895, &ibo); ibo
GLint fbo=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8CA6, &fbo); fbo
GLint activeTex=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x84C0, &activeTex); activeTex
GLint tex2d=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8B82, &tex2d); tex2d
GLint tex3d=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8B83, &tex3d); tex3d
int scissorEn=((int(*)(unsigned int))glIsEnabled)(0x0C11); scissorEn
int blendEn=((int(*)(unsigned int))glIsEnabled)(0x0BE2); blendEn
int depthEn=((int(*)(unsigned int))glIsEnabled)(0x0B71); depthEn
int stencilEn=((int(*)(unsigned int))glIsEnabled)(0x0B90); stencilEn
int cullEn=((int(*)(unsigned int))glIsEnabled)(0x0B44); cullEn
2.4 一键修复(在调试器中直接执行,立即恢复 GL 状态)
cpp
// 禁用 Scissor + 恢复默认 Blend 状态
((void(*)(unsigned int))glDisable)(0x0C11);
((void(*)(unsigned int))glBlendEquation)(0x8006);
((void(*)(unsigned int, int*, int*, int*, int*))glBlendFuncSeparate)(0x0302, 0x0303, 0x0302, 0x0303);
((void(*)(unsigned int))glDisable)(0x0B90);
((void(*)(unsigned int))glDisable)(0x0B71);
// 之后重新执行 2.3 快照确认状态已恢复
2.5 常见错误与解决方案
| 错误信息 | 原因 | 解决方案 |
|---|---|---|
use of undeclared identifier 'GL_xxx' |
LLDB 无 GL 头文件 | 使用十六进制数值代替常量名 |
'glGetIntegerv' has unknown return type |
函数无声明 | 使用 ((void(*)(uint, int*))glGetIntegerv) 强制转换 |
unsupported expression with unknown type |
复杂类型推断失败 | 分步执行,每个查询单独一条语句 |
no member named 'glGetString' |
GLES2.0 无 glGetString | 使用 glGetIntegerv 查询具体值 |
3. 绘制调试流程
3.1 绘制管线关键路径
3.2 断点位置推荐
3.3 GL 状态差异
4. 绘制失败根因分析
4.1 已确认的根因
4.2 根因链路
4.3 关键代码路径分析
cpp
6. 调试实战 Checklist
6.1 启动调试前
- 确认设备已连接,USB 调试已开启
- 确认使用
corretto-17JDK - 确认 Gradle 同步成功
- 确认
paint_oplus模块编译通过
6.2 复现问题时
- 在
BaseBrushMaterial::DrawInstanced入口设断点 - 切换笔形(圆珠笔 → 铅笔)触发问题
- 断点命中后,执行第 2.3 节的全量状态快照
- 对比正常状态和异常状态的差异
- 记录所有 GL 状态值
6.3 分析差异时
- 对比
scissor启用状态和矩形值 - 对比
blend equation(RGB 和 Alpha 分离) - 对比
blend func源/目标因子 - 检查
activeTexture和纹理绑定 - 检查
VAO/VBO/FBO绑定 - 检查
programID 是否正确
6.4 修复验证时
- 修复后多次切换笔形测试
- 连续绘制 50+ 次测试稳定性
- 测试不同粗细、不同颜色
- 测试快速连续切换场景
- 测试旋转/缩放后绘制
7. GL 状态完整性检查表
每次绘制调用前后应确保以下状态一致:
| 状态项 | 绘制前期望值 | 绘制后期望值 | 检查方法 |
|---|---|---|---|
GL_BLEND |
按绘制类型决定 | 与绘制前一致 | glIsEnabled(0x0BE2) |
GL_SCISSOR_TEST |
DISABLED (或按需启用) | DISABLED | glIsEnabled(0x0C11) |
GL_BLEND_EQUATION |
GL_FUNC_ADD (0x8006) |
GL_FUNC_ADD (0x8006) |
glGetIntegerv(0x8009) |
GL_BLEND_EQUATION_ALPHA |
GL_FUNC_ADD (0x8006) |
GL_FUNC_ADD (0x8006) |
glGetIntegerv(0x883D) |
GL_BLEND_SRC_RGB |
GL_SRC_ALPHA (0x0302) |
GL_SRC_ALPHA (0x0302) |
glGetIntegerv(0x80C9) |
GL_BLEND_DST_RGB |
GL_ONE_MINUS_SRC_ALPHA (0x0303) |
GL_ONE_MINUS_SRC_ALPHA (0x0303) |
glGetIntegerv(0x80CA) |
GL_FRAMEBUFFER_BINDING |
目标 FBO ID | 与绘制前一致 | glGetIntegerv(0x8CA6) |
GL_CURRENT_PROGRAM |
对应 shader program | 0 (解绑) | glGetIntegerv(0x8B8D) |
GL_ACTIVE_TEXTURE |
GL_TEXTURE0 (0x84C0) |
GL_TEXTURE0 (0x84C0) |
glGetIntegerv(0x84C0) |
附录 A: GLES2.0 常见 Token 值完整列表
启用/禁用能力 (Enable/Disable)
GL_BLEND 0x0BE2
GL_SCISSOR_TEST 0x0C11
GL_DEPTH_TEST 0x0B71
GL_STENCIL_TEST 0x0B90
GL_CULL_FACE 0x0B44
GL_DITHER 0x0BD0
GL_POLYGON_OFFSET_FILL 0x8037
GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E
GL_SAMPLE_COVERAGE 0x80A0
GL_TEXTURE_2D 0x0DE1
GL_TEXTURE_3D 0x802F
GL_TEXTURE_CUBE_MAP 0x8513
GL_MULTISAMPLE 0x8429
GL_RASTERIZER_DISCARD 0x8C89
混合方程
GL_FUNC_ADD 0x8006 ← 默认
GL_FUNC_SUBTRACT 0x8003
GL_FUNC_REVERSE_SUBTRACT 0x8004
GL_MIN 0x8007
GL_MAX 0x8008
混合因子
GL_ZERO 0x0000
GL_ONE 0x0001
GL_SRC_COLOR 0x0300
GL_ONE_MINUS_SRC_COLOR 0x0301
GL_SRC_ALPHA 0x0302 ← 默认源
GL_ONE_MINUS_SRC_ALPHA 0x0303 ← 默认目标
GL_DST_ALPHA 0x0304
GL_ONE_MINUS_DST_ALPHA 0x0305
GL_DST_COLOR 0x0306
GL_ONE_MINUS_DST_COLOR 0x0307
GL_SRC_ALPHA_SATURATE 0x0308
GL_CONSTANT_COLOR 0x8001
GL_ONE_MINUS_CONSTANT_COLOR 0x8002
GL_CONSTANT_ALPHA 0x8003
GL_ONE_MINUS_CONSTANT_ALPHA 0x8004
目标
GL_TEXTURE_2D 0x0DE1
GL_TEXTURE_3D 0x802F
GL_TEXTURE_CUBE_MAP 0x8513
GL_FRAMEBUFFER 0x8D40
GL_READ_FRAMEBUFFER 0x8CA8
GL_DRAW_FRAMEBUFFER 0x8CA9
GL_RENDERBUFFER 0x8D41
GL_ARRAY_BUFFER 0x8892
GL_ELEMENT_ARRAY_BUFFER 0x8893
GL_PIXEL_PACK_BUFFER 0x88EB
GL_PIXEL_UNPACK_BUFFER 0x88EC
GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E
GL_UNIFORM_BUFFER 0x8A11
查询值
GL_VIEWPORT 0x0BA2
GL_SCISSOR_BOX 0x0C12
GL_COLOR_CLEAR_VALUE 0x0C22
GL_DEPTH_CLEAR_VALUE 0x0B73
GL_STENCIL_CLEAR_VALUE 0x0B91
GL_LINE_WIDTH 0x0B22
GL_POINT_SIZE 0x0B11
GL_MAX_VERTEX_ATTRIBS 0x8869
GL_MAX_VERTEX_UNIFORM_VECTORS 0x886A
GL_MAX_VARYING_VECTORS 0x886F
GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8870
GL_MAX_TEXTURE_SIZE 0x8473
GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C
GL_MAX_RENDERBUFFER_SIZE 0x84E8
GL_MAX_TEXTURE_IMAGE_UNITS 0x8872
GL_MAX_DRAW_BUFFERS 0x8824
GL_MAX_COLOR_ATTACHMENTS 0x8CDF
GL_MAX_VIEWPORT_DIMS 0x0BA3
GL_MAX_PIXEL_WIDTH 0x0D0B
GL_MAX_PIXEL_HEIGHT 0x0D0C
GL_MIN_PROGRAM_TEXEL_OFFSET 0x8804
GL_MAX_PROGRAM_TEXEL_OFFSET 0x8805
附录 B: 快速调试命令(可直接复制)
检查 Scissor 状态
cpp
int en=((int(*)(unsigned int))glIsEnabled)(0x0C11); en
GLint sc[4]={0}; ((void(*)(unsigned int, int*))glGetIntegerv)(0x0C12, sc); sc
检查 Blend 状态
cpp
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8009, &v); v
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x883D, &v); v
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x80C9, &v); v
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x80CA, &v); v
一键修复(立即恢复 GL 状态)
cpp
((void(*)(unsigned int))glDisable)(0x0C11);
((void(*)(unsigned int))glBlendEquation)(0x8006);
((void(*)(unsigned int, int*, int*, int*, int*))glBlendFuncSeparate)(0x0302, 0x0303, 0x0302, 0x0303);
8. glGetError 错误码查询
8.1 基本用法
glGetError() 是最基础的 OpenGL 错误检查方式,在每次 GL 调用后立即查询是否有错误发生。
cpp
// 单次查询
unsigned int err=((unsigned int(*)(void))glGetError)(); err
// 返回值对应:
// 0x0000 = GL_NO_ERROR (无错误)
// 0x0500 = GL_INVALID_ENUM
// 0x0501 = GL_INVALID_VALUE
// 0x0502 = GL_INVALID_OPERATION
// 0x0503 = GL_STACK_OVERFLOW
// 0x0504 = GL_STACK_UNDERFLOW
// 0x0505 = GL_OUT_OF_MEMORY
8.2 一键查询所有错误(循环直到 GL_NO_ERROR)
cpp
// 查询并清空所有错误
unsigned int err;
do { err=((unsigned int(*)(void))glGetError)(); } while(err!=0); err
8.3 错误码对照表
| 十六进制值 | 常量名 | 含义 | 常见原因 |
|---|---|---|---|
0x0000 |
GL_NO_ERROR |
无错误 | 正常 |
0x0500 |
GL_INVALID_ENUM |
枚举参数非法 | 传了未定义的 GL 枚举值 |
0x0501 |
GL_INVALID_VALUE |
值参数非法 | 数值超出允许范围 |
0x0502 |
GL_INVALID_OPERATION |
操作非法 | 当前状态下该操作不允许 |
0x0503 |
GL_STACK_OVERFLOW |
栈溢出 | 矩阵栈溢出(GLES1.0) |
0x0504 |
GL_STACK_UNDERFLOW |
栈下溢 | 矩阵栈下溢(GLES1.0) |
0x0505 |
GL_OUT_OF_MEMORY |
内存不足 | GPU 内存分配失败 |
0x0506 |
GL_INVALID_FRAMEBUFFER_OPERATION |
FBO 操作非法 | FBO 不完整或配置错误 |
0x8031 |
GL_TABLE_TOO_LARGE |
表过大 | (GLES1.0) |
8.4 在代码中添加错误检查(推荐)
cpp
// 宏定义 --- 在关键路径使用
#define GL_CHECK_ERROR(msg) \
do { \
GLenum err = glGetError(); \
if (err != GL_NO_ERROR) { \
LOGE("[GL_ERROR] %s: error=0x%04X", msg, err); \
} \
} while(0)
// 使用示例
glDrawArrays(GL_TRIANGLES, 0, vertexCount);
GL_CHECK_ERROR("glDrawArrays failed");
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, count);
GL_CHECK_ERROR("glDrawArraysInstanced failed");
8.5 在 LLDB 中查询
cpp
unsigned int err=((unsigned int(*)(void))glGetError)(); err
9. 帧缓冲完整性检查
9.1 FBO 完整性
当使用自定义 FBO 渲染时,必须检查 FBO 是否完整。
cpp
// LLDB 表达式 --- 检查 FBO 完整性
int v=((int(*)(unsigned int))glCheckFramebufferStatus)(0x8D40); v
// 返回值:
// 0x8CD5 = GL_FRAMEBUFFER_COMPLETE (完整,可以渲染)
// 0x8CD6 = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
// 0x8CD7 = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
// 0x8CD9 = GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
// 0x8CDA = GL_FRAMEBUFFER_INCOMPLETE_FORMATS
// 0x8CDB = GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER
// 0x8CDC = GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER
// 0x8CDD = GL_FRAMEBUFFER_UNSUPPORTED
// 0x8CF0 = GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE
9.2 FBO 完整性码对照表
| 十六进制值 | 常量名 | 含义 |
|---|---|---|
0x8CD5 |
GL_FRAMEBUFFER_COMPLETE |
完整 |
0x8CD6 |
GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT |
附件不完整 |
0x8CD7 |
GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT |
缺少附件 |
0x8CD9 |
GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS |
尺寸不匹配 |
0x8CDA |
GL_FRAMEBUFFER_INCOMPLETE_FORMATS |
格式不支持 |
0x8CDB |
GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER |
绘制缓冲区不完整 |
0x8CDC |
GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER |
读取缓冲区不完整 |
0x8CDD |
GL_FRAMEBUFFER_UNSUPPORTED |
不支持的配置 |
0x8CF0 |
GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE |
多重采样不匹配 |
9.3 在代码中检查
cpp
bool CheckFramebufferStatus() {
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
const char* reason = "unknown";
switch (status) {
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: reason = "attachment incomplete"; break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: reason = "missing attachment"; break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: reason = "dimensions mismatch"; break;
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS: reason = "format unsupported"; break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: reason = "draw buffer incomplete"; break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: reason = "read buffer incomplete"; break;
case GL_FRAMEBUFFER_UNSUPPORTED: reason = "configuration unsupported"; break;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: reason = "multisample mismatch"; break;
}
LOGE("FBO incomplete: %s (0x%04X)", reason, status);
return false;
}
return true;
}
10. 着色器调试
10.1 检查着色器编译状态
cpp
// LLDB 表达式 --- 检查着色器是否编译成功
// 参数: shader ID (从 glCreateShader 获取)
GLint v=0; ((int(*)(unsigned int, unsigned int, int*))glGetShaderiv)(SHADER_ID, 0x8B81, &v); v
// 0x8B81 = GL_COMPILE_STATUS
// 返回 1 = 编译成功, 0 = 编译失败
10.2 检查程序链接状态
cpp
// LLDB 表达式 --- 检查程序是否链接成功
GLint v=0; ((int(*)(unsigned int, unsigned int, int*))glGetProgramiv)(PROGRAM_ID, 0x8B82, &v); v
// 0x8B82 = GL_LINK_STATUS
// 返回 1 = 链接成功, 0 = 链接失败
10.3 获取编译/链接日志
cpp
// 获取着色器编译日志
char log[512]={0};
((void(*)(unsigned int, int, int, char*))glGetShaderInfoLog)(SHADER_ID, 512, nullptr, log);
// log 变量即为错误日志
// 获取程序链接日志
((void(*)(unsigned int, int, int, char*))glGetProgramInfoLog)(PROGRAM_ID, 512, nullptr, log);
10.4 着色器相关 Token
| 常量名 | 十六进制值 | 说明 |
|---|---|---|
GL_FRAGMENT_SHADER |
0x8B30 |
片段着色器 |
GL_VERTEX_SHADER |
0x8B31 |
顶点着色器 |
GL_COMPUTE_SHADER |
0x91B9 |
计算着色器 |
GL_GEOMETRY_SHADER |
0x8DD9 |
几何着色器 |
GL_TESS_CONTROL_SHADER |
0x8E88 |
曲面细分控制着色器 |
GL_TESS_EVALUATION_SHADER |
0x8E87 |
曲面细分评估着色器 |
GL_COMPILE_STATUS |
0x8B81 |
编译状态 |
GL_LINK_STATUS |
0x8B82 |
链接状态 |
GL_INFO_LOG_LENGTH |
0x8B84 |
日志长度 |
GL_SHADER_SOURCE_LENGTH |
0x8B88 |
着色器源长度 |
GL_SHADER_TYPE |
0x8B8F |
着色器类型 |
GL_ACTIVE_UNIFORMS |
0x8B86 |
活跃 uniform 数量 |
GL_ACTIVE_ATTRIBUTES |
0x8B85 |
活跃属性数量 |
11. 纹理与缓冲区验证
11.1 纹理有效性检查
cpp
// LLDB --- 检查纹理 ID 是否有效
int v=((int(*)(unsigned int))glIsTexture)(TEXTURE_ID); v
// 返回 1=有效, 0=无效
// LLDB --- 检查缓冲区 ID 是否有效
int v=((int(*)(unsigned int))glIsBuffer)(BUFFER_ID); v
// 返回 1=有效, 0=无效
// LLDB --- 检查程序 ID 是否有效
int v=((int(*)(unsigned int))glIsProgram)(PROGRAM_ID); v
// 返回 1=有效, 0=无效
// LLDB --- 检查 FBO ID 是否有效
int v=((int(*)(unsigned int))glIsFramebuffer)(FBO_ID); v
// 返回 1=有效, 0=无效
// LLDB --- 检查 RBO ID 是否有效
int v=((int(*)(unsigned int))glIsRenderbuffer)(RBO_ID); v
// 返回 1=有效, 0=无效
11.2 获取纹理参数
cpp
// 获取纹理宽度
GLint v=0; ((void(*)(unsigned int, int, int*))glGetTexParameteriv)(0x0DE1, 0x2807, &v); v
// 0x0DE1=GL_TEXTURE_2D, 0x2807=GL_TEXTURE_WIDTH
// 获取纹理高度
GLint v=0; ((void(*)(unsigned int, int, int*))glGetTexParameteriv)(0x0DE1, 0x2808, &v); v
// 0x2808=GL_TEXTURE_HEIGHT
// 获取纹理内部格式
GLint v=0; ((void(*)(unsigned int, int, int*))glGetTexParameteriv)(0x0DE1, 0x8015, &v); v
// 0x8015=GL_TEXTURE_INTERNAL_FORMAT
// 获取最小过滤模式
GLint v=0; ((void(*)(unsigned int, int, int*))glGetTexParameteriv)(0x0DE1, 0x2801, &v); v
// 0x2801=GL_TEXTURE_MIN_FILTER
// 获取放大过滤模式
GLint v=0; ((void(*)(unsigned int, int, int*))glGetTexParameteriv)(0x0DE1, 0x2800, &v); v
// 0x2800=GL_TEXTURE_MAG_FILTER
// 获取 S/T 环绕模式
GLint v=0; ((void(*)(unsigned int, int, int*))glGetTexParameteriv)(0x0DE1, 0x2802, &v); v
GLint v=0; ((void(*)(unsigned int, int, int*))glGetTexParameteriv)(0x0DE1, 0x2803, &v); v
// 0x2802=GL_TEXTURE_WRAP_S, 0x2803=GL_TEXTURE_WRAP_T
11.3 获取缓冲区参数
cpp
// 获取缓冲区大小
GLint v=0; ((void(*)(unsigned int, int*))glGetBufferParameteriv)(0x8892, 0x87B4, &v); v
// 0x8892=GL_ARRAY_BUFFER, 0x87B4=GL_BUFFER_SIZE
// 获取缓冲区用法
GLint v=0; ((void(*)(unsigned int, int*))glGetBufferParameteriv)(0x8892, 0x87B6, &v); v
// 0x87B6=GL_BUFFER_USAGE
11.4 纹理参数 Token
| 常量名 | 十六进制值 | 说明 |
|---|---|---|
GL_TEXTURE_WIDTH |
0x2807 |
纹理宽度 |
GL_TEXTURE_HEIGHT |
0x2808 |
纹理高度 |
GL_TEXTURE_DEPTH |
0x8071 |
纹理深度(3D) |
GL_TEXTURE_INTERNAL_FORMAT |
0x8015 |
内部格式 |
GL_TEXTURE_RED_SIZE |
0x805C |
R通道大小 |
GL_TEXTURE_GREEN_SIZE |
0x805D |
G通道大小 |
GL_TEXTURE_BLUE_SIZE |
0x805E |
B通道大小 |
GL_TEXTURE_ALPHA_SIZE |
0x805F |
A通道大小 |
GL_TEXTURE_DEPTH_SIZE |
0x8051 |
深度大小 |
GL_TEXTURE_COMPRESSED |
0x86A1 |
是否压缩 |
GL_TEXTURE_MIN_FILTER |
0x2801 |
缩小过滤 |
GL_TEXTURE_MAG_FILTER |
0x2800 |
放大过滤 |
GL_TEXTURE_WRAP_S |
0x2802 |
S环绕 |
GL_TEXTURE_WRAP_T |
0x2803 |
T环绕 |
GL_TEXTURE_WRAP_R |
0x8072 |
R环绕(3D) |
GL_TEXTURE_MAX_ANISOTROPY |
0x84FE |
最大各向异性 |
11.5 缓冲区参数 Token
| 常量名 | 十六进制值 | 说明 |
|---|---|---|
GL_BUFFER_SIZE |
0x87B4 |
缓冲区大小 |
GL_BUFFER_USAGE |
0x87B6 |
缓冲区用法 |
GL_BUFFER_ACCESS |
0x88BB |
访问模式 |
GL_BUFFER_MAPPED |
0x88BC |
是否映射 |
12. 帧缓冲内容读取与验证
12.1 ReadPixels --- 读取帧缓冲像素
用于验证渲染结果是否正确输出到屏幕/离屏缓冲。
cpp
// 在代码中读取 FBO 内容
void DumpFramebuffer(const std::string& filename, int x, int y, int w, int h) {
std::vector<unsigned char> pixels(w * h * 4);
glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
// 可选: 保存为原始数据文件
// 或检查像素值
for (int i = 0; i < pixels.size(); i += 4) {
if (pixels[i] > 0) {
LOGD("Found non-zero pixel at offset %d: RGBA=(%d,%d,%d,%d)",
i/4, pixels[i], pixels[i+1], pixels[i+2], pixels[i+3]);
break;
}
}
}
12.2 ReadPixels Token
| 常量名 | 十六进制值 | 说明 |
|---|---|---|
GL_COLOR_BUFFER_BIT |
0x00004000 |
颜色缓冲区位 |
GL_DEPTH_BUFFER_BIT |
0x00000100 |
深度缓冲区位 |
GL_STENCIL_BUFFER_BIT |
0x00000400 |
模板缓冲区位 |
GL_RGBA |
0x1908 |
RGBA 格式 |
GL_RGB |
0x1907 |
RGB 格式 |
GL_ALPHA |
0x1906 |
Alpha 格式 |
GL_LUMINANCE |
0x1909 |
亮度格式 |
GL_LUMINANCE_ALPHA |
0x190A |
亮度Alpha格式 |
GL_UNSIGNED_BYTE |
0x1401 |
无符号字节类型 |
GL_UNSIGNED_INT |
0x1405 |
无符号整型 |
GL_FLOAT |
0x1406 |
浮点类型 |
12.3 Clear 相关
cpp
// 查询清除颜色
GLfloat clearColor[4]={0}; ((void(*)(unsigned int, float*))glGetFloatv)(0x0C22, clearColor); clearColor
// 0x0C22=GL_COLOR_CLEAR_VALUE
// 查询清除深度
GLfloat clearDepth=0; ((void(*)(unsigned int, float*))glGetFloatv)(0x0B73, &clearDepth); clearDepth
// 0x0B73=GL_DEPTH_CLEAR_VALUE
// 查询清除模板
GLint clearStencil=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x0B91, &clearStencil); clearStencil
// 0x0B91=GL_STENCIL_CLEAR_VALUE
13. 代码级 GL 状态快照
13.1 完整 GL 状态转储函数
将以下代码添加到项目中,在需要的位置调用即可输出所有 GL 状态到 Logcat:
cpp
#define TAG "GL_STATE"
void DumpGLState() {
// === 启用状态 ===
int blendEn = glIsEnabled(GL_BLEND);
int scissorEn = glIsEnabled(GL_SCISSOR_TEST);
int depthEn = glIsEnabled(GL_DEPTH_TEST);
int stencilEn = glIsEnabled(GL_STENCIL_TEST);
int cullEn = glIsEnabled(GL_CULL_FACE);
LOGD("Enabled: blend=%d scissor=%d depth=%d stencil=%d cull=%d",
blendEn, scissorEn, depthEn, stencilEn, cullEn);
// === 视口和裁剪 ===
GLint vp[4]; glGetIntegerv(GL_VIEWPORT, vp);
GLint sc[4]; glGetIntegerv(GL_SCISSOR_BOX, sc);
LOGD("Viewport: [%d,%d,%d,%d] Scissor: [%d,%d,%d,%d]",
vp[0], vp[1], vp[2], vp[3], sc[0], sc[1], sc[2], sc[3]);
// === 混合状态 ===
GLint beq, beqa, srgb, drgb, salp, dalp;
glGetIntegerv(GL_BLEND_EQUATION, &beq);
glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &beqa);
glGetIntegerv(GL_BLEND_SRC_RGB, &srgb);
glGetIntegerv(GL_BLEND_DST_RGB, &drgb);
glGetIntegerv(GL_BLEND_SRC_ALPHA, &salp);
glGetIntegerv(GL_BLEND_DST_ALPHA, &dalp);
LOGD("Blend: eq=0x%x eqA=0x%x srcRGB=0x%x dstRGB=0x%x srcA=0x%x dstA=0x%x",
beq, beqa, srgb, drgb, salp, dalp);
// === 绑定状态 ===
GLint prog, vao, vbo, ibo, fbo, activeTex, tex2d, tex3d;
glGetIntegerv(GL_CURRENT_PROGRAM, &prog);
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &vao);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vbo);
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &ibo);
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex);
glGetIntegerv(GL_TEXTURE_BINDING_2D, &tex2d);
glGetIntegerv(GL_TEXTURE_BINDING_3D, &tex3d);
LOGD("Bindings: prog=%d vao=%d vbo=%d ibo=%d fbo=%d activeTex=0x%x tex2d=%d tex3d=%d",
prog, vao, vbo, ibo, fbo, activeTex, tex2d, tex3d);
// === 线宽和点大小 ===
GLfloat lineWidth, pointSize;
glGetFloatv(GL_LINE_WIDTH, &lineWidth);
glGetFloatv(GL_POINT_SIZE, &pointSize);
LOGD("LineWidth=%.2f PointSize=%.2f", lineWidth, pointSize);
// === 活动属性 ===
GLint maxAttribs;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttribs);
LOGD("MaxVertexAttribs=%d", maxAttribs);
// 哪些属性被启用
for (int i = 0; i < maxAttribs; i++) {
if (glIsVertexAttribEnabledEXT(i)) {
LOGD(" Vertex attrib %d: ENABLED", i);
}
}
// === FBO 完整性 ===
if (fbo != 0) {
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
LOGE("FBO %d incomplete: 0x%04X", fbo, status);
}
}
}
13.2 在 DrawInstanced 入口添加断点日志
cpp
// 在 BaseBrushMaterial::DrawInstanced 入口处添加
void BaseBrushMaterial::DrawInstanced(...) {
LOGD("=== DrawInstanced: count=%zu ===", count);
DumpGLState(); // 调用上面的状态转储函数
// ... 原有逻辑 ...
}
14. GPU 调试工具
14.1 RenderDoc
适用场景: PC 平台上的 OpenGL/Vulkan 应用
使用步骤:
- 下载 RenderDoc
- 运行目标应用并在 RenderDoc 中捕获帧
- 查看每一帧的 GL 调用、资源、状态
优点:
- 完整的帧捕获和回放
- 可视化检查纹理、FBO、UBO 等所有 GPU 资源
- 时间线视图分析绘制命令
- 支持 Shader 编辑和热重载
缺点:
- 不直接支持 Android(需配合 NDK 远程调试)
- 需要对应用进行一定修改以支持捕获
14.2 Android GPU Inspector (AGI)
适用场景: Android 平台 GPU 调试
使用步骤:
- 在 Android Studio 中打开 GPU Inspector
- 连接设备并启动应用
- 选择进程,开始录制
- 查看 GPU 活动、帧时间、渲染管线
优点:
- Android 原生支持
- 可查看 GPU 频率、温度、负载
- 分析渲染性能瓶颈
缺点:
- 需要特定设备和 API 支持
- 部分功能需要开发者选项
14.3 Snapdragon Profiler
适用场景: 高通骁龙芯片的 Android 设备
使用步骤:
- 下载 Snapdragon Profiler
- 连接骁龙设备
- 启动分析,查看 OpenGL/Vulkan 调用
优点:
- 深度 GPU 性能分析
- 着色器耗时分析
- 纹理和缓冲区使用情况
缺点:
- 仅限骁龙设备
- 安装配置较复杂
14.4 Perfetto (Android)
适用场景: Android 系统级性能追踪
使用步骤:
adb shell perfetto -c config.pbtxt -o trace.perfetto-trace- 在 ui.perfetto.dev 打开 trace 文件
- 查看 SurfaceFlinger、HWUI、GL 等时序
优点:
- 系统级时序分析
- 可查看帧合成管线
- 分析掉帧原因
14.5 adb 相关调试命令
bash
# 查看 SurfaceFlinger 合成层
adb shell dumpsys SurfaceFlinger --latency
# 查看 GPU 信息(高通)
adb shell dumpsys gpu
# 查看 GPU 频率
adb shell cat /sys/class/kgsl/kgsl-3d0/gpuclk
# 查看 dumpsys 中的 GL 信息
adb shell dumpsys WindowManager | grep -i gl
15. GL 调试扩展(如果可用)
15.1 KHR_debug(OpenGL 4.3+ / ES 3.2+)
如果设备支持 KHR_debug 扩展,可以注册调试回调函数:
cpp
// 检查扩展支持
if (glfwExtensionSupported("KHR_debug") || GLEW_KHR_debug) {
// 设置调试回调
glDebugMessageCallback(DebugCallback, nullptr);
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); // 同步模式,便于断点
}
// 调试回调函数
void GLAPIENTRY DebugCallback(GLenum source, GLenum type, GLuint id,
GLenum severity, GLsizei length,
const GLchar* message, const void* userParam) {
if (severity == GL_DEBUG_SEVERITY_HIGH) {
LOGE("[GL Debug HIGH] %s", message);
} else if (severity == GL_DEBUG_SEVERITY_MEDIUM) {
LOGW("[GL Debug MED] %s", message);
} else {
LOGD("[GL Debug LOW] %s", message);
}
// 可在调试器中设置条件断点
}
15.2 调试扩展 Token
| 常量名 | 十六进制值 | 说明 |
|---|---|---|
GL_DEBUG_OUTPUT |
0x92E0 |
调试输出启用 |
GL_DEBUG_OUTPUT_SYNCHRONOUS |
0x8242 |
同步调试输出 |
GL_DEBUG_SEVERITY_HIGH |
0x9148 |
高严重级别 |
GL_DEBUG_SEVERITY_MEDIUM |
0x9149 |
中严重级别 |
GL_DEBUG_SEVERITY_LOW |
0x914A |
低严重级别 |
GL_DEBUG_SEVERITY_NOTIFICATION |
0x826B |
通知级别 |
GL_DEBUG_SOURCE_API |
0x8246 |
API 源 |
GL_DEBUG_SOURCE_WINDOW_SYSTEM |
0x8247 |
窗口系统源 |
GL_DEBUG_SOURCE_SHADER_COMPILER |
0x8248 |
着色器编译器源 |
GL_DEBUG_SOURCE_THIRD_PARTY |
0x8249 |
第三方源 |
GL_DEBUG_SOURCE_APPLICATION |
0x824A |
应用源 |
GL_DEBUG_SOURCE_OTHER |
0x824B |
其他源 |
15.3 其他有用扩展
| 扩展名 | 说明 | 检测方法 |
|---|---|---|
GL_KHR_debug |
调试回调 | glfwExtensionSupported("KHR_debug") |
GL_EXT_debug_label |
资源标签 | glfwExtensionSupported("EXT_debug_label") |
GL_EXT_marker |
帧标记 | glfwExtensionSupported("EXT_marker") |
GL_AMD_performance_monitor |
性能监测 | 高通/AMD 设备 |
GL_NV_performance_monitor |
性能监测 | NVIDIA 桌面 |
16. 端到端调试策略
16.1 分层调试法
┌─────────────────────────────────────────────┐
│ Layer 1: 应用层 │
│ - 检查传入的笔形、颜色、大小参数是否正确 │
│ - 检查触摸事件坐标转换 │
├─────────────────────────────────────────────┤
│ Layer 2: 绘制逻辑层 │
│ - 检查 DrawStrokesInternal 分发逻辑 │
│ - 检查 MARKER/其他笔形路径是否正确切换 │
├─────────────────────────────────────────────┤
│ Layer 3: GL 状态层 │
│ - BeginDrawPass / EndDrawPass 状态完整性 │
│ - Blend / Scissor / FBO 状态一致性 │
│ - 纹理绑定和属性数组状态 │
├─────────────────────────────────────────────┤
│ Layer 4: GPU 执行层 │
│ - 着色器编译/链接状态 │
│ - VBO/IBO 数据有效性 │
│ - FBO 完整性检查 │
│ - glGetError 检查 │
└─────────────────────────────────────────────┘
16.2 快速诊断流程
当遇到"画不出来"问题时,按以下顺序排查:
Step 1: 检查 GL Error
cpp
unsigned int err=((unsigned int(*)(void))glGetError)(); err
// 应该是 0x0000
Step 2: 检查 FBO 完整性
cpp
int v=((int(*)(unsigned int))glCheckFramebufferStatus)(0x8D40); v
// 应该是 0x8CD5
Step 3: 检查 Scissor 状态
cpp
int en=((int(*)(unsigned int))glIsEnabled)(0x0C11); en
GLint sc[4]={0}; ((void(*)(unsigned int, int*))glGetIntegerv)(0x0C12, sc); sc
// 如果 scissorEn=1 且 sc=[0,0,0,0] → 空裁剪!
Step 4: 检查 Blend 状态
cpp
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8009, &v); v
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x883D, &v); v
// 应该都是 0x8006 (GL_FUNC_ADD)
Step 5: 检查 Program 和 VBO
cpp
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8B8D, &v); v // program
GLint v=0; ((void(*)(unsigned int, int*))glGetIntegerv)(0x8894, &v); v // vbo
// 都应该是非零值
Step 6: 检查 Viewport
cpp
GLint vp[4]={0}; ((void(*)(unsigned int, int*))glGetIntegerv)(0x0BA2, vp); vp
// 宽高应该非零
16.3 典型故障模式
| 症状 | 可能原因 | 排查步骤 |
|---|---|---|
| 完全不渲染 | FBO 不完整 / Shader 无效 | 检查 Step 1, 2 |
| 画面全黑 | Blend 因子错误 / Shader 编译失败 | 检查 Step 4 + 编译日志 |
| 部分区域不显示 | Scissor 裁剪 / Viewport 错误 | 检查 Step 3, 6 |
| 颜色异常 | Blend Equation 被污染 | 检查 Step 4 |
| 笔触断断续续 | VBO 数据不完整 | 检查 Step 5 + 缓冲区大小 |
| 切换笔形后失效 | GL 状态残留污染 | 全量快照对比 |
| 缩放后模糊 | displayRect 映射错误 | 检查 NDC 坐标转换 |
| 叠加颜色变暗 | 预乘 Alpha 计算错误 | 检查 colorBlendLighten |