PDA

View Full Version : How many uniforms?



Harvey_Birdman
08-12-2015, 02:14 PM
Hi, all -

I'm puzzled by a really basic issue - determining how many uniform variables I can pass to a fragment shader. I've been working my way through the OpenGL 4 Shading Language Cookbook, and have run into an issue that seems related to the number of uniforms I'm passing. It seems I can only pass 16 variables to a fragment shader - the 17th never seems to make it. I've tried querying every parameter I can think of related to uniform capacity - GL_MAX_FRAGMENT_UNIFORM_COMPONENTS = 2048, etc. Nowhere is there an indication that the number of named uniforms (not in a block) is limited to 16; at least, not that I can find.

So what am I missing?

Thanks in advance.

Alfonse Reinheart
08-12-2015, 10:59 PM
It seems I can only pass 16 variables to a fragment shader - the 17th never seems to make it.

Well, without knowing what exactly that means, we can't really help you fix the problem. In what way does it not "seem to make it"? Does shader compilation fail? Does it succeed, but the value is unavailable? What does your code look like?

Harvey_Birdman
08-13-2015, 12:24 PM
Compilation succeeds, and all 17 variables are listed as active uniforms after linking. The value apparently never makes it to the shader. Most uniforms are routine matrix, vector, and simple scalar types, along with two sampler2D's; no uniform blocks involved. The one that fails to make it is the last in the sorted list (sorted by index) of 17 declared in the shader.

The line of code in question is part of a tone mapping algorithm (if you have the book, it's the 'White' uniform in the Bloom effect shader). I determined the line at fault (second line of a luminance calculation in the fifth and final rendering pass), substituted a reasonable hard-coded value in place of the variable in question, and lo and behold, a reasonable output. I next removed one of the other 17 uniforms (an array of 10 floats that are always the same - blur coefficients) and replaced it with hard-coded values in the shader, and re-instituted the 'White' uniform instead of a hard-coded value. And problem is completely solved.

So it seems that the implication was that I could only pass 16 independent uniforms to the fragment shader. Seems like a mighty low number, given the reassuringly large values for GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, etc. (The total number of components in play doesn't begin to approach the limit.)

So I'm puzzled - I can't think of a reason why I shouldn't have been able to load a value in the 17th uniform.

Alfonse Reinheart
08-13-2015, 02:59 PM
I'm really not sure what you expect us to do. You won't post the code for us to actually look at it, so we have absolutely no way of diagnosing the problem.

arekkusu
08-13-2015, 03:15 PM
I would do two different things to diagnose:

1) CPU introspection. Use glGetProgram* and glGetUniform* to verify that all the uniform values you think you've set are actually set. Or, use your favorite debugger to just inspect the program state and the current uniform values at your draw call. If your inputs are bad, then outputs can't work.

2) GPU introspection. While keeping all of the uniforms active (i.e. used and actually written into an output), write one component to an output channel, and ReadPixels from an appropriate render target to verify the real value. Repeat for each uniform, then you know what the GPU values really are, not just "can't pass the value". Maybe the real value is NaN or something.

Harvey_Birdman
08-13-2015, 03:37 PM
@Alfonse -

Dude.

I can post the code, if you feel it's important.

The code works. If you'd read what I've written, I'm not asking for help with the code.

What I'm asking is if there is some other interrogative besides GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, or GL_MAX_UNIFORM_LOCATIONS, etc, which might define the number of independent (non-block) uniforms available.

That's all I'm asking. It doesn't require a code sample. All the code samples in the world aren't going to provide an answer to the question.

Perhaps I didn't state it clearly enough.

