为啥 iOS 设备上的 glGenerateMipmap() 会占用这么多客户端内存?

Posted

技术标签:

【中文标题】为啥 iOS 设备上的 glGenerateMipmap() 会占用这么多客户端内存?【英文标题】:Why does glGenerateMipmap() on iOS device take so much client memory?为什么 iOS 设备上的 glGenerateMipmap() 会占用这么多客户端内存? 【发布时间】:2017-05-06 17:05:21 【问题描述】:

我正在开发一个使用 OpenGL ES 渲染图像的 ios 应用程序。这是我设置纹理参数和数据的函数的关键代码 sn-p:

glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelData);

这在 iPhone 上工作得很好,但是在缩小时会出现锯齿。所以我尝试使用mipmap,通过修改之前的代码如下:

glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);// for mipmapping
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelData);
glGenerateMipmap(GL_TEXTURE_2D);// This will generate all mip-mapping textures

而且这种 mipmapping 效果很好,在缩小期间不再出现锯齿。

但是,我发现 mipmapping 占用了太多内存:不是图形内存,而是主内存(在 OpenGL 术语中,不是服务器端内存,而是客户端内存)!

我通过 Instruments 的内存监视器确认了这一点:

如果没有 mipmapping,我的纹理设置功能几乎不会占用任何内存,即使纹理源是 4K 分辨率的图像。这样的结果清楚地证明glTexImage2D只在OpenGL服务器,即显卡中分配内存,因此不关心显卡内存使用情况的Memory Monitor无法对其进行监控。

但是使用 glGenerateMipmap() 进行 mipmapping,当我创建 4K 纹理时,堆内存使用量迅速增加了大约 100M。而在 glDeleteTextures() 之后,它会回退。

看来glGenerateMipmap()不仅在显存中生成mip贴图纹理,还消耗主存。而这块主存在纹理删除后可以回收。很奇怪,不是吗?

我想知道为什么。 而我的重点是,如何在不占用太多内存的情况下使用 mip 映射?感谢您的帮助。

【问题讨论】:

【参考方案1】:

您不应使用 glGenerateMipmap(),而应使用 PVRTexTools 或 TextureTool 之类的工具并使用 PVRTC 压缩格式,请参阅: https://developer.apple.com/library/content/qa/qa1611/_index.html

【讨论】:

谢谢。但似乎这种方法只适用于静态内容,例如游戏中 3d 模型的纹理。但我需要的是渲染动态加载的图像。【参考方案2】:

神速。

    您可能需要设置 GL_TEXTURE_MAX_LEVEL(默认为 1000)和 GL_TEXTURE_BASE_LEVEL(默认为 0)的值。 4K 分辨率图像并不意味着“4K 纹理”。根据我的经验,如果图像的长度是 1025 X 1025,那么纹理是 2048 X 2048。请检查图像的大小。 "cpu 内存" --> "主内存"; "gpu memory" --> "graphics memory",后者可能合适。

【讨论】:

1.我尝试添加 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 3);但这对内存使用没有影响。 2. 图像分辨率为6912*3456,所以最接近2的幂为8192*4096,RGBA8纹理占用128M原始字节。 @godspeed,请参阅Mipmap wiki:“在将三个通道存储为单独平面的 RGB 图像的情况下,可以将总 mipmap 可视化为整齐地适合两倍大的正方形区域原始图像每边的尺寸。它还直观地展示了使用 mipmap 需要多 33% 的内存。” 作为wiki,GL_TEXTURE_MAX_LEVEL的值对Mipmap的大小没有影响。

以上是关于为啥 iOS 设备上的 glGenerateMipmap() 会占用这么多客户端内存?的主要内容,如果未能解决你的问题,请参考以下文章

为啥视网膜设备上的图标不显示?

为啥 iOS 蓝牙会多次发现同一设备?

为啥我的 iOS 设备不扫描广告扩展程序?

为啥推送通知不能在 iOS 中使用 Firebase 从设备到设备?

为啥使用相机意图捕获的图像会在 Android 上的某些设备上旋转?

为啥远程服务器上的签名验证比设备上更安全?