Prefer to read without ads? Become a member — from $10/month — and support the work. Already a member? Log in to read ad-free on this device.

10.3 1D Texturing

For illustrative purposes, we will deal with 1D textures in detail and then expand the discussion to include 2D and 3D textures.

10.3.1 Texture Setup

The data in textures can consist of 1, 2 or 4 elements of any of the following types:

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;}
Listing 10-1. Texture Unit Floating Point Conversion (source on GitHub)

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.

CUDA Runtime

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.


  1. Section 8.3.4 describes 16-bit floats in detail.↩︎