uniforms

Started by
9 comments, last by Hodgman 11 years, 5 months ago
Hi,

I'm trying to move to using the new opengl layout qualifiers. I almost got everything working, however I ran into a small problem.
When I add the overlay_color uniform, I can't set it using the glUniform3fv(), and I don't know why. All the other uniforms, attributes, samplers can be set, except this one... The shader still compiles, links and validates successfully, and no warning is shown that would indicate anything.
When I try to set the overlay_color (using glUniform3fv) I get GL_INVALID_OPERATION. I checked the specs, but I can't figure out what I'm missing, because:
-the program object is set, and valid
-the variable's size matches the glUniform function
-the location (I think) is valid, and not -1
-the count is 1

here's the shader:


[ vertex shader ]

#version 420 core

layout(location=0) uniform mat4 mvp;

layout(location=0) in vec4 in_vertex;
layout(location=5) in vec2 in_texture;

out cross_shader_data
{
vec2 tex_coord;
} o;

out vec4 color;

void main()
{
o.tex_coord = in_texture;
gl_Position = mvp * in_vertex;
}

[ pixel shader ]

#version 420 core

layout(location=1,binding=0) uniform sampler2D texture0;
layout(location=2) uniform vec3 overlay_color;

in cross_shader_data
{
vec2 tex_coord;
} i;

out vec4 color;

void main()
{
color = texture(texture0, i.tex_coord) + vec4(overlay_color, 1);
}


here's how I'm passing the values:

glUseProgram( this->shader );
vec3 overlay_color = {{ 1, 0, 0 }};
glUniform3fv( 2, 1, &overlay_color.v[0] );
glUniformMatrix4fv( 0, 1, false,
&o->ppl->get_model_view_projection_matrix( o->ppl ).m[0].v[0] );
Advertisement
Why do you need this feature? If it is for the learning sake only, please wait until stable drivers release.
There are several sources of the problem.

What drivers are you using? GL_ARB_explicit_uniform_location is the part of GLSL 4.3 core spec, not 4.2. If you are using NV 310.xx drivers, then there could be a bug, since they are in a beta phase. If you are using previous releases, then you should check whether the extension is supported and probably enable it explicitly in GLSL (if it is supported).

I guess you don't have support GL_ARB_explicit_uniform_location and the reason it works for the first uniform is accidental correspondence with compiler's associated locations. NV uses alphabetical order of names. So, locations are as follows:
0 - mvp
2 - texture0
1 - overlay_color

Before trying to use some new feature check if it is presented and read specification to understand how it works. ;)

Why do you need this feature? If it is for the learning sake only, please wait until stable drivers release.
There are several sources of the problem.

What drivers are you using? GL_ARB_explicit_uniform_location is the part of GLSL 4.3 core spec, not 4.2. If you are using NV 310.xx drivers, then there could be a bug, since they are in a beta phase. If you are using previous releases, then you should check whether the extension is supported and probably enable it explicitly in GLSL (if it is supported).

I guess you don't have support GL_ARB_explicit_uniform_location and the reason it works for the first uniform is accidental correspondence with compiler's associated locations. NV uses alphabetical order of names. So, locations are as follows:
0 - mvp
2 - texture0
1 - overlay_color

Before trying to use some new feature check if it is presented and read specification to understand how it works. ;)


