OpenGL4.5 - bind multiple textures and samplers
Asked Answered
A

2

12

enter image description here

I'm trying to understand Textures, Texture Units and Samplers in OpenGL 4.5. I'm attaching a picture of what I'm trying to figure out. I think in my example everything is correct, but I am not so sure about the 1D Sampler on the right side with the question mark.

So, I know OpenGL offers a number of texture units/binding points where textures and samplers can be bound so they work together.

Each of these binding points can support one of each texture targets (in my case, I'm binding targets GL_TEXTURE_2D and GL_TEXTURE_1D to binding point 0, and another GL_TEXTURE_2D to binding point 1).

Additionally, samplers can be bound to these binding points in much the same way (I have bound a 2D sampler to binding point 0 in the pic).

The functions to perform these operations are glBindTextureUnit and glBindSampler.

My initial thought was to bind the 1D sampler to binding point 0, too, and in shader land do the matching based on the binding point and the type of the sampler:

layout (binding = 0) uniform sampler1D tex1D;
layout (binding = 0) uniform sampler2D tex2D;

Quoting the source:

Each texture image unit supports bindings to all targets. So a 2D texture and an array texture can be bound to the same image unit, or different 2D textures can be bound in two different image units without affecting each other. So which texture gets used when rendering? In GLSL, this depends on the type of sampler that uses this texture image unit.

but I found the following statement:

[..] sounds suspiciously like you can use the same texture image unit for different samplers, as long as they have different texture types. Do not do this. The spec explicitly disallows it; if two different GLSL samplers have different texture types, but are associated with the same texture image unit, then rendering will fail. Give each sampler a different texture image unit.

So, my question is, what is the purpose of binding different texture targets to the same binding point at all, if ultimately a single sampler is going to be bound to that binding point, forcing you to choose?

The information I'm quoting: https://www.khronos.org/opengl/wiki/Texture#Texture_image_units

Alphaalphabet answered 21/7, 2017 at 9:9 Comment(0)
M
11

So why does this exist? Well...

Once upon a time, there were no texture units (this is why glActiveTexture is a separate function from glBindTexture). Indeed, there weren't even texture objects in OpenGL 1.0. But there still needed to be different kinds of textures. You still needed to be able to create data for a 2D texture and a 3D texture. So they came up with the texture target distinction, and they used glEnables to determine which target would be used in a rendering operation.

When texture objects came into being in GL 1.1, they had to decide on the relationship between a texture object and the target. They decided that once an object was bound to a target, it was permanently associated with that target. Because of the aforementioned need to have multiple textures of different types, with the old enable functionality, it was decided that each target represented a separate object binding point. And they made you repeat the binding point in glBindTexture, so that it would be clear to the reader of the code which binding point's data you were disturbing.

Cut to OpenGL 1.2, when multitexture came out. So now they need you to be able to bind multiple textures of the same target, but to different "units". But they couldn't change glBindTexture to specify a particular unit; that would be a backwards-incompatible change.

Now, they could have completely revamped how textures work, creating a new binding function specifically for multitexturing and the like. But the OpenGL ARB loves backwards compatibility; they like making the old API functions work, no matter what the resulting API looks like. So instead, they decided that a texture unit would be an entire set of bindings, with each set having an enable state saying which target was the one to be used. And you switch between units with glActiveTexture.

Of course, once shaders came about, you can see how this all changes. The enable state becomes the sampler type in the shader. So now there's no explicit code describing which texture target is enabled; it's just shader stuff. So they had to make a rule that says that two samplers cannot use the same unit if they're different types.

That's why each texture unit has multiple independent binding points: OpenGL's commitment to backwards compatibility.

It is best to ignore that this capability exists. Bind the right textures that your particular shader needs. So focus on using those functions, and don't worry about the fact that you could have two textures bound to the same target. If you want to make certain that you're not accidentally using the wrong texture, you can use glBindTextures or glBindTextureUnit with a texture name of 0, which will unbind all targets in the particular texture unit(s).

Metamorphism answered 21/7, 2017 at 14:41 Comment(7)
Can you show an example of muti-bind for OpenGL 3.3? glBindTextures and glBindTextureUnit exist only for OGL4.5. There are plenty of hardware still today that don't allow that version.Koweit
@Ripi2: Literally the first word of the title is "OpenGL4.5". So clearly, the OP is asking about OpenGL version 4.5, so there's no need to give a 3.3 alternative. Also, the question is tagged opengl-4, so again 3.3 makes no sense here.Metamorphism
@Ripi2: Also, multi-bind was added to core in GL 4.4. And it's an extension that's supported on some 3.3 class hardware too. Even ARB_DSA has some support on 3.3 hardware.Metamorphism
@NicolBolas thanks for your answer. So, in short, connect to a unit texture exclusively a) a single texture and b) a single sampler, both of which must match in dimensionality. Is that correct? I got the 4.5 Programming Guide and I'm trying to stick to it (actually it doesn't even mention glActiveTexture), and the description for glBindTextureUnit does not say that it unbinds all the other targets for the given texture unit (the second effect you mentioned in your first paragraph).Alphaalphabet
@CarlosRomero: Huh, you're right. I misread the specification for both glBindTextureUnit and glBindTextures. Neither unbinds textures from other targets in that texture unit, unless you are explicitly binding 0 for that unit.Metamorphism
I see, thank you! That makes it clear. A small something - I think glBindTextures is backwards-compatible? So the correct thing (if sticking to 4.5 / the big fat book) would be to call glBindTextureUnit(unit, 0)?Alphaalphabet
@CarlosRomero: glBindTextures does what glBindTextureUnit does, just for multiple textures at one time.Metamorphism
K
-1

Let's say you have two GLSL programs:

in progA:

uniform sampler1D progA_sampler1D;
uniform sampler2D progA_sampler2D;

in progB:

uniform sampler1D progB_sampler1D;
uniform sampler2D progB_sampler2D;

And you have several textures with names text1D_1, text1D_2, text1D_3,... text2D_1, text2D_2, etc

Now let's suppose you want progA to sample from text1D_1 and text2D_1 and progB to sample from text1D_2 and text2D_2

You already know that each sampler must be associated with a texture unit, not with a texture name. We can not use the same texture unit for both samplers progA_sampler1D and progA_sampler2D

FIRST OPTION: four texture units

glUseProgram(progA);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_1D, text1D_1);
glUniform1i(locationProgA_forSampler1D, 1); // Not glUniform1i(locationProgA_forSampler1D, GL_TEXTURE0 + 1);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, text2D_1);
glUniform1i(locationProgA_forSampler2D, 2);

glUseProgram(progB);
glActiveTexture(GL_TEXTURE0 + 3);
glBindTexture(GL_TEXTURE_1D, text1D_2);
glUniform1i(locationProgA_forSampler1D, 3);
glActiveTexture(GL_TEXTURE0 + 4);
glBindTexture(GL_TEXTURE_2D, text2D_2);
glUniform1i(locationProgA_forSampler2D, 4);

SECOND OPTION: two texture units

glUseProgram(progA);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_1D, text1D_1);
glUniform1i(locationProgA_forSampler1D, 1);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, text2D_1);
glUniform1i(locationProgA_forSampler2D, 2);

glUseProgram(progB);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_1D, text1D_2);
glUniform1i(locationProgA_forSampler1D, 2);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_2D, text2D_2);
glUniform1i(locationProgA_forSampler2D, 1);

Note that unit GL_TEXTURE0 + 1 has bound two textures text1D_1 and text2D_2 with different types.
On the same way GL_TEXTURE0 + 2 has bound two textures, of types GL_TEXTURE_2D and GL_TEXTURE_1D

WRONG OPTION: two texture units

glUseProgram(progA);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_1D, text1D_1);
glUniform1i(locationProgA_forSampler1D, 1);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, text2D_1);
glUniform1i(locationProgA_forSampler2D, 2);

glUseProgram(progB);
glActiveTexture(GL_TEXTURE0 + 1);
//Next is wrong: two textures (text1D_1 and text1D_2) of same type GL_TEXTURE_1D
glBindTexture(GL_TEXTURE_1D, text1D_2);
glUniform1i(locationProgA_forSampler1D, 1);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, text2D_2);  //Wrong: two textures of same type GL_TEXTURE_2D
glUniform1i(locationProgA_forSampler2D, 2);
Koweit answered 21/7, 2017 at 14:49 Comment(2)
glUniform1i(locationProgA_forSampler1D, GL_TEXTURE0 + 1); That's not how it works. And even if we ignore that bug, what you're talking about is something you should never do. That is exceedingly error-prone code.Metamorphism
@NicolBolas Thanks for pointing that bug. Fixed now. Of course Second option is error-prone. It's just an explanation for the OP about those apparently contradictory sentences.Koweit

© 2022 - 2024 — McMap. All rights reserved.