Is there some other parameter (besides the ones I've already mentioned) I might use with glGetxx, or getProgramiv, or some other interrogative, that would define a limit on the number of available uniform slots?

@arekkusu

Thanks - that's helpful. glGetUniform is worth investigating. Like I said, though, if I limit the number of uniforms to 16, all work. If I add a 17th, it behaves as if it's value is undefined - the one line of code where it's used gives invalid results. If I remove a different uniform, returning the number of active uniforms to 16, the last one, the one that wouldn't work when it was 17th in line, now works.

And since you seem to have actually read what I've written, here's the code. This is the working variant - there are only 16 uniforms, and it works as intended. If I modify the code such that the array of floats 'Weight[10]' is a uniform, that will make a total of 17, and the last one assigned an index when the program is compiled is 'White', and I can no longer pass a value to it.

So. Is there some interrogative that would reveal a relevant limit to the number of uniforms available?

(And did seeing the code really help?)


(EDIT - If there is no other relevant limit, I'll look elsewhere for a problem. As I said, the implication seemed to be that there was some limit at 16 uniforms. (Just as the GPU in question limits the number of vertex attributes to 16.) Maybe that isn't the issue, though - maybe it's something else. And that's fine - I'm willing to spend some time investigating. I just thought that maybe there was some defined limit that I was unaware of, and which one of you might know about.)






#version 330 core

#pragma debug(on)
#pragma optimize(off)


in vec3 Position;
in vec3 Normal;
in vec2 TexCoords;

uniform int RenderPass;
uniform sampler2D HDRTex;
uniform sampler2D FilterTex;

uniform float White;
uniform float Exposure;
uniform float AveLum;

uniform float LumThreshold;

struct LightInfo
{
vec4 Position; // Light position in eye coords.
vec3 Intensity; // Light intensity
};

uniform LightInfo Light;

struct MaterialInfo
{
vec3 Ka; // Ambient reflectivity
vec3 Kd; // Diffuse reflectivity
vec3 Ks; // Specular reflectivity
float Shininess; // Specular shininess factor
};

uniform MaterialInfo Material;




layout( location = 0 ) out vec4 FragColor;


float Weight[10] = float[] (0.084612906, 0.082937457, 0.078107551, 0.070674635, 0.061441574,
0.051320318, 0.041185521, 0.031756159, 0.023525544, 0.016744783);

float PixOffset[10] = float[](0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0);

mat3 rgb2xyz = mat3(
0.4124564, 0.2126729, 0.0193339,
0.3575761, 0.7151522, 0.1191920,
0.1804375, 0.0721750, 0.9503041 );

mat3 xyz2rgb = mat3(
3.2404542, -0.9692660, 0.0556434,
-1.5371385, 1.8760108, -0.2040259,
-0.4985314, 0.0415560, 1.0572252 );


float luminance( vec3 color )
{
return 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
}


vec3 phongModel( )
{
vec3 n = normalize( Normal );
vec3 s = normalize( vec3( Light.Position) - Position );
vec3 v = normalize( vec3(-Position));

vec3 total = vec3(0.0f, 0.0f, 0.0f);

vec3 r = reflect( -s, n );

//for (int i = 0; i < 3; i++)
total += (Light.Intensity * ( Material.Ka + Material.Kd * max( dot( s, n), 0.0 ) +
Material.Ks * pow( max( dot( r, v), 0.0 ), Material.Shininess ) ));

return(total);
}

void main()
{

vec4 texColor;
vec4 texColor2;
vec2 tc = TexCoords;

vec4 sum;

tc.x *= -1.0;
tc.x += 1.0;

switch(RenderPass)
{

case 1:
FragColor = vec4(phongModel(), 1.0);
break;

case 2:
texColor = texture(HDRTex, tc);

if (luminance(texColor.rgb) > LumThreshold)
FragColor = texColor;
else
FragColor = vec4(0.0);

break;

case 3:

float dy = 1.0 / (textureSize(FilterTex, 0)).y;

sum = texture(FilterTex, tc) * Weight[0];
for( int i = 1; i < 10; i++ )
{
sum += texture( FilterTex, tc + vec2(0.0,PixOffset[i]) * dy ) * Weight[i];
sum += texture( FilterTex, tc - vec2(0.0,PixOffset[i]) * dy ) * Weight[i];
}

FragColor = sum;

break;

case 4:

float dx = 1.0 / (textureSize(FilterTex,0)).x;

sum = texture(FilterTex, tc) * Weight[0];
for( int i = 1; i < 10; i++ )
{
sum += texture( FilterTex, tc + vec2(PixOffset[i],0.0) * dx ) * Weight[i];
sum += texture( FilterTex, tc - vec2(PixOffset[i],0.0) * dx ) * Weight[i];
}

FragColor = sum ;

break;

case 5:

///////////////// Tone mapping ///////////////
//// Retrieve high-res color from texture
vec4 color = texture( HDRTex, tc );
vec4 blurTex = texture(FilterTex, tc);


// Convert to XYZ
vec3 xyzCol = rgb2xyz * vec3( color);

// Convert to xyY
float xyzSum = xyzCol.x + xyzCol.y + xyzCol.z;

vec3 xyYCol = vec3( 0.0);

if( xyzSum > 0.0 ) // Avoid divide by zero
xyYCol = vec3( xyzCol.x / xyzSum, xyzCol.y / xyzSum, xyzCol.y);

// Apply the tone mapping operation to the luminance
// (xyYCol.z or xyzCol.y)
float L = (Exposure * xyYCol.z) / AveLum;
L = (L * ( 1.0 + L / (White * White) )) / ( 1.0 + L );

// Using the new luminance, convert back to XYZ
if( xyYCol.y > 0.0 )
{
xyzCol.x = (L * xyYCol.x) / (xyYCol.y);
xyzCol.y = L;
xyzCol.z = (L * (1.0 - xyYCol.x - xyYCol.y))/ xyYCol.y;
}


////// Convert back to RGB
vec4 toneMapColor = vec4(( xyz2rgb * xyzCol), 1.0);

/////////////// Combine with blurred texture /////////////
//// We want linear filtering on this texture access so that
//// we get additional blurring.

FragColor = toneMapColor + blurTex;


break;

}
}

arekkusu
08-13-2015, 04:27 PM
If I drop your shader (with dummy vertex passthrough) into some utility code I have laying around here, my link introspection looks like this:



13 uniforms:
Exposure @0 1xFLOAT
White @1 1xFLOAT
Light.Position @2 1xFLOAT_VEC4
Light.Intensity @3 1xFLOAT_VEC3
HDRTex @4 1xSAMPLER_2D
LumThreshold @5 1xFLOAT
AveLum @6 1xFLOAT
Material.Ka @7 1xFLOAT_VEC3
Material.Kd @8 1xFLOAT_VEC3
Material.Ks @9 1xFLOAT_VEC3
Material.Shininess @10 1xFLOAT
RenderPass @11 1xINT
FilterTex @12 1xSAMPLER_2D


And, after changing "Weight" to a uniform array:


14 uniforms:
Exposure @0 1xFLOAT
White @1 1xFLOAT
Light.Position @2 1xFLOAT_VEC4
Light.Intensity @3 1xFLOAT_VEC3
HDRTex @4 1xSAMPLER_2D
Weight[0] @5 10xFLOAT
LumThreshold @15 1xFLOAT
Material.Ka @16 1xFLOAT_VEC3
Material.Kd @17 1xFLOAT_VEC3
Material.Ks @18 1xFLOAT_VEC3
Material.Shininess @19 1xFLOAT
AveLum @20 1xFLOAT
RenderPass @21 1xINT
FilterTex @22 1xSAMPLER_2D


So-- just to double check the obvious, in your CPU code setting uniforms, you're not confusing active uniform indices with the active uniform locations, are you? Remember, the locations are effectively random numbers (typically, contiguously packed starting at zero, but... don't assume anything...)




So. Is there some interrogative that would reveal a relevant limit to the number of uniforms available?

No, MAX_FRAGMENT_UNIFORM_COMPONENTS is correct.

Harvey_Birdman
08-13-2015, 05:02 PM
Hey -

The uniform count is correct - the missing three are the matrices passed into the vertex shader.

And I wonder if you might not have hit on the problem... there is indeed the possibility that the program wrapper class is erroneously mapping indices to the uniform variable data, instead of location. That sounds like a reasonable place to look. It has to be something painfully obvious to someone else, you know?

I'm off to check...

Harvey_Birdman
08-13-2015, 05:26 PM
@arekkusu -

That was it!! I've been living with that little bug in my program wrapper class for the last couple of months while learning GLSL, and this was the first time I'd used a large enough number of uniforms to expose it. The locations are identical to the indices for the first 16 values, and the 17th is different.

Outstanding call, dude. Many thanks.

:biggrin-new:

arekkusu
08-13-2015, 06:33 PM
The locations are identical to the indices for the first 16 values, and the 17th is different.

Even that is just coincidence. Run your app on another driver or OS and you'll get different results. ;)

Harvey_Birdman
08-13-2015, 07:02 PM
I understand.

And thanks for clarifying the role of MAX_FRAGMENT_UNIFORM_COMPONENTS.

:cool: