Texture/Sampler Binding Caching

I am intending to do some state caching so that I can prune out redundant bind calls.

To bind a texture one must first activate a texture unit (glActivateTexture) then bind the texture to a specific target (GL_TEXTURE_2D, 3D etc). This seems to imply that the conceptual record of bindinds are stored in a structure like this: textureBindings[slot][target].

Sampler objects however make no mention of the target in their binding, they only specify the slot in glBindSampler.

This leads me to my question.

If a sampler is bound to slot 0, does that sampler get used for all textures (of different types) bound to slot 0? Or are texture bindinds not soped by the pair of slot/target, but configured to both a slot and a target when bound, somewhat like this
textureBindings[slot] = texture
textureTarget[GL_TEXTURE_2D] = texture

Thanks for any insight!


The target is a property of the texture, not the texture image unit or the binding.

A texture is created by binding an allocated but unused name to a target. Calling glIsTexture() on a name returned from glGenTextures() but not yet bound will return GL_FALSE. The texture isn’t created until the first glBindTexture() call, at which point the texture is created with the specified target. Calling glBindTexture() on a name which refers to an existing texture checks that the target parameter matches the texture’s target, and fails with GL_INVALID_OPERATION if it doesn’t.

With the direct state access (DSA) functions introduced in OpenGL 4.5, texture name allocation and texture creation are combined into the glCreateTextures() function. This takes the target as a parameter, so that it knows which type of texture to create.


It’s not implied. It’s explicitly documented as such, in the specification’s state tables for TEXTURE_BINDING_xD.

It’s legal to bind a texture of every target to the same unit. Prior to shaders, the fixed-function pipeline uses these bindings, and an enable for each target, and (if more than one target is enabled) a target precedence to determine which texture is actually sampled.

With the programmable pipeline, the user has to declare a sampler of a specific type, and that’s the target that is sampled. It’s a validation error to draw with two samplers of differing target bound to the same unit.

Note the usage of the term “sampler” with regard to shaders is unrelated to sampler objects (glBindSampler() etc).

I had forgotten that texture bindings are stored per target per texture unit. Sampler bindings are just per texture unit (i.e. a single sampler object affects all targets).

One should just forget about this idiosyncrasy of “old style” (Where you use glActivateTexture) OpenGL and always set the older texture type of the slot to 0 when setting another texture with a different type. That saves you from a lot of headaches!

Thanks for the replies

Glad that I had interpreted the texture binding correctly. The state caching, while yes making the implementation on my part more complicated, adds up to saving quite a bit of performance. Its simple enough to handle typically.

Samplers only being scoped per slot while textures is slot and target type is a bit odd (in the sense of inconstant), but I understand the historical reasons.

Thanks for the clarifications everyone, good to know I was not missing anything.


Late to the party with this, but what I’ve done in the past is use specific slots for different target types. So I might say something like “diffuse texture always goes in slot 0” or “skybox cubemap always goes in slot 6”. That way I get to quite happily (and safely) ignore the fact that multiple target types can be bound to the same slot. Of course, it won’t work if you’re using the fixed pipeline.

That would mean you couldn’t do something like having a diffuse color come from an array texture. That seems unnecessarily limiting.

If you want to arbitrarily flip between array textures and regular textures you need separate shaders anyway. Plus a way of flagging the texture type CPU-side so that you know which shader to use. Your glBindTexture calls need to be different. I don’t see the addition of using another separate slot for array textures as being too onerous.