OpenGL ES调试汇总

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-17 JDK
  • 确认 Gradle 同步成功
  • 确认 paint_oplus 模块编译通过

6.2 复现问题时

  • BaseBrushMaterial::DrawInstanced 入口设断点
  • 切换笔形(圆珠笔 → 铅笔)触发问题
  • 断点命中后,执行第 2.3 节的全量状态快照
  • 对比正常状态和异常状态的差异
  • 记录所有 GL 状态值

6.3 分析差异时

  • 对比 scissor 启用状态和矩形值
  • 对比 blend equation (RGB 和 Alpha 分离)
  • 对比 blend func 源/目标因子
  • 检查 activeTexture 和纹理绑定
  • 检查 VAO/VBO/FBO 绑定
  • 检查 program ID 是否正确

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 应用

使用步骤:

  1. 下载 RenderDoc
  2. 运行目标应用并在 RenderDoc 中捕获帧
  3. 查看每一帧的 GL 调用、资源、状态

优点:

  • 完整的帧捕获和回放
  • 可视化检查纹理、FBO、UBO 等所有 GPU 资源
  • 时间线视图分析绘制命令
  • 支持 Shader 编辑和热重载

缺点:

  • 不直接支持 Android(需配合 NDK 远程调试)
  • 需要对应用进行一定修改以支持捕获

14.2 Android GPU Inspector (AGI)

适用场景: Android 平台 GPU 调试

使用步骤:

  1. 在 Android Studio 中打开 GPU Inspector
  2. 连接设备并启动应用
  3. 选择进程,开始录制
  4. 查看 GPU 活动、帧时间、渲染管线

优点:

  • Android 原生支持
  • 可查看 GPU 频率、温度、负载
  • 分析渲染性能瓶颈

缺点:

  • 需要特定设备和 API 支持
  • 部分功能需要开发者选项

14.3 Snapdragon Profiler

适用场景: 高通骁龙芯片的 Android 设备

使用步骤:

  1. 下载 Snapdragon Profiler
  2. 连接骁龙设备
  3. 启动分析,查看 OpenGL/Vulkan 调用

优点:

  • 深度 GPU 性能分析
  • 着色器耗时分析
  • 纹理和缓冲区使用情况

缺点:

  • 仅限骁龙设备
  • 安装配置较复杂

14.4 Perfetto (Android)

适用场景: Android 系统级性能追踪

使用步骤:

  1. adb shell perfetto -c config.pbtxt -o trace.perfetto-trace
  2. 在 ui.perfetto.dev 打开 trace 文件
  3. 查看 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
相关推荐
郝学胜-神的一滴4 天前
Horse3D 游戏引擎研发笔记(七):Clydesdale——从流式日志到多输出订阅
c++·qt·unity·游戏引擎·图形渲染·unreal engine·opengl
杨丰玮4184 天前
从零手写Java飞机躲障碍游戏|Swing绘图、鼠标跟随、计时器碰撞检测实战(五)
java·python·游戏·游戏引擎·图形渲染·动画·贴图
郝学胜-神的一滴7 天前
[简化版 GAMES 104] 现代游戏引擎 06:从Tick时序到邮局模型,拆解确定性世界的底层密码
开发语言·c++·游戏引擎·图形渲染·软件开发·opengl
烧酒同学7 天前
【C++】记录size of std::vector的巧妙坑
开发语言·c++·图形渲染
玖釉-7 天前
nvpro_core2 源码与架构解析:NVIDIA Vulkan 图形开发基础框架
c++·windows·图形渲染
小小数媒成员11 天前
合并阶段和计算着色器【图文解释】
着色器
小小数媒成员12 天前
顶点,几何,片元着色器+曲面细分阶段【图文解析】
着色器
SNAKEpc1213812 天前
OpenGL(十六)- 着色器统一值
c语言·c++·线性代数·算法·矩阵·图形渲染·着色器
SNAKEpc1213813 天前
OpenGL(十三)- Mip贴图
c语言·c++·图形渲染·贴图