glCompressedTexSubImage2D何时需传入行首对齐的imageSize参数
ASTC纹理
glCompressedTexSubImage2D参数适配方案 没有官方公开的OpenGL/OpenGL ES API可以直接查询设备对glCompressedTexSubImage2D的imageSize参数校验规则,这个差异本质是部分老旧Adreno GPU的驱动bug,不是标准规定的可查询特性,你遇到的RedMI 4(搭载Adreno 505 GPU)就在受影响设备范围内。
受影响设备范围
这个bug基本只出现在2018年之前发布的、搭载Adreno 5xx及更早系列GPU的安卓设备上,ARM Mali、Imagination PowerVR系列GPU,以及Adreno 6xx及之后的新GPU驱动都不存在这个问题,会正确按照OpenGL ES规范校验实际数据尺寸。
通用适配方案
因为没有可直接查询的设备参数,最稳妥的方案是做运行时轻量探测,无额外性能开销:
- 完成ASTC纹理初始化后,第一次调用
glCompressedTexSubImage2D做数据更新前,先发起一次无副作用的测试调用:将x偏移设为1个块宽度(对ASTC_RGBA_6x6格式就是xoffset=6),更新6x6像素大小的区域,传入符合规范的块大小16字节作为imageSize。如果驱动触发参数校验错误,这次调用会直接返回,不会写入任何纹理数据,不存在副作用。 - 调用完成后立刻执行
glGetError(),如果返回GL_INVALID_VALUE,标记当前设备需要传行首对齐尺寸;如果没有报错,后续所有更新都传实际数据尺寸即可。
两类尺寸的计算方式
以ASTC_RGBA_6x6格式为例,单块固定占16字节,块像素维度为6x6:
- 符合规范的实际数据尺寸
先把更新区域的偏移、宽高对齐到块边界,再计算大小:const int blockPixelW = 6, blockPixelH = 6; const int blockBytes = 16; int updateBlockX = (updateWidth + blockPixelW - 1) / blockPixelW; int updateBlockY = (updateHeight + blockPixelH - 1) / blockPixelH; int validImageSize = updateBlockX * updateBlockY * blockBytes; - 驱动bug要求的行首对齐尺寸
这类驱动校验imageSize时,不会扣减x方向偏移对应的块宽度,会默认从纹理最左边缘开始计算横向块数:int alignedBlockX = (xOffset + updateWidth + blockPixelW - 1) / blockPixelW; int alignedBlockY = (updateHeight + blockPixelH - 1) / blockPixelH; int bugCompatImageSize = alignedBlockX * alignedBlockY * blockBytes;
注意:有bug的驱动仅在参数校验阶段用到这个对齐尺寸,实际读取纹理数据时还是会按正确的块偏移访问,所以哪怕传入的
imageSize比实际数据大,也不会出现内存越界、渲染错误的问题。
兜底快速适配方案
如果不想做运行时探测,可以直接通过GPU厂商字符串做判断:调用glGetString(GL_VENDOR)获取厂商字符串,如果返回值包含Qualcomm,再进一步判断GPU型号为Adreno 5xx及更早型号,直接使用行首对齐尺寸传参即可覆盖绝大多数问题设备。
内容的提问来源于stack exchange,提问作者Tiewei Li
相关产品推荐
相关产品推荐

