10.3_一维纹理操作
10.3 一维纹理操作
为了便于说明,本节将详细介绍一维纹理,然后扩展到二维及三维纹理的讨论。
纹理设置
纹理中的数据可以由1个、2个或4个元素组成,元素的类型可以是:
有符号或无符号的8位、16位或32位整型值;
16位的浮点型数值;
32位的浮点型数值。
在后缀名为.cu的文件中(无论使用的是CUDA运行时还是驱动程序API),纹理引用的声明如下所示:
texture<ReturnType, Dimension, ReadMode> Name;
其中ReturnType为纹理内置指令的返回值;Dimension的值能取1、2或3,分别表示一维、二维或三维;ReadMode是一个可选参数,默认值为CUDAReadMode ElementType。读取模式仅会对整型值的纹理数据
产生影响。默认情况下,当纹理数据是整型值时,纹理将传回整型值,必要时,还会将其转化成32位的整型值。然而,当读取模式指定为CUDAReadModemNormalizedFloat时,8位或16位的整型值将转化为范围在 之间的浮点值,具体转化公式如表10-1所示。
表10-1 纹理中浮点值的转化
此类转化操作的C代码如代码清单10-1所示。
代码清单10-1 纹理单元的浮点转换
float
TexPromoteFloat( signed char c ) { if ( c == (signed char) 0x80 ) { return -1.0f; } return(float)c/127.0f;
}
float
TexPromoteFloat(short s) { if(s==(short)0x8000){ return-1.0f; } return(float)s/32767.0f;
}
float
TexPromoteFloat(unsigned char uc) { return(float)uc/255.0f;
}
float
TexPromoteFloat(unsigned short us) { return(float)us/65535.0f;一旦声明纹理引用,就可以通过调用纹理内置指令在内核中对其调用。不同类型的纹理将使用不同内置指令,如表10-2所示。
表10-2 纹理指令
纹理引用类似于全局变量,具有文件范围的作用域。它们不能以参数的形式创建、销毁或传递,因此,如果要将其封装到上层抽象结构中,必须格外小心。
1. CUDA运行时
在启动使用纹理的内核之前,必须调用CUDABindTexture()、CUDABindTexture2D()或CUDABindTextureToArray()将纹理绑定到CUDA数组或设备内存。由于CUDA运行时的语言集成特性,开发人员可以通过名字引用纹理,如下所示
texture<float, 2,udaReadMode ElementType> tex;
...
CUDART CHECK(cudaBindTextureToArrayTEX, texArray));绑定纹理之后,只要纹理绑定没有改变,内核就可以使用纹理引用读取对应绑定内存里的数据。
2. 驱动程序API
若一个纹理在一个.cu后缀文件中声明,驱动程序必须使用cuModuleGetTexRef()对其进行查询。使用驱动程序API时,纹理的固定属性必须显式设置,且必须符合编译器生成代码时的假定条件。对于绝大多数纹理而言,这仅仅意味着纹理格式必须与.cu文件中声明的纹理格式一致;但也有一些例外,比如设置纹理将整型值或16位的浮点值增强为归一化的32位浮点值时。
函数cuTexRefSetFormat负责指定纹理中数据的格式。
数组格式如下所示。
NumPackedComponents指定了每个纹理元素中分量的数目。该值可以为1、2或4。16位的浮点数(half)是一种特殊的数据类型,很适合用来高效保真地表示图像数据。[1]尾数部分使用10位(实际上归一化的数据的尾数精度为11位)已可以精确表示大多数传感器生成的数据,指数部分使用5位也足够表示同一图像中星光以及日光亮度的动态范围。大多数浮点数架构都不包含处理16位浮点数的原生指令,CUDA也不例外。硬件纹理单元可以自动将16位的浮点数转换为32位的浮点数,或者在CUDA内核中,使用__float2half_rn和__half2float_rn指令将数据在16位与32位浮点数之间相互转换。
[1] 8.3.4节详细介绍了16位的浮点数。