:( I'm on AMD with only OGL4.2 drivers... I guess I'll have to stick with glGetUniformLocation...
as for the why: I just wanted to get rid of keeping track of uniform locations. It's not that cumbersome, but it would be great if I didn't have to.
I guess for the rest you're right, I just accidentally got the uniform locations right. Well, thank you for clarifying the situation. I just thought this was available since gl 3.1

I'm on AMD with only OGL4.2 drivers... I guess I'll have to stick with glGetUniformLocation...
as for the why: I just wanted to get rid of keeping track of uniform locations. It's not that cumbersome, but it would be great if I didn't have to.

T thought you are using NV hardware, since only NV currently has GL 4.3 support.

Well, I don't agree that getting uniforms' (or attributes') location is cumbersome, especially because that should be done only once, during the initialization phase.
I'm using class-wrapper for each shaders. Uniforms and attributes locations are attributes of the instance, and they are set in the Init() function (at the same place where I load and compile shaders, and link the program). After that, I just call appropriate function (method) of the particular object to set values. It cannot be easier.

Another remark: Setting attribute location is also not correct in your code, because you are wasting 4 locations.
layout(location=0) in vec4 in_vertex;
layout(location=5) in vec2 in_texture;

In the unpacked format, both vec2 and vec4 consumes just one location. In your case, layout is like follows:
location 0: | in_vertex.x | in_vertex.y | in_vertex.z | in_vertex.w |
location 1: | < empty > | < empty > | < empty > | < empty > |
location 2: | < empty > | < empty > | < empty > | < empty > |
location 3: | < empty > | < empty > | < empty > | < empty > |
location 4: | < empty > | < empty > | < empty > | < empty > |
location 5: | in_texture.s | in_texture.t | < empty > | < empty > |

As you can see, there is a lot of wasted space. So, if you don't have a particular reason to define layout and reuse it across multiple shader-objects, It would be wiser to just let compiler do it, or read specifications to see how it actually works.
Happy coding! smile.png

[quote name='Yours3!f' timestamp='1351934185' post='4996807']
I'm on AMD with only OGL4.2 drivers... I guess I'll have to stick with glGetUniformLocation...
as for the why: I just wanted to get rid of keeping track of uniform locations. It's not that cumbersome, but it would be great if I didn't have to.

T thought you are using NV hardware, since only NV currently has GL 4.3 support.

Well, I don't agree that getting uniforms' (or attributes') location is cumbersome, especially because that should be done only once, during the initialization phase.
I'm using class-wrapper for each shaders. Uniforms and attributes locations are attributes of the instance, and they are set in the Init() function (at the same place where I load and compile shaders, and link the program). After that, I just call appropriate function (method) of the particular object to set values. It cannot be easier.

Another remark: Setting attribute location is also not correct in your code, because you are wasting 4 locations.
layout(location=0) in vec4 in_vertex;
layout(location=5) in vec2 in_texture;

In the unpacked format, both vec2 and vec4 consumes just one location. In your case, layout is like follows:
location 0: | in_vertex.x | in_vertex.y | in_vertex.z | in_vertex.w |
location 1: | < empty > | < empty > | < empty > | < empty > |
location 2: | < empty > | < empty > | < empty > | < empty > |
location 3: | < empty > | < empty > | < empty > | < empty > |
location 4: | < empty > | < empty > | < empty > | < empty > |
location 5: | in_texture.s | in_texture.t | < empty > | < empty > |

As you can see, there is a lot of wasted space. So, if you don't have a particular reason to define layout and reuse it across multiple shader-objects, It would be wiser to just let compiler do it, or read specifications to see how it actually works.
Happy coding! smile.png
[/quote]

I know it should be done only once, but I just have't came up with a system to do it :)
Thanks, I wasn't aware of this behaviour of the attributes, I'll fix this.
Well, I don't agree that getting uniforms' (or attributes') location is cumbersome, especially because that should be done only once, during the initialization phase.
I'm using class-wrapper for each shaders. Uniforms and attributes locations are attributes of the instance, and they are set in the Init() function (at the same place where I load and compile shaders, and link the program). After that, I just call appropriate function (method) of the particular object to set values. It cannot be easier.
It can be a lot easier -- this is one of the long-standing design faults in GL, which has only recently been addressed with UBOs and explicit uniform locations.

On SM2 and early SM3 GPUs, uniform variables (i.e. constants that can be modified by the host API) didn't actually exist in hardware; uniforms were implemented by using hard-coded literal values in the shader code, and having the host API patch/recompile the shader code with new literal values when necessary.
Given this hardware design, the "old" GL way of managing uniforms is a perfect fit -- each "instance" of a shader can have it's uinforms set to some values, and those values are a property of the "instance". Two instances can't share values, and whenever switching instances they each retain their own values that were set on them.

Late SM3 GPUs replaced this design by actually adding hardware support for uniform variables. Instead of hard-coded literals, uniforms are now offsets into a shared "uniform" register bank. Now when the API sets a shader program, it's uniforms will contain the values in the register bank, so the API must also issue many command packets instructing the GPU to first set those registers to the values that 'belong' to that program instance.
Now the API no longer matches the hardware -- a more accurate API would instead expose the idea of there being a register bank in which values can be stored, and of shader programs that do not store uniform values as properties.
Under this hardware design, if two shader programs use the same uniform layout, and require the same values, then no work needs to be done when switching programs -- however, because GL's API is mismatched, the driver is forced to do a lot of useless work (setting redundant values, or caching/checking all values to avoid this) and the user is also forced to do a lot of useless work (any values to be shared between two different 'shader instances' have to be set on both instances).
Also, if the user is able to specify the register layout, then they can greatly reduce the number of API calls by setting arrays of contiguous registers instead of each uniform individually.

If the user is given the ability to explicitly state uniform locations, and the ability to write values to the shared uniform registers (instead of writing values to shader instance) then a smart rendering engine can greatly reduce the amount of time spent in the GPU API/driver.

However, SM4/SM5 GPUs also gave us the alternative of UBO's, which give you the same idea of having "shaders without values" and a "uniform register bank", except each UBO instance is it's own "register bank" that can be used by any shaders of your choosing -- so the UBO API is superior to both the "old" method, and GL4.3's "Explicit uniform location" API (which was the model used by D3D9), and I'd recommend you use UBO's, if targeting GL3.1 and up.

On a side note -- on all other APIs, I can write tools to pre-compile both my shader code and my UBO values, so that all the shaders and uniform values for a specific scene full of objects/materials can be saved in a file, and loaded straight into memory with no parsing/compiling/processing steps, which makes the runtime code about as easy as it gets.

a more accurate API would instead expose the idea of there being a register bank in which values can be stored, and of shader programs that do not store uniform values as properties.
Under this hardware design, if two shader programs use the same uniform layout, and require the same values, then no work needs to be done when switching programs


It's interesting to note that the older ARB assembly extensions exposed this kind of setup (in an interesting manner, with both "local" and "env" parameters), so in many respects GLSL was actually quite a step backwards from even that (having to link vertex and fragment shaders into a program object was another example).

I believe that even with explicit uniform location GLSL still doesn't expose a way to just set a uniform once and have it available to all programs, short of using UBOs.

It's a case of: unless one has been exposed to something cleaner, neater and simpler, one really doesn't get the full appreciation of just how nasty, messy and awkward the way one is currently doing stuff really is.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.


I believe that even with explicit uniform location GLSL still doesn't expose a way to just set a uniform once and have it available to all programs, short of using UBOs.

I don't see what the problem is. Uniforms are the part of the program's state. They shouldn't be visible outside of the program and their state remains until they are explicitly changed for the particular program. There is nothing wrong with that. Explicit uniform location has nothing with sharing uniforms. It has the same purpose as explicit attrib. location. If uniforms have to be shared among multiple programs that's where UBOs should be used.

I don't see what the problem is. Uniforms are the part of the program's state. They shouldn't be visible outside of the program and their state remains until they are explicitly changed for the particular program. There is nothing wrong with that. Explicit uniform location has nothing with sharing uniforms. It has the same purpose as explicit attrib. location. If uniforms have to be shared among multiple programs that's where UBOs should be used.


Well that's more or less exactly what I said, if erring a little on the side of "the way GL does it is best because it's the way GL does it", but I'd suggest that you re-read Hodgman's post above for a description of what's wrong with it.

Now, I'm not going split hairs over which is the best approach from an API standpoint; one can easily produce test cases in which either approach can be shown to be preferable to the other and in fact my own opinion is that ARB assembly's concept of "local" and "env" parameters is close to what I'd consider ideal, and which unfortunately neither of the current high-level languages in either GL or D3D can cleanly replicate.

Of course you can write your own layer of additional boilerplate to handle all of it, but that's kinda missing the point of using an API, methinks, as it's functionality that each API should be providing.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.


Now, I'm not going split hairs over which is the best approach from an API standpoint; one can easily produce test cases in which either approach can be shown to be preferable to the other and in fact my own opinion is that ARB assembly's concept of "local" and "env" parameters is close to what I'd consider ideal, and which unfortunately neither of the current high-level languages in either GL or D3D can cleanly replicate.


I'm not sure the hardware would be happy with that anyway; given that all loads basically go via a L1/L2 cache system the idea of a 'register' across shaders is moot anyway.
Given the requirements for managing data upload the idea of being able to upload one bit of memory and reuse it doesn't seem like a great idea either.

Approiately sized and split cbuffer/UBOs where you can control the size and upload frequency is probably the best fit for the current hardware and will continue to be for a little while yet.

This topic is closed to new replies.

Advertisement