4
\$\begingroup\$

Here is my attemps to pass array to uniform array:

struct Vector{ float x,y,z; }

float threshold[2] = { 0.5, 0.25 };
Vector *kernel = new Vector[_kernel_size]; // _kernel_size==16
// fill kernel

glUniform1fv(glGetUniformLocation(_program, "t"), 2, threshold);
glUniform3fv(glGetUniformLocation(_program, "kernel"), _kernel_size, (const GLfloat*)kernel); // because Vector contains only 3 float fields this kind of casting should be ok

shader:

uniform float t[2];
uniform vec3 kernel[16];

And the results are weird. Only first float or first vector are filled with proper values. For example:

t = {0.5, 7.1830559e-042} 

Even when I try change only one value (name = "t[1]") it doesn't work. I checked this on AMD CodeXL.

My graphics card is Radeon HD 5770 and I have the newest drivers. I'm using OpenGL 3.3 and GLSL 330.

What am I doing wrong?

\$\endgroup\$
  • \$\begingroup\$ This struct struct Vector{ float x,y,z; } seems wrong to me. Try struct Vector {float x; float y; float z;} \$\endgroup\$ – Dan Feb 18 '15 at 6:01
  • \$\begingroup\$ Also, what is value? Should it be kernel what you want to pass to the shader? \$\endgroup\$ – Dan Feb 18 '15 at 6:03
  • \$\begingroup\$ Yes, it should be kernel. I made mistake because I have glUniforms wrapped in functions and I copy, pase and edit this fragment :) I'll try with struct but I tried cast my kernel to const float* and then in 0.._kernel_size*3 loop I iterated over this array and values were good. \$\endgroup\$ – Harry Feb 18 '15 at 9:03
  • \$\begingroup\$ @Dan The struct definition is fine, you can declare/initialize multiple variables of the same type together. \$\endgroup\$ – aslg Jul 31 '15 at 23:19
  • 1
    \$\begingroup\$ What are the return codes from glUniform()? \$\endgroup\$ – John K Sep 30 '15 at 19:22
1
\$\begingroup\$

Try using glUniform2fv on threshold.

\$\endgroup\$
  • \$\begingroup\$ Result is the same: Active Uniforms: t[0] Type: GL_FLOAT Value: {0,5, 7,1830559e-042} \$\endgroup\$ – Harry Feb 17 '15 at 21:46
  • \$\begingroup\$ glUniform2fv is is for updating arrays of vec2, ivec2, uvec2, bvec2, etc.. glUniform1fv is definitely correct. \$\endgroup\$ – John K Sep 30 '15 at 19:20
1
\$\begingroup\$

I checked another simple project where passing array to uniform works and checked what CodeXL is showing. And again only first value was ok.

So I believe that this is some kind of bug/limited functionality. Maybe name of uniform which CodeXL shows (kernel[0]) is a hint that only first value is presented.

\$\endgroup\$
1
\$\begingroup\$

You have to send the data to the first address of the array, e.g. t[0]

glUniform1fv(glGetUniformLocation(_program, "t[0]"), 2, threshold);
\$\endgroup\$
  • \$\begingroup\$ This won't change anything. From the spec: ...the location of the first element of an array can be retrieved by using the name of the array, or by using the name appended by "[0]" \$\endgroup\$ – John K Sep 30 '15 at 19:09
  • \$\begingroup\$ just using the name for an array never worked for me on windows using nvidia drivers. \$\endgroup\$ – Raphael Mayer Oct 1 '15 at 10:29
1
\$\begingroup\$

Try switching every arrays to vec4:

uniform vec4 t[2];
uniform vec4 kernel[16];

And use t[i].x and kernel[i].xyz in the shader.

Upload them as vec4 on the CPU side as well (don't forget padding with w)

struct Vector{ float x,y,z,w; }

float threshold[2][4] = { {0.5f}, {0.25f} };
Vector *kernel = new Vector[_kernel_size]; // _kernel_size==16

// fill kernel

glUniform4fv(glGetUniformLocation(_program, "t"), 2, threshold);
glUniform4fv(glGetUniformLocation(_program, "kernel"), _kernel_size, (const GLfloat*)kernel);

It's possible the GPU and drivers only properly supports arrays of vec4 due to a hardware limitation.

Alternatively, you can use t.x and t.y as the two thresholds in the shader:

uniform vec4 t;

CPU side:

float threshold[4] = { 0.5f, 0.25f };
glUniform4fv(glGetUniformLocation(_program, "t"), 1, threshold);

If that doesn't work try uploading each uniform one by one.

glUniform4fv(glGetUniformLocation(_program, "kernel[0]"), 1, kernel+0);
glUniform4fv(glGetUniformLocation(_program, "kernel[1]"), 1, kernel+1);
...

(for-loop left out for brevity)

Mobile GPUs (ie: cell phones) tend to be scalar-based (not vector) and can access floats, vec2s and vec4s just fine. Avoid vec3 uniforms but using them as temporaries in the shader code is fine and will be faster. Use lower precisions / fixed points when possible on Mobile.

Desktop GPUs vary. Some are vector4-only internally and the driver has to put floats into vec4s. Some can access scalar uniforms at any address with a slight performance penalty.

If you have multiple arrays of floats it's a good idea to interleave them if you can

For example:

uniform vec4 table[8];

table[i].x // hue shift table
table[i].y // blur factor table
table[i].z // de-saturation factor table
// etc

If you can, invest a few bucks in getting test computers with GPUs from other manufacturers to make sure your code and shaders work on most, test regularly.

They all have quirks, some more than others.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.