For illustrative purposes, we will deal with 1D textures in detail and then expand the discussion to include 2D and 3D textures.
The data in textures can consist of 1, 2 or 4 elements of any of the following types:
Signed or unsigned 8-, 16-, or 32-bit integers,
16-bit floating point values, or
32-bit floating point values.
A texture is accessed at runtime through a texture object,
of type cudaTextureObject_t, that is built from two descriptors: a
cudaResourceDesc that identifies the underlying memory (a CUDA array or
a range of device memory) and a cudaTextureDesc that specifies how that
memory is to be sampled – the addressing modes, the filtering mode,
whether coordinates are normalized, and the read mode. The object is
created with cudaCreateTextureObject() and released with
cudaDestroyTextureObject(); unlike the file-scope texture references
used by older versions of CUDA, it is an ordinary handle that is passed
to a kernel as a parameter.
The read mode is the readMode field of the cudaTextureDesc. It only
affects integer-valued texture data: by default
(cudaReadModeElementType), the texture passes back integers when the
texture data is integer-valued, promoting them to 32-bit if necessary.
But when cudaReadModeNormalizedFloat is specified, 8- or 16-bit integers
are promoted to floating point values in the range [0.0, 1.0] according
to the formulas below.
| Format | Conversion Formula To Float |
|---|---|
char c |
\[\left\{ \begin{matrix} - 1.0,\ c = = 0x80 \\ \frac{c}{127.0},\ otherwise \end{matrix} \right.\ \] |
short s |
\[\left\{ \begin{matrix} - 1.0,\ s = = 0x8000 \\ \frac{s}{32767.0},otherwise \end{matrix} \right.\ \] |
unsigned char uc |
\[\frac{uc}{255.0}\] |
unsigned short us |
\[\frac{us}{65535.0}\] |
Table 10-1. Floating point promotion (texture)
The C versions of this conversion operation are given in Listing 10-1.
floatTexPromoteToFloat( signed char c ){ if ( c == (signed char) 0x80 ) { return -1.0f; } return (float) c / 127.0f;} floatTexPromoteToFloat( short s ){ if ( s == (short) 0x8000 ) { return -1.0f; } return (float) s / 32767.0f;} floatTexPromoteToFloat( unsigned char uc ){ return (float) uc / 255.0f;} floatTexPromoteToFloat( unsigned short us ){ return (float) us / 65535.0f;}
A texture object is read inside a kernel by passing its handle to one of the texture intrinsics. Each intrinsic is templated on the return type and takes the texture object as its first argument; the intrinsic used depends on the type of texture, as shown in Table 10-2.
| Texture Type | Intrinsic |
|---|---|
| Linear device memory | tex1Dfetch<T>(tex, int index); |
| 1D CUDA array | tex1D<T>(tex, float x); |
| 2D CUDA array or 2D device memory | tex2D<T>(tex, float x, float y); |
| 3D CUDA array | tex3D<T>(tex, float x, float y, float z); |
| 1D layered texture | tex1DLayered<T>(tex, float x, int layer); |
| 2D layered texture | tex2DLayered<T>(tex, float x, float y, int layer); |
Table 10-2. Texture intrinsics
A texture object’s descriptors are immutable. Once it has been created, its addressing modes, filtering, normalization, and read mode are fixed; to sample the same memory a different way – for example, with a different addressing mode – an application creates a second texture object. Because the object is just a handle, it can be created and destroyed at will and passed freely as a kernel parameter.
A texture object is built from a cudaResourceDesc, which identifies
the memory to be textured, and a cudaTextureDesc, which specifies how to
sample it. To texture from a 2D CUDA array, for example:
cudaResourceDesc resDesc = { .resType = cudaResourceTypeArray };
resDesc.res.array.array = texArray;
cudaTextureDesc texDesc = {};
texDesc.filterMode = cudaFilterModePoint;
texDesc.addressMode[0] = cudaAddressModeClamp;
texDesc.addressMode[1] = cudaAddressModeClamp;
cudaTextureObject_t tex = 0;
cudaCreateTextureObject( &tex, &resDesc, &texDesc, NULL );
The resType selects the kind of memory: cudaResourceTypeArray for a
CUDA array, cudaResourceTypeLinear for a range of device memory (read
with tex1Dfetch()), or cudaResourceTypePitch2D for 2D-pitched device
memory. The fields of the cudaTextureDesc left unset by the empty-brace
initializer default to zero, which selects cudaReadModeElementType,
point filtering, unnormalized coordinates, and the clamp addressing
mode. The resulting handle is passed to kernels as a cudaTextureObject_t
parameter and released with cudaDestroyTextureObject() when it is no
longer needed.
16-bit floats (half) are a special data type that is well-suited to
representing image data with high integrity3;
with 10 bits of floating point mantissa (effectively 11 bits of
precision for normalized numbers), there is enough precision to
represent data generated by most sensors, and 5 bits of exponent gives
enough dynamic range to represent starlight and sunlight in the same
image. Most floating point architectures do not include native
instructions to process 16-bit floats, and CUDA is no exception. The
texture hardware promotes 16-bit floats to 32-bit floats automatically,
and CUDA kernels can convert between 16- and 32-bit floats with the
__float2half_rn() and __half2float_rn() intrinsics.
Section 8.3.4 describes 16-bit floats in detail.↩︎