纹理缓存有两个作用。首先,纹理缓存中的数据可以被重复利用,当一次访问需要的数据已经存在于纹理缓存中时,就可以避免对显存进行读取。数据重用过滤了一部分对显存的访问,节约了带宽,也不必按照显存对齐的要求读取。第二,纹理缓存可以缓存拾取坐标附近几个像元的数据,可以实现滤波模式,也能提高具有一定局部性的访问的效率。
纹理存储器是只读的,不需要关心缓存数据一致性问题。这意味着如果更改了绑定到纹理存储器的数据,纹理缓存中的数据可能并没有被更新,此时通过纹理拾取得到的数据可能是错误的。因此,在每次修改了绑定到纹理的数据以后,都需要对纹理进行重新绑定。由于不能从设备端修改CUDA数组,因此只有在对绑定到纹理的线性内存进行修改时才需要注意这一点。
线性内存中的数据只能与一维纹理绑定,并且纹理拾取坐标是定点型,坐标的值也与数据在线性内存中的偏移量相同;而CUDA数组可以与一维、二维或者三维纹理绑定,纹理拾取坐标是浮点型,并且支持许多特殊功能。纹理存储器的特殊功能有:
浮点型纹理拾取坐标:使用浮点型的纹理拾取坐标对纹理进行寻址,只对与CUDA数组绑定的存储器有效。地址映射的方式可以是归一化或者非归一化的:使用归一化纹理时,纹理在每个维度上的坐标被映射到浮点数[0.0, 1.0)范围内;使用非归一化纹理坐标时,各个维度上的坐标则被映射到浮点数[0.0, N)的范围内,其中N是纹理在该维度上像元的数量。由于在GPU中通常用浮点计算点的坐标,因此使用浮点数作为纹理拾取坐标更加自然;使用归一化的纹理拾取坐标可以不用关心纹理的实际尺寸,简化了渲染程序的编写。
寻址模式:寻址模式规定了纹理拾取的输入坐标超出纹理寻址范围时的行为,有钳位模式和循环模式两种。使用钳位模式时,当输入的坐标超出了寻址范围,输入的值将被“钳位”到寻址范围的最大值或者最小值;循环模式只对归一化坐标有效,此时要对超出寻址范围的纹理坐标作求模等处理。例如,对映射到[0.0, 1.0)的归一化纹理坐标,输入拾取坐标1.25,钳位模式会将输入按照0.99999…处理,而循环模式会将输入0.25处理。
类型转换:如果像元中的数据是8-bit或者16-bit定点型,类型转换功能对拾取的返回值进行类型转换,将其映射到归一化的浮点范围[0.0f, 1.0f](对无符号整型)或者[ -1.0f, 1.0f](对有符号整型)。
滤波:如果将返回类型是浮点型的CUDA数组与纹理绑定,那么就可以对返回的值进行滤波。滤波模式可以是最近点取样模式或者线性滤波模式两种。最近点模式返回与浮点型的纹理抓取坐标最近像元的值,而线性滤波模式则会先取出附近几个像元,然后按照抓取坐标与这几个像元的距离进行线性插值,返回线性插值得到的值。线性滤波可以使纹理渲染得到的画面更加平滑自然。线性滤波需要的插值计算不需要可编程单元参与,提供了额外的浮点处理能力,但精度较低。使用线性滤波模式返回的值经过了插值处理,适合用于图像处理;使用最近点取样模式的返回值不会改变纹理中像元的值,适合用于实现查找表。
关于纹理拾取模式的详细描述,可以参考附录F。
使用纹理存储器时,首先要在主机端声明需要绑定到纹理的线性存储器或CUDA数组,并设置好纹理参照系,然后将纹理参照系与线性内存或者CUDA数组绑定。在主机端完成配置工作后,就可以在内核函数中通过纹理抓取函数访问纹理存储器了。
3.2.4.1 CUDA数组
在显存中可以分配的空间有两种:CUDA 数组和线性内存。此外,常数存储器中通过缓存加速读取的数据实际也存在于显存中。CUDA数组和线性内存都可以与纹理参照系绑定,但CUDA数组对纹理拾取访问进行了优化,在设备端也只能通过纹理拾取访问。 |