Bindless Texture

From OpenGL Wiki
Jump to: navigation, search
Bindless Texture
ARB extension ARB_bindless_texture
Vendor extension NV_bindless_texture

Bindless Textures are a method for passing Textures to Shader by a name, rather than by binding them to the OpenGL Context. These texture can be used by samplers or images.

Purpose

With Uniform Buffer Objects, Shader Storage Buffer Objects, and various other means, it is possible to communicate state information to a Shader without having to modify any OpenGL context state. You simply set data into the appropriate buffer objects. Then make sure that those buffers are bound when it comes time to render with the shader.

There are lines of communication for which this cannot work. Specifically, the Opaque Types in GLSL: samplers, images, and atomic counters. These all derive their data based on objects bound to locations in the OpenGL context at the time of the rendering call.

This has two performance bearing consequences. The immediate performance cost is that you must bind textures and images to the context before rendering. This process has an intrinsic cost.

A second consequence is that you must issue more rendering calls. The reason being that you need to switch textures for different objects. If the objects could fetch which textures to use solely from memory (UBOs, SSBOs, etc), then one could render a number of objects with the same multi-draw drawing command. Indeed, with Indirect Rendering, it becomes possible to generate those rendering commands on the GPU. In a perfect world, this would reduce rendering to little more than a compute dispatch operation followed by a single multi-draw-indirect operation.

This can only work if the shader can get its textures from values in memory, rather than data bound to the context. This is the purpose of bindless texturing; to remove an obstacle that makes this possible.

Overview

Bindless texturing is a bit complex. This represents a general overview of the process and the concepts that it uses.

Texture handles

The basic idea of bindless textures is to convert a Texture object into an integer (technically, OpenGL Objects are already integer numbers, but never mind that). This integer is called a handle, and it is an unsigned, 64-bit integer.

Note: The handle's value may or may not be an actual GPU address. You should not assume that it is, nor should you make any assumptions about its value relative to any other handle (other than the fact that different textures will have different handle values). Treat it as purely a reference.

Handles can be created from a Texture object alone. Such a handle refers to the texture using the sampler parameters within the texture object.

Handles can also be created from a Texture and Sampler Object. A handle created from a texture+sampler represents using that texture with that particular sampler object. Handles created by either one of these two processes are called texture handles.

Lastly, handles can be created from a specific image within the texture. These are called image handles. Image handles are intended to be used for Image Load Store operations, and cannot be used for regular sampler accesses. Similarly, texture handles cannot be used for image load/store access.

An outgrowth of the above is that an object (texture or sampler) can have multiple handles associated with it. A texture could get texture handles with different samplers, or a texture and image handle, or multiple image handles for the multiple images it stores. A single sampler object can be associated with multiple textures.

Once one of these objects has at least one handle associated with it, the object's state immediately becomes immutable. No functions that modify anything about the texture will work. This includes the Sampler Object used in texture+sampler handles.

Furthermore, there is no way to undo this immutability. Once you get a handle that is associated with that object, it is permanently frozen.

Note that you can still create View Textures from textures with handles. Similarly, views of a texture that uses a handle are still mutable (or at least, as mutable as an Immutable Storage Texture can be). Also, you can update the contents of the storage for such textures; just not their state.

Residency

Once an appropriate handle is created, the handle cannot be used by any program until it is made resident. Texture and image handles use different residency functions, but the concept is the same.

Handles can remain resident for as long as you wish. Residency is removed with a separate function call.

Note: Handle residency does not affect what operations can be performed on the texture/samplers involved. This however does not mean that residency is not cheap or unimportant. You should only maintain residency for handles that need it.

Usage

The foundation of bindless texture usage is the ability of GLSL to convert a 64-bit unsigned integer texture handle value into a sampler or image variable. Thus, these types are no longer truly Opaque Types, though the operations you can perform on them are still quite limited (no arithmetic for example).

Bindless texturing adds a number of features for communicating 64-bit integers to the API and to GLSL on the receiving end. It extends buffer-backed Interface Blocks with the ability to store sampler/image types. From the OpenGL side, these are treated as 64-bit unsigned integers.

Bindless texturing also adds a few unique features for transmitting bindless handles to shaders. It adds a 64-bit unsigned integer Vertex Attribute. It also allows sampler and image types to be passed as inputs and outputs between Shader Stages; they may only use flat interpolation qualifiers.

Safety

Bindless textures are not safe. The API is given fewer opportunities to ensure sane behavior; it is up to the programmer to maintain integrity. Furthermore, the consequences for mistakes are much more severe. Usually with OpenGL, if you do something wrong, you get either an OpenGL Error or undefined behavior. With bindless textures, if you do something wrong, the GPU can crash or your program can terminate. It might even bring down the whole OS.

Things to keep in mind:

  • When converting a handle into a sampler/image variable, the type of sampler/image must match with the handle.
  • The integer values used with handles must be actual handles returned by the handle APIs, and those handles must be resident. So you can't perform "pointer arithmetic" or anything of the like on them; treat them as opaque values that happen to be 64-bit unsigned integers.

Handle creation

Handle residency

GLSL handle usage

Extension implementation

This is not a core feature of any OpenGL version; at present, it only exists as an extension. This is not for any of the reasons that an extension might become ubiquitous. It is instead a matter of practicality.

If OpenGL 4.4 required bindless texture support, then only hardware that could support bindless textures could implement a conforming 4.4 implementation. But not all 4.3 hardware can handle bindless textures. The OpenGL ARB decided to make the functional optional by having it be an extension.

It is implemented across much of the OpenGL 4.x hardware spectrum. Intel is absent, but both AMD and NVIDIA have a lot of hardware that can handle it.