调用glTexImage2D导致应用线程永久冻结的原因及解决方案咨询
故障堆栈信息
thread #3 frame #0: 0x00000001bb04d9b4 libsystem_kernel.dylib`mach_msg_trap + 8 frame #1: 0x00000001bb04dd60 libsystem_kernel.dylib`mach_msg + 76 frame #2: 0x00000001bdb28dd0 IOKit`io_connect_method + 440 frame #3: 0x00000001bdb28bec IOKit`IOConnectCallMethod + 236 frame #4: 0x00000001d5b1b970 IOGPU`IOGPUResourceCreate + 224 frame #5: 0x00000001d5b159f8 IOGPU`-[IOGPUMetalResource initWithDevice:remoteStorageResource:options:args:argsSize:] + 476 frame #6: 0x00000001d5b18ab8 IOGPU`-[IOGPUMetalTexture initWithBuffer:descriptor:sysMemOffset:sysMemRowBytes:vidMemSize:vidMemRowBytes:args:argsSize:isStrideTexture:] + 616 frame #7: 0x00000001d5b1925c IOGPU`-[IOGPUMetalTexture initWithPrimaryBuffer:heapIndex:bufferIndex:bufferOffset:length:descriptor:sysMemRowBytes:vidMemSize:vidMemRowBytes:args:argsSize:] + 96 frame #8: 0x000000021c45bc10 AGXMetal13_3`___lldb_unnamed_symbol4663$$AGXMetal13_3 + 468 frame #9: 0x0000000133953aa8 AppleMetalOpenGLRenderer`GLDTextureRec::loadObj() + 3156 frame #10: 0x000000013398a960 AppleMetalOpenGLRenderer`gldModifyTexSubImage + 160 frame #11: 0x000000021bd9a7e8 GLEngine`glTexImage2D_Exec + 1944 frame #12: 0x000000021bd7b8d0 libGL.dylib`glTexImage2D + 80
故障触发原因
从堆栈可以明确调用卡在Apple平台内核态的GPU纹理资源创建环节,属于AppleMetalOpenGLRenderer适配层的典型挂起问题,触发原因主要分为三类:
- 参数非法:传入
glTexImage2D的入参不匹配,包括但不限于:纹理宽高不符合当前OpenGL版本要求(比如低版本要求2的幂次尺寸)、GL_UNPACK_ALIGNMENT设置值和实际像素数据的行字节数不匹配、传入的像素数据指针指向的内存已被释放/存在越界,导致底层创建GPU资源时触发异常挂起 - 上下文/线程冲突:在未绑定合法OpenGL上下文的线程调用
glTexImage2D,或是多线程同时操作同一个纹理对象、多线程调用OpenGL API未加锁,导致底层Metal资源调度冲突 - 系统适配缺陷:Apple已正式弃用OpenGL,其底层OpenGL转Metal的适配层本身存在已知bug,在使用压缩纹理、4K以上大尺寸纹理时容易触发内核态调用挂起
解决思路与方法
- 首先校验参数合法性:
- 打印
glTexImage2D所有入参,确认尺寸、格式、数据指针符合规范,同时检查GL_UNPACK_ALIGNMENT设置值和传入像素数据的行字节数匹配,保证数据指针指向的内存在glTexImage2D调用完成前不会被释放 - 开启OpenGL调试输出:调用
glEnable(GL_DEBUG_OUTPUT)绑定调试回调,查看调用前是否存在未捕获的OpenGL错误告警
- 打印
- 其次排查线程与上下文问题:
- 确认调用
glTexImage2D的线程已绑定合法OpenGL上下文,所有OpenGL API调用要么在同一个线程执行,要么多线程调用时各自绑定独立的共享上下文 - 排查是否存在其他线程同时操作同一个纹理ID,比如调用
glTexImage2D的同时另一个线程在删除该纹理
- 确认调用
- 最后做适配规避:
- 确认参数和线程无问题的前提下,可将
glTexImage2D替换为glTexStorage2D+glTexSubImage2D的组合调用,避免每次调用都重新创建纹理资源 - 大纹理或复杂场景下,可切换到Metal API彻底规避OpenGL适配层的缺陷
- 确认参数和线程无问题的前提下,可将
内容的提问来源于stack exchange,提问作者Changkun
相关产品推荐
相关产品推荐

