OpenGL支持非二次幂纹理的底层原理是什么?

如题,是在底层填充空白强行使纹理大小符合二次幂,然后依然采用二次幂的形式,还是其他的?我主要是为了优化纹理图集的长度和宽度的设定,如果是前者,那我就不…
关注者
41
被浏览
24,571

3 个回答

这个问题其实与GPU的寻址方式(地址对齐要求)有关,并非是图形API层面的限制。

GPU作为一种专用的绘图硬件(当然现在也在逐渐泛用化),其高速运作的能力来自于对于特殊目的定制的特殊硬件模块。其中,实现贴图参照的模块就是这么一种特殊的硬件模块。

在CPU当中,当要参照一张贴图的时候,你需要在程序当中给出计算地址的公式。比如,当你要访问一张宽度为w高度为h的以行优先顺序存储的贴图当中的坐标为(x,y)的贴图的时候,就需要用y*w+x来计算这个地址。这个计算公式是以代码的形式给出的,对于CPU来说,它只是将这张贴图作为一个连续的内存空间处理而已。

但是GPU不同。GPU当中完成对贴图访问(寻址以及获取数据)的模块是以硬件形式存在的。它是不可编程的,你只能从它所提供的几种方式当中选一种。 这是第一个因素。

其次,GPU当中进行的是大量的并行计算。这就意味着GPU经常会需要对贴图的不同位置进行同时参照。而且,这种参照往往是需要截取贴图当中一小块面积的内容,也就是是一种2D甚至是3D形状的参照,按CPU那种1D线性的方式保存贴图的话,会使得要参照的那一小部分地址不连续,从而影响高速缓存的命中率,降低速度。

所以,贴图在GPU的内存(显存)当中,一般都不是以行优先或者列优先方式存储的,而是以“块”为单位存储的。就好像一块一块马赛克组成的墙面那样,每个马赛克对应的像素在内存上是连续存储的。 这是第二个因素。

此外,为了尽可能节约内存开销和传输带宽,贴图一般都会以一种合适的压缩格式存储。而常用的压缩格式,如BC或者DXT,都是将贴图分块进行压缩。这同样隐含了贴图必须是这些“块”的整数倍这样一个条件。 这是第三个因素。

上述几个因素(硬件寻址+贴图在内存上的特殊排布方式+压缩算法要求)决定了一款GPU在设计的时候就会将这个“块”的最小尺寸固定下来。有的GPU是2x2像素,有的是4x4像素,还有的是8x8像素。所以当贴图的尺寸不是这些“块”的整数倍的时候,当贴图被传送到GPU内存(显存)的时候,就会被拉伸或者在四周(一般是右侧和下侧)填充无用数据,使其成为这些“块”的整数倍。(称为pitch)

这个情况是GPU硬件的要求,与使用何种图形API无关。但是诸如OpenGL这种抽象等级较高的图形API在易用性和可控性之间选择了易用性,也就是尽力隐藏这些细节,在其内部为你完成必要的拉伸或者pitch的操作,从而使得你觉得好像它支持非二次幂的纹理。

opengl只是个规范, 只要结果/表现符合要求,runtime怎么做都可以

比如你传一张21x1024的纹理,rt可以直接线性存,也可以padding成24x1024然后分块存(假设tilesize=4),也可以padding成32x1024存(要求二次幂),甚至padding成4096x4096(吃饱了撑的)

通常就是前两种情况,第一种完全不浪费空间但效率可能下降,第二种稍微会多消耗一点空间。


能二次幂就二次幂,不能也尽量做到某个二次幂的整数倍咯。不同的硬件上都很可能会有不同表现,等这个真的成为瓶颈了再去优化吧……