调用 glDrawArrays 时随机崩溃

Random crash when calling glDrawArrays

我正在制作一个 iOS 应用程序,它可以渲染大量我从磁盘动态流式传输的纹理。我将 NSCache 用于纹理的 LRU 缓存。一个屏幕显示 3D 模型,另一个屏幕显示纹理的全屏细节,可以通过滑动来更改该纹理。一种非常简单的旋转木马。该应用程序在 1GiB 设备上永远不会占用超过 250MiB 的 RAM,纹理的缓存运行良好。

对于全屏视图,我有一个基于屏幕分辨率和纹理分辨率(不同纹理坐标)的 VBO 缓存。我从不删除这些 VBO,并始终检查 VBO 是否正常 (glIsBuffer())。屏幕是单独的 UIViewControllers,我在它们两个中使用相同的 EAGLContext,没有上下文共享。这没关系,因为它在同一个线程上。

所有这些都是 Open GL ES 2.0,一切正常。我可以在 3D/2D 屏幕之间切换,更改纹理。根据可用内存的需要,纹理 created/deleted 即时运行。

但有时我在调用时渲染全屏四边形时随机崩溃:

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

当我连续收到大量内存警告时,就会发生这种情况。有时我会在几秒钟内收到数百条内存警告,应用程序运行正常,但有时它会在滑动到新的全屏纹理四边形时崩溃。即使对于已经全屏渲染的纹理,也会发生这种情况。它永远不会在使用相同纹理的 3D 模型上崩溃。

崩溃报告总是在 glDrawArrays 调用(在我的代码中),但出现 EXC_BAD_ACCESS KERN_INVALID_ADDRESS at 0x00000018 异常。堆栈跟踪中的最后一次调用始终是 gleRunVertexSubmitARM。这种情况发生在各种 iPad 和 iPhone 上。

看起来系统内存压力损坏了一些 GL 内存,但我不知道何时何地以及为什么。

我还尝试从 VBO 切换到在堆上保存顶点数据的旧方法,在调用 glDrawArrays 之前,我首先检查顶点数据是否不是 NULL。结果是一样的,在内存不足的情况下随机崩溃。

有什么想法是错误的吗? EXC_BAD_ACCESS中的地址0x00000018真的很烂,但我不知道应该是谁的地址。取消分配的纹理或着色器是否会导致 glDrawArrays 中的 EXC_BAD_ACCESS

经过几天的紧张调试,我终于弄明白了。问题是 NSCache 存储 OpenGL 纹理。在内存压力下,NSCache 开始从中删除项目以释放​​内存。问题是,在那种情况下,它是在自己的后台线程 (com.apple.root.utility-qos) 上执行的,因此该线程没有 GL 上下文(也没有与主上下文的共享组),因此纹理名称不是有效的名称(不能删除)并且会泄漏 GL 内存。所以在一些内存警告之后,出现了很多泄漏的 GL 纹理,内存已满,应用程序最终崩溃了。

TL DR: 不要使用 NSCache 来缓存 OpenGL 对象,因为它会在内存警告后泄漏它们。