Normally I would call glTexParameter when loading/initializing a texture. However, my current use case is that the same texture image may be used with many different parameter combinations (including GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, GL_TEXTURE_BORDER_COLOR, etc).
Basically, I am mapping a texture to a mesh where it is clamped, and then mapping the same texture to different mesh where it is mirrored/repeated.
Is it better to call glTexParameter every frame before drawing each mesh/group (this is essentially supplying uniforms to my fragment shader), or should I load each texture multiple times for each different combination of parameters?
Keep in mind I may have a large number of textures each with several combinations of texture parameters.
I am using OpenGL 3.3 but am interested in any ideas.
Looks like you should try Sampler Object feature (core from 3.3). It separates texture image from it's parameters. So you can use just one texture with multiple parameter sets.
From spec:
Sampler objects may be bound to texture
units to supplant the bound texture's sampling state. A single
sampler may be bound to more than one texture unit simultaneously,
allowing different textures to be accessed with a single set of
shared sampling parameters.
Also, by binding different sampler
objects to texture units to which the same texture has been bound,
the same texture image data may be sampled with different sampling
parameters.
Looks like it is exactly what you need but you should definitely test it on the OpenGL implementation you are targeting and compare with other approaches - this is the only way to answer to anything related to performance in OpenGL.
You are approaching this all wrong, in GL 3.3+ Sampler Objects allow you to separate all of the states you just mentioned from Texture Objects.
OpenGL 4.4 Core Profile Specification - Table 23.18. Textures (state per sampler object) - pp. 539
As long as you have a non-zero Sampler Object bound to a Texture Image Unit, then when it comes time to sample that texture the state will be taken from the Sampler Object instead of the bound Texture Object. Note that the only thing that differs between 3.3 and 4.4 in terms of sampler state is the addition of a Debug label.
This approach allows you to change the sampling parameters for a texture simply by binding a different sampler object to the texture image unit you sample it using. Furthermore, the sampler object interface uses a much nicer design that does not require you to bind a sampler in order to change its properties.
Related
So recently I started reading through OpenGL wiki articles. This is how I picture OpenGL texturing described from here. Though, several points are not clear yet.
Will following statements be true, false or depends?
Binding two textures to same texture unit is impossible.
Binding two samplers to same texture unit is impossible.
Binding one texture to two different texture units is impossible.
Binding one sampler to two different texture units is impossible.
It is application's responsibility to be clear about what sampler type is passed to what uniform variable.
It is shader program's responsibility to make sure to take sampler as correct type of uniform variable.
number of texture units are large enough. Let each mesh loaded to application occupy as much texture unit as it please.
Some Sampler parameters are duplicate of texture parameters. They will override texture parameter setting.
Some Sampler parameters are duplicate of sampler description in shader program. Shader program's description will override samplers parameters.
I'm going through your statements in the following. Sometimes I will argue with quotes from the OpenGL 4.5 core profile specification. None of that is specific to GL 4.5, I just chose it because that is the most recent version.
1. Binding two textures to same texture unit is
impossible.
If I'd say "false", it would be probably misleading. The exact statement would be "Binding two textures to the same target of the same texture unit is impossible." Technically, you can say, bind a 2D texture and a 3D texture to the same unit. But you cannot use both in the same draw call. Note that this is a dynamic error condition which depends on what values you set the sampler uniforms to. Quote from section 7.10 "Samplers" of the GL spec:
It is not allowed to have variables of different sampler types
pointing to the same texture image unit within a program object. This
situation can only be detected at the next rendering command issued
which triggers shader invocations, and an INVALID_OPERATION error will
then be generated.
So the GL will detect this error condition as soon as you actually try to draw something (or otherwise trigger shader invocations) with that shaders while you configured it such that two sampler uniforms reference different targets of the same unit. But it is not an error before. If you temporarily set both uniforms to the same value but do not try to draw in that state, no error is ever generated.
2. Binding two samplers to same texture unit is impossible.
You probably mean Sampler Objects (as opposed to just "sampler" types in GLSL), so this is true.
3. Binding one texture to two different texture units is impossible.
False. You can bind the same texture object to as many units as there are available. However, that is quite a useless operation. Back in the days of the fixed-function pipeline, there were some corner cases where this was of limited use. For example, I've saw someone binding the same texture twice and use register combiners to multiply both together, because he needed some square operation. However, with shaders, you can sample the texture once and do anything you want with the result, so there is really no use case left for this.
4. Binding one sampler to two different texture units is impossible.
False. A single sampler object can be referenced by multiple texture units. You can just create a sampler object for each sampling state you need, no need to create redundant ones.
5. It is application's responsibility to be clear about what sampler type is passed to what uniform variable.
6. It is shader program's responsibility to make sure to take sampler as correct type of uniform variable.
I'm not really sure what exaclty you are asking here. The sampler variable in your shader selectes the texture target and must also match the internal data fromat of the texture object you want to use (i.e. for isampler or usampler, you'll need unnormalized integer texture formats otherwise results are undefined).
But I don't know what "what sampler type is passed to what uniform variable" is supposed to mean here. As far as the GL client side is concerned, the opaque sampler uniforms are just something which can be set to the index of the texture unit which is to be used, and that is done as an integer via glUniform1i or the like. There is no "sampler type" passed to a uniform variable.
7. number of texture units are large enough. Let each mesh loaded to application occupy as much texture unit as it please.
Not in the general case. The required GL_MAX_TEXTURE_IMAGE_UNITS (which defines how many different texture units a fragment shader can access) by the GL 4.5 spec is just 16. (There are separate limits per shder stage, so there is GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS and so on. They are all required to be at least 16 in the current spec.)
Usually, you have to switch textures inbetween draw calls. The usage of array textures and texture atlasses might allow one to further reduce the number of necessary state switches (and, ultimately, draw calls).
Very modern GPUs also support GL_ARB_bindless_texture, which completely bypasses the "texture unit" layer of indirection and allows the shader to directly reference a texture object by some opaque handle (which basically boils down to referencing some virtual GPU memory address under the hood). However, that feature is not yet part of the OpenGL standard.
8. Some Sampler parameters are duplicate of texture parameters. They will override texture parameter setting.
Yes. Traditionally, there were no separate sampler objects in the GL. Instead, the sampler states like filtering or wrap modes were part of the texture object itself. But modern hardware does not operate this way, so the sampler objects API has been introduced as the GL_ARB_sampler_objects extension (nowadays, a core feature of GL). If a sampler object is bound to a texture unit, its settings will override the sampler state present in the texture object.
9. Some Sampler parameters are duplicate of sampler description in shader program. Shader program's description will override samplers parameters.
I'm not sure what you mean by that. What "sampler descripitons" does a shader program define? There is only the declaration of the uniform and possibly the initialization via layout(binding=...). However, that is just the initial value. The client can update that any time by setting the uniform to another value, so that is not really "overriding" anything. But I'm not sure if you mean that.
Is there a way in OpenGL to render a vertex buffer using multiple independent textures in VRAM without manually binding them (i.e. returning control to the CPU) in between?
Edit: So I'm currently rendering objects with multiple textures by rendering with a single texture, binding a new texture, and repeating, until everything is done. This is slow and requires returning control to CPU and making syscalls for every texture. Is there a way to avoid this switching, and make multiple textures available to the shaders to choose based on vertex data?
As mentioned in the comments on the question, glActiveTexture is the key - samplers in GLSL bind to texture units (e.g. GL_TEXTURE0), not specific texture targets (e.g. GL_TEXTURE2D), so you can bind a GL_TEXTURE2D texture under glActiveTexture(GL_TEXTURE0), another under glActiveTexture(GL_TEXTURE1), and then bind your GLSL sampler2D values to be 0, 1, etc. (NB: do not make your sampler2D values GL_TEXTURE0, GL_TEXTURE1, etc. - they are offsets from GL_TEXTURE0).
according to the https://www.opengl.org/wiki/Texture, "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."
but the glactivetexture use enum to iterate texture unit. How to make sure the texture unit is associate with correct target when i want to reuse the unit. for example use unit 2 for 2D first, and then want reuse it for 3D. I have tried glBindTexture(GL_TEXTURE_1D/2D/3D, 0); but seems not work. Should i use glenable?
You have misinterpreted what this statement on the OpenGL Wiki means.
It is referring to sampler uniforms in GLSL. It is an error to have a sampler2D and samplerCube, for instance, that both reference the same texture image unit. Since this situation cannot be determined at compile- / link-time, there is no error state that will be generated. Instead, you will create undefined behavior at shader run-time if you try to use two different types of samplers that refer to the same texture image unit.
Regarding enabling GL_TEXTURE_1D, etc. That is for the fixed-function pipeline. It does nothing in shader-based OpenGL, textures are effectively "enabled" or "disabled" completely programatically. If you do not sample anything from a certain texture image unit during the execution of your shader, then you can think of it as "disabled." However, ultimately such thinking is not productive in the programmable pipeline. You should simply forget that those states ever existed.
Do I understand correctly that the typical way to draw multiple objects that each have a different texture in OpenGL is to simply bind one texture at a time using glBindTexture and draw the objects that use it, then bind a different texture and do the same?
In other words, only one texture can be "active" for drawing at any particular time, and that is the last texture that was bound using glBindTexture. Is this correct?
bind one texture at a time using glBindTexture and draw the objects that use it, then bind a different texture and do the same?
In other words, only one texture can be "active" for drawing at any particular time
These two statements are not the same thing.
A single rendering operation can only use a single set of textures at any time. This set is defined by the textures currently bound to the various texture image units. The number of texture image units available is queryable through GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS.
So multiple textures can be "active" for a single rendering command.
However, that's not the same thing as binding a bunch of textures and then rendering several objects, where each object only uses some (or one, in your case) of the bound textures. You could do that, but really, there's not much point.
The association between a GLSL sampler and a texture image unit is part of the program object state. It's the value you set on the sampler uniform. Therefore, in order to do what you suggest, you would have to bind a bunch of textures, set the uniform to one of them, render, then set the uniform to the next, render, etc.
You are still incurring all of the cost of binding those textures. You're still incurring all of the overhead of changing uniforms. Indeed, it might be less efficient, since the normal way (bind, render, bind, render) doesn't involve changing uniform state.
Plus, it's just weird. Generally, you shouldn't be changing uniform sampler state dynamically. You define a simple convention (diffuse texture comes from unit 0, specular from unit 1, etc), and you stick to that convention. Any GL 3.x-class hardware is required to provide no less than 48 texture image units (16 per stage), so it's not like you're going to run out.
There are mechanisms to do things not entirely unlike what you're talking about. For example, array textures can be leveraged, though this requires explicit shader logic and support. That would almost certainly be faster than the "bind a bunch of textures" method, since you're only binding one. Also, the uniform you'd be changing is a regular data uniform rather than a sampler uniform, which is likely to be faster.
With the array texture mechanism, you can also leverage instancing, assuming you're rendering the same object with slightly different parameters.
From what I gather, glActiveTexture sets the active "texture unit". Each texture unit can have multiple texture targets (usually GL_TEXTURE_1D, 2D, 3D or CUBE_MAP).
If I understand correctly, you have to call glActiveTexture to set the texture unit first (initialized to GL_TEXTURE0), and then you bind (one or more) "texture targets" to that texture unit?
The number of texture units available is system dependent. I see enums for up to 32 in my library. I guess this essentially means I can have the lesser of my GPU's limit (which I think is 16 8) and 32 textures in GPU memory at any one time? I guess there's an additional limit that I don't exceed my GPU's maximum memory (supposedly 1 GB).
Am I understanding the relationship between texture targets and texture units correctly? Let's say I'm allowed 16 units and 4 targets each, does that mean there's room for 16*4=64 targets, or does it not work like that?
Next you typically want to load a texture. You can do this via glTexImage2D. The first argument of which is a texture target. If this works like glBufferData, then we essentially bind the "handle"/"texture name" to the texture target, and then load the texture data into that target, and thus indirectly associate it with that handle.
What about glTexParameter? We have to bind a texture target, and then choose that same target again as the first argument? Or does the texture target not need to be bound as long as we have the correct active texture unit?
glGenerateMipmap operates on a target too...that target has to still be bound to the texture name for it to succeed?
Then when we want to draw our object with a texture on it, do we have to both choose an active texture unit, and then a texture target? Or do we choose a texture unit, and then we can grab data from any of the 4 targets associated with that unit? This is the part that's really confusing me.
All About OpenGL Objects
The standard model for OpenGL objects is as follows.
Objects have state. Think of them as a struct. So you might have an object defined like this:
struct Object
{
int count;
float opacity;
char *name;
};
The object has certain values stored in it and it has state. OpenGL objects have state too.
Changing State
In C/C++, if you have an instance of type Object, you would change its state as follows: obj.count = 5; You would directly reference an instance of the object, get the particular piece of state you want to change, and shove a value into it.
In OpenGL, you don't do this.
For legacy reasons better left unexplained, to change the state of an OpenGL object, you must first bind it to the context. This is done with some from of glBind* call.
The C/C++ equivalent to this is as follows:
Object *g_objs[MAX_LOCATIONS] = {NULL};
void BindObject(int loc, Object *obj)
{
g_objs[loc] = obj;
}
Textures are interesting; they represent a special case of binding. Many glBind* calls have a "target" parameter. This represents different locations in the OpenGL context where objects of that type can be bound. For example, you can bind a framebuffer object for reading (GL_READ_FRAMEBUFFER) or for writing (GL_DRAW_FRAMEBUFFER). This affects how OpenGL uses the buffer. This is what the loc parameter above represents.
Textures are special because when you first bind them to a target, they get special information. When you first bind a texture as a GL_TEXTURE_2D, you are actually setting special state in the texture. You are saying that this texture is a 2D texture. And it will always be a 2D texture; this state cannot be changed ever. If you have a texture that was first bound as a GL_TEXTURE_2D, you must always bind it as a GL_TEXTURE_2D; attempting to bind it as GL_TEXTURE_1D will give rise to an error (while run-time).
Once the object is bound, its state can be changed. This is done via generic functions specific to that object. They too take a location that represents which object to modify.
In C/C++, this looks like:
void ObjectParameteri(int loc, ObjectParameters eParam, int value)
{
if(g_objs[loc] == NULL)
return;
switch(eParam)
{
case OBJECT_COUNT:
g_objs[loc]->count = value;
break;
case OBJECT_OPACITY:
g_objs[loc]->opacity = (float)value;
break;
default:
//INVALID_ENUM error
break;
}
}
Notice how this function sets whatever happens to be in currently bound loc value.
For texture objects, the main texture state changing functions are glTexParameter. The only other functions that change texture state are the glTexImage functions and their variations (glCompressedTexImage, glCopyTexImage, the recent glTexStorage). The various SubImage versions change the contents of the texture, but they do not technically change its state. The Image functions allocate texture storage and set the texture's format; the SubImage functions just copy pixels around. That is not considered the texture's state.
Allow me to repeat: these are the only functions that modify texture state. glTexEnv modifies environment state; it doesn't affect anything stored in texture objects.
Active Texture
The situation for textures is more complex, again for legacy reasons best left undisclosed. This is where glActiveTexture comes in.
For textures, there aren't just targets (GL_TEXTURE_1D, GL_TEXTURE_CUBE_MAP, etc). There are also texture units. In terms of our C/C++ example, what we have is this:
Object *g_objs[MAX_OBJECTS][MAX_LOCATIONS] = {NULL};
int g_currObject = 0;
void BindObject(int loc, Object *obj)
{
g_objs[g_currObject][loc] = obj;
}
void ActiveObject(int currObject)
{
g_currObject = currObject;
}
Notice that now, we not only have a 2D list of Objects, but we also have the concept of a current object. We have a function to set the current object, we have the concept of a maximum number of current objects, and all of our object manipulation functions are adjusted to select from the current object.
When you change the currently active object, you change the entire set of target locations. So you can bind something that goes into current object 0, switch to current object 4, and will be modifying a completely different object.
This analogy with texture objects is perfect... almost.
See, glActiveTexture does not take an integer; it takes an enumerator. Which in theory means that it can take anything from GL_TEXTURE0 to GL_TEXTURE31. But there's one thing you must understand:
THIS IS FALSE!
The actual range that glActiveTexture can take is governed by GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS. That is the maximum number of simultaneous multitextures that an implementation allows. These are each divided up into different groupings for different shader stages. For example, on GL 3.x class hardware, you get 16 vertex shader textures, 16 fragment shader textures, and 16 geometry shader textures. Therefore, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS will be 48.
But there aren't 48 enumerators. Which is why glActiveTexture doesn't really take enumerators. The correct way to call glActiveTexture is as follows:
glActiveTexture(GL_TEXTURE0 + i);
where i is a number between 0 and GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS.
Rendering
So what does all of this have to do with rendering?
When using shaders, you set your sampler uniforms to a texture image unit (glUniform1i(samplerLoc, i), where i is the image unit). That represents the number you used with glActiveTexture. The sampler will pick the target based on the sampler type. So a sampler2D will pick from the GL_TEXTURE_2D target. This is one reason why samplers have different types.
Now this sounds suspiciously like you can have two GLSL samplers, with different types that use the same texture image unit. But you can't; OpenGL forbids this and will give you an error when you attempt to render.
I'll give it a try ! All this is not that complicated, just a question of terms, hope I'll make myself clear.
You can create roughly as many Texture Objects as there is available memory in your system. These objects hold the actual data (texels) of your textures, along with parameters, provided by glTexParameter (see FAQ).
When being created, you have to assign one Texture Target to one texture object, which represents the type of the texture (GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE, ...).
These two items, texture object and texture target represent the texture data. We'll come back to them later.
Texture units
Now, OpenGL provides an array of texture units, that can be used simultaneously while drawing. The size of the array depends of the OpenGL system, yours has 8.
You can bind a texture object to a texture unit to use the given texture while drawing.
In a simple and easy world, to draw with a given texture, you'd bind a texture object to the texture unit, and you'd do (pseudocode):
glTextureUnit[0] = textureObject
As GL is a state machine, it, alas, does not work this way. Supposing that our textureObject has data for the GL_TEXTURE_2D texture target, we'll express the previous assignment as :
glActiveTexture(GL_TEXTURE0); // select slot 0 of the texture units array
glBindTexture(GL_TEXTURE_2D, textureObject); // do the binding
Note that GL_TEXTURE_2D really depends on the type of the texture you want to bind.
Texture objects
In pseudo code, to set texture data or texture parameters, you'd do for example :
setTexData(textureObject, ...)
setTexParameter(textureObject, TEXTURE_MIN_FILTER, LINEAR)
OpenGL can't directly manipulate texture objects, to update/set their content, or change their parameters, you have to first bind them to the active texture unit (whichever it is). The equivalent code becomes :
glBindTexture(GL_TEXTURE_2D, textureObject) // this 'installs' textureObject in texture unit
glTexImage2D(GL_TEXTURE_2D, ...)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
Shaders
Shaders have access to all the texture units, they don't care about the active texture.
Sampler uniforms are int values representing the index of the texture unit to use for the sampler (and not the texture object to use).
So you have to bind your texture objects to the units you want to use.
The type of the sampler will do the match with the texture target that is used in the texture unit : Sampler2D for GL_TEXTURE_2D, and so on...
Imagine the GPU like some paint processing plant.
There are a number of tanks, which delivers dye to some painting machine. In the painting machine the dye is then applied to the object. Those tanks are the texture units
Those tanks can be equipped with different kinds of dye. Each kind of dye requires some other kind of solvent. The "solvent" is the texture target. For convenience each tank is connected to some solvent supply, and but only one kind of solvent can be used at a time in each tank. So there's a valve/switch TEXTURE_CUBE_MAP, TEXTURE_3D, TEXTURE_2D, TEXTURE_1D. You can fill all the dye types into the tank at the same time, but since only one kind of solvent goes in, it will "dilute" only the kind of dye matching. So you can have each kind of texture being bound, but the binding with the "most important" solvent will actually go into the tank and mix with the kind of dye it belongs to.
And then there's the dye itself, which comes from a warehouse and is filled into the tank by "binding" it. That's your texture.