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.
Related
Suppose I want to render many different models, each with a different transformation matrix I want to be applied to their vertices. As far as I understand, the naive approach is to specify a matrix uniform in the vertex shader, the value of which is updated for each mesh during rendering.
It's obvious to me that this is a bad idea, due to the expense of many uniform updates and draw calls. So, what is the most efficient way to achieve this in modern OpenGL?
I've genuinely tried to find a straight, clear answer to this question. Most answers I find vaguely mention UBOs, or instance drawing (which afaik won't work unless you are drawing instances of the same mesh many times, which is not my goal).
With OpenGL 4.6 or with ARB_shader_draw_parameters, each draw in a multi-draw rendering command (functions of the form glMultiDraw*) is assigned a draw index from 0 to the number of draw calls specified by that function. This index is provided to the Vertex Shader via the gl_DrawID input value. You can then use this index to fetch a matrix from any number of constructs: UBOs, SSBOs, buffer textures, etc.
This works for multi-draw indirect rendering as well. So in theory, you can have a compute shader operation generate a bunch of rendering commands, then render your entire scene with a single draw call (assuming that all of your objects live in the same vertex buffers and can use the same shader and other state). Or at the very least, a large portion of the scene.
Furthermore, this index is considered dynamically uniform, so you can also use it (or values derived from it and other dynamically uniform values) to index into arrays of textures, fetch a texture from an array of bindless textures, or the like.
I've found a lot of resources that tell you what to type to get a texture on screen, but would like a higher level conceptual understanding of what the openGL API is "doing" and what all of the differences in terminology "mean".
I'm going to do my best to explain what I've picked up, but would love any corrections/additions, or pointers to resources where I can read further (and just a note that I've found the documentation of the actual API calls to just reference themselves in circles and be conceptually lacking).
glGenTextures- this won't actually allocate any memory for the data of a texture on the graphics card (you just tell it "how many" textures you want it to generate, so it doesn't know anything about the size...), but instead sets kind of a "name" aside so you can reference given textures consistently (I've been thinking of it as kind of "allocating a pointer").
glBindTexture- use the "name" generated in glGenTexture to specify that "we're now talking about this texture for future API calls until further notice", and further, we're specifying some metadata about that "pointer" we've allocated saying whether the texture it points to (/will point to) is of type GL_TEXTURE_2D or ..._3D or whatever. (Is it just me, or is it weird that this call has those two seemingly totally different functionalities?)
glTexParameter- sets other specified metadata about the currently "bound" texture. (I like this API as it seems pretty self explanatory and lets you set metadata explicitly... but I wonder why letting OpenGL know that it's a GL_TEXTURE_2D isn't part of THIS call, and not the previous? Especially because you have to specify that it's a GL_TEXTURE_2D every time you call this anyways? And why do you have to do that?)
glTexImage2D- allocates the memory for the actual data for the texture on the graphics card (and optionally uploads it). It further specifies some metadata regarding how it ought be read: its width, height, formatting (GL_RGB, GL_RGBA, etc...). Now again, why do I again have to specify that it's a GL_TEXTURE_2D when I've done it in all the previous calls? Also, I guess I can understand why this includes some metadata (rather than offloading ALL the texture metadata calls to glTexParameter as these are pretty fundamental/non-optional bits of info, but there are also some weird parameters that seem like they oughtn't have made the cut? oh well...)
glActiveTexture- this is the bit that I really don't get... So I guess graphics cards are capable of having only a limited number of "texture units"... what is a texture unit? Is it that there can only be N texture buffers? Or only N texture pointers? Or (this is my best guess...) there can only be N pointers being actively read by a given draw call? And once I get that, where/how often to I have to specify the "Active Texture"? Does glBindTexture associate the bound texture with the currently active texture? Or is it the other way around (bind, then set active)? Or does uploading/allocating the graphics card memory do that?
sampler2D- now we're getting into glsl stuff... So, a sampler is a thing that can reference a texture from within a shader. I can get its location via glGetUniformLocation, so I can set which texture that sampler is referencing- does this correspond to the "Active Texture"? So if I want to talk about the texture I've specified as GL_TEXTURE0, I'd call glUniform1i(location_of_sampler_uniform,0)? Or are those two different things?
I think that's all I got... if I'm obviously missing some intuition or something, please let me know! Thanks!
Let me apologize for answering with what amounts to a giant wall of text. I could not figure out how to format this any less obnoxious way ;)
glGenTextures
this won't actually allocate any memory for the data of a texture on the graphics card (you just tell it "how many" textures you want it to generate, so it doesn't know anything about the size...), but instead sets kind of a "name" aside so you can reference given textures consistently (I've been thinking of it as kind of "allocating a pointer").
You can more or less think of it as "allocating a pointer." What it really does is reserve a name (handle) in the set of textures. Nothing is allocated at all at this point, basically it just flags GL to say "you can't hand out this name anymore." (more on this later).
glBindTexture
use the "name" generated in glGenTexture to specify that "we're now talking about this texture for future API calls until further notice", and further, we're specifying some metadata about that "pointer" we've allocated saying whether the texture it points to (/will point to) is of type GL_TEXTURE_2D or ..._3D or whatever. (Is it just me, or is it weird that this call has those two seemingly totally different functionalities?)
If you will recall, glGenTextures (...) only reserves a name. This function is what takes the reserved name and effectively finalizes it as a texture object (the first time it is called). The type you pass here is immutable, once you bind a name for the first time, it has to use the same type for every successive bind.
Now you have finally finished allocating a texture object, but it has no data store at this point -- it is just a set of states with no data.
glTexParameter
sets other specified metadata about the currently "bound" texture. (I like this API as it seems pretty self explanatory and lets you set metadata explicitly... but I wonder why letting OpenGL know that it's a GL_TEXTURE_2D isn't part of THIS call, and not the previous? Especially because you have to specify that it's a GL_TEXTURE_2D every time you call this anyways? And why do you have to do that?)
I am actually not quite clear what you are asking here -- maybe my explanation of the previous function call will help you? But you are right, this function sets the state associated with a texture object.
glTexImage2D
allocates the memory for the actual data for the texture on the graphics card (and optionally uploads it). It further specifies some metadata regarding how it ought be read: its width, height, formatting (GL_RGB, GL_RGBA, etc...). Now again, why do I again have to specify that it's a GL_TEXTURE_2D when I've done it in all the previous calls? Also, I guess I can understand why this includes some metadata (rather than offloading ALL the texture metadata calls to glTexParameter as these are pretty fundamental/non-optional bits of info, but there are also some weird parameters that seem like they oughtn't have made the cut? oh well...)
This is what allocates the data store and (optionally) uploads texture data (you can supply NULL for the data here and opt to finish the data upload later with glTexSubImage2D (...)).
You have to specify the texture target here because there are half a dozen different types of textures that use 2D data stores. The simplest way to illustrate this is a cubemap.
A cubemap has type GL_TEXTURE_CUBE_MAP, but you cannot upload its texture data using GL_TEXTURE_CUBE_MAP -- that is nonsensical. Instead, you call glTexImage2D (...) while the cubemap is bound to GL_TEXTURE_CUBE_MAP and then you pass something like GL_TEXTURE_CUBE_MAP_POSITIVE_X to indicate which of the 6 2D faces of the cubemap you are referencing.
glActiveTexture
this is the bit that I really don't get... So I guess graphics cards are capable of having only a limited number of "texture units"... what is a texture unit? Is it that there can only be N texture buffers? Or only N texture pointers? Or (this is my best guess...) there can only be N pointers being actively read by a given draw call? And once I get that, where/how often to I have to specify the "Active Texture"? Does glBindTexture associate the bound texture with the currently active texture? Or is it the other way around (bind, then set active)? Or does uploading/allocating the graphics card memory do that?
This is an additional level of indirection for texture binding (GL did not always have multiple texture units and you would have to do multiple render passes to apply multiple textures).
Once multi-texturing was introduced, binding a texture actually started to work this way:
glBindTexture (target, name) => ATIU.targets [target].bound = name
Where:
* ATIU is the active texture image unit
* targets is an array of all possible texture types that can be bound to this unit
* bound is the name of the texture bound to ATIU.targets [target]
The rules since OpenGL 3.0 have been, you get a minimum of 16 of these for every shader stage in the system.
This requirement allows you enough binding locations to maintain a set of 16 different textures for each stage of the programmable pipeline (vertex,geometry,fragment -- 3.x, tessellation control / evaluation -- 4.0). Most implementations can only use 16 textures in a single shader invocation (pass, basically), but you have a total of 48 (GL3) or 80 (GL4) places you can select from.
sampler2D
now we're getting into glsl stuff... So, a sampler is a thing that can reference a texture from within a shader. I can get its location via glGetUniformLocation, so I can set which texture that sampler is referencing- does this correspond to the "Active Texture"? So if I want to talk about the texture I've specified as GL_TEXTURE0, I'd call glUniform1i(location_of_sampler_uniform,0)? Or are those two different things?
Yes, the samplers in GLSL store indices that correspond to GL_TEXTUREn, where n is the value you have assigned to this uniform.
These are not regular uniforms, mind you, they are called opaque types (the value assigned cannot be changed/assigned from within a shader at run-time). You do not need to know that, but it might help to understand that if the question ever arises:
"Why can't I dynamically select a texture image unit for my sampler at run-time?" :)
In later versions of OpenGL, samplers actually became state objects of their own. They decouple some of the state that used to be tied directly to texture objects but had nothing to do with interpreting how the texture's data was stored. The decoupled state includes things like texture wrap mode, min/mag filter and mipmap levels. Sampler objects store no data.
This decoupling takes place whenever you bind a sampler object to a texture image unit - that will override the aforementioned states that are duplicated by every texture object.
So effectively, a GLSL sampler* references neither a texture nor a sampler; it references a texture image unit (which may have one or both of those things bound to it). GLSL will pull sampler state and texture data accordingly from that unit based on the declared sampler type.
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).
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.