GL_ARB_bindless_texture 64-Bit Sampler Handles
The GL_ARB_bindless_texture extension fundamentally
changes how OpenGL shaders access textures by replacing traditional
texture unit bindings with direct 64-bit GPU memory handles. This
overview explains how the extension exposes these 64-bit handles inside
the OpenGL Shading Language (GLSL), detailing the syntax, type
representations, conversions, and buffer storage mechanisms that enable
limitless, direct texture referencing in shader programs.
Enabling the Extension in GLSL
To use bindless textures in GLSL, the shader must explicitly enable the extension at the top of the source file:
#version 450 core
#extension GL_ARB_bindless_texture : requireEnabling this extension alters the behavior of standard sampler types
(such as sampler2D, samplerCube, and
sampler2DArray) so that they are treated as first-class,
64-bit data values rather than opaque references tied to limited texture
units.
Representation as 64-Bit Types
Under the hood, a bindless texture handle is a 64-bit unsigned
integer generated on the CPU via glGetTextureHandleARB or
glGetTextureSamplerHandleARB. Inside GLSL, this value is
exposed in two primary ways:
- Native Sampler Types: Standard sampler variables
(e.g.,
sampler2D) directly occupy 64 bits of storage. - Integer Representations:
uvec2: If 64-bit integers are not natively available or enabled, GLSL represents the handle as a two-component 32-bit unsigned integer vector (uvec2).uint64_t: If theGL_ARB_gpu_shader_int64extension is supported and enabled, handles can be represented directly as native 64-bit unsigned integers.
Explicit Type Casting and Constructors
The extension defines constructors and explicit conversions between sampler types and their underlying integer representations. This allows handles to be passed or unpacked from generic integer formats:
From integers to samplers:
// Constructing a sampler from a uvec2 handle uvec2 handleVec = ...; sampler2D mySampler = sampler2D(handleVec); // Constructing a sampler from uint64_t (with GL_ARB_gpu_shader_int64) uint64_t handleInt = ...; sampler2D mySampler64 = sampler2D(handleInt);From samplers to integers:
uvec2 handleVec = uvec2(mySampler); uint64_t handleInt = uint64_t(mySampler);
Storage in Uniform Buffers and Shader Storage Buffers
Traditionally, samplers could only be declared as loose uniform
variables tied to texture image units.
GL_ARB_bindless_texture lifts this restriction entirely.
Because samplers are 64-bit values, they can be stored in:
- Uniform Buffer Objects (UBOs)
- Shader Storage Buffer Objects (SSBOs)
- User-defined structs
- Dynamic arrays and function parameters
For example, a Shader Storage Buffer Object can pass an unbounded array of texture handles directly to a draw call:
layout(std430, binding = 0) readonly buffer MaterialBuffer {
sampler2D textures[];
} materials;
void main() {
// Directly sample using a handle fetched from an SSBO
vec4 color = texture(materials.textures[gl_DrawID], texCoords);
}Layout Qualifiers: Bindless vs. Bound
The extension introduces interface layout qualifiers to control whether samplers behave as bindless handles or fall back to standard OpenGL bindings:
layout(bindless_sampler): Forces uniform samplers to be treated as 64-bit bindless handles. By default, enablingGL_ARB_bindless_texturemakes all samplers bindless.layout(bound_sampler): Reverts specific sampler uniforms to the legacy binding model, allowing traditional texture unit bindings (glBindTextureUnit) to be used alongside bindless handles in the same shader.