AMD's GLSL Compiler bug

I verified that it’s a driver bug.
A possible workaround is that swapping the order of the shaders that are using “struct lrec”.

Okey, thanks.

Workaround is work. But only data of first light properly read.
So I must wait for Catalist 10.12 with new year gifts :wink:

On the agenda the question - an array of structures is a bug or a feature?
The GLSL Specification does not clearly explain this.
Please, anyone can answer whether it is worth doing well or not? If not - then what alternative, simple array of structure without unifrom block or block per light or manual unrolling without array but static branching?

Definitely a feature, not a bug. The GLSL 4.0 spec says in the “Arrays” section “All basic types and structures can be formed into arrays.”.

And even better info on uniform arrays of structs can be found in the OpenGL specification:

If you look at the glGetActiveUniform man page, it explicitly calls out uniform arrays of structures, marking it valid, and telling you how they’re going to be handled (see “Uniform variables that are declared as structures or arrays of structures…”), and this is mirrored by language directly in the OpenGL spec.

So “uniform arrays of structs” are perfectly valid, and I wouldn’t just give up on this approach. And I agree from your example code that this form lays very well, compared to the alternatives I could think of.

I have tried “ordinary arrays of structures” (on NVidia with ordinary shader data) and it works well.

What I haven’t tried (or spent time getting working anyway) is what you’re doing: “uniform” arrays of structures. Have only tried passing in uniform arrays of scalar/vector types, and that works well.

I can also tell you that your code in this post compiles fine on the NVidia compiler.

If not - then what alternative, simple array of structure without unifrom block or block per light or manual unrolling without array but static branching?

Well, looks like what you have should work. I’ll plug this it up to code at some point and give it a whirl. I’ve you’ve already got a short GLUT test prog driver, post, and I’ll try here on NVidia.

If all else fails, I can tell you that the following kludgy work-around (compared to your clean solution) “does” work. That is, instead of array of structures, use multiple arrays of primitive scalar/vector types, one for each “struct” member:


const int NUM_LIGHTS = 16;

uniform vec4 LightWorldPosition[ NUM_LIGHTS ];
uniform vec4 LightAmbient      [ NUM_LIGHTS ];
uniform vec3 LightDiffuse      [ NUM_LIGHTS ];
...

And definitely works efficiently (on NVidia), without forcing manual loop unrolling.

(In the future, GLSL posts like this would probably be better placed in the GLSL forum, until the issue is established to be a vendor-specific bug/quirk.

One thing that is a little odd about uniform arrays of structs…

With uniform arrays of ordinary scalar/vector data, glGetActiveUniform says it just returns one entry per uniform array. So you can populate a whole array with a single uniform call, which is pretty efficient.

But with uniform arrays of “structs” it says:

which suggests you can’t set all the light.diffuse values with a single uniform call, you end up having individual sets for:

  • light[0].diffuse
  • light[1].diffuse
  • light[2].diffuse
  • light[0].specular
  • light[1].specular
  • light[2].specular

    (N calls for M elements = N*M sets to populate!!!)

I hope I’m reading this wrong because that seems pretty horrible. Anybody read this differently? Or is there another API I haven’t played with which is better tuned to discovering and populating uniform arrays of structures?

I hope I’m reading this wrong because that seems pretty horrible. Anybody read this differently? Or is there another API I haven’t played with which is better tuned to discovering and populating uniform arrays of structures?

I think you are reading it perfectly correctly, separate arrays are better than arrays of structs for uniforms to set bunches at the same time… to set an array of structs, the only thing that I can think of is uniform buffer objects (which is not the same thing)… oh wait…that was how this thread started :smiley:

Ok. Yeah, definitely a major disadvantage there. In a perfect world, you’d like to get light.diffuse as a uniform array you can set at once. And so on for light.specular, etc.

So on the multiple uniform arrays of primitive types vs. UBOs front, I think the main factor there is possibly speed, with normal uniform arrays allegedly leading, according to Ilian Dinev (random thread link), at least on NVidia.

An array of structure is certainly a feature and AMD supports it.
Some difference is that AMD driver has limitation on the uniform block array size. For example,
uniform block0
{

} B[size];
If the “size” is greater than we expected, the compiler will throw out an error.

New I try variant with multiple scalar/vector array in block

struct lrec {
vec4 WorldPosition;
vec3 Ambient; 
vec3 Diffuse; 
vec3 Specular; 
vec3 SpotDirection; 
float SpotExponent; 
float ConstantAtten; 
float LinearAtten; 
float QuadAtten;
float SpotCosCutoff;
};
		
layout(std140) uniform LightsBlock { 
vec4 WorldPosition[8];
vec4 Ambient[8]; 
vec4 Diffuse[8]; 
vec4 Specular[8]; 
vec4 SpotDirection[8]; 
float SpotExponent[8]; 
float ConstantAtten[8]; 
float LinearAtten[8]; 
float QuadAtten[8];
float SpotCosCutoff[8];
}; 

uniform int LightIndices[8];
  
lrec GetLight(int A)
{
  A = LightIndices[A];
  lrec B;
  B.WorldPosition = WorldPosition[A];
  B.Ambient = Ambient[A].rgb; 
  B.Diffuse = Diffuse[A].rgb; 
  B.Specular = Specular[A].rgb; 
  B.SpotDirection = SpotDirection[A].xyz; 
  B.SpotExponent = SpotExponent[A]; 
  B.ConstantAtten = ConstantAtten[A]; 
  B.LinearAtten = LinearAtten[A]; 
  B.QuadAtten = QuadAtten[A];
  B.SpotCosCutoff = SpotCosCutoff[A];  
  return B;  
}

Get block-offset for each uniform

uniform: vec4 WorldPosition    	Block offset: 0
uniform: vec4 Ambient           Block offset: 128
uniform: vec4 Diffuse           Block offset: 256
uniform: vec4 Specular          Block offset: 384
uniform: vec4 SpotDirection	Block offset: 512
uniform: float SpotExponent	Block offset: 640
uniform: float ConstantAtten    Block offset: 768
uniform: float LinearAtten      Block offset: 896
uniform: float QuadAtten        Block offset: 1024
uniform: float SpotCosCutoff    Block offset: 1152

Make similar structure for host

  
const
  MAX_HARDWARE_LIGHT = 8;
  TLightSourceState = packed record
    Position:      array[0..MAX_HARDWARE_LIGHT-1] of TVector;  
    Ambient:       array[0..MAX_HARDWARE_LIGHT-1] of TVector;  
    Diffuse:       array[0..MAX_HARDWARE_LIGHT-1] of TVector;  
    Specular:      array[0..MAX_HARDWARE_LIGHT-1] of TVector; 
    SpotDirection: array[0..MAX_HARDWARE_LIGHT-1] of TAffineVector; 
    Space1:        array[0..MAX_HARDWARE_LIGHT-1] of Single;
    SpotExponent:  array[0..MAX_HARDWARE_LIGHT-1] of Single;
    Space2:        array[0..MAX_HARDWARE_LIGHT-1] of TAffineVector;
    ConstantAtten: array[0..MAX_HARDWARE_LIGHT-1] of Single;
    Space3:        array[0..MAX_HARDWARE_LIGHT-1] of TAffineVector;
    LinearAtten:   array[0..MAX_HARDWARE_LIGHT-1] of Single;  
    Space4:        array[0..MAX_HARDWARE_LIGHT-1] of TAffineVector;
    QuadAtten:     array[0..MAX_HARDWARE_LIGHT-1] of Single;  
    Space5:        array[0..MAX_HARDWARE_LIGHT-1] of TAffineVector;
    SpotCosCutoff: array[0..MAX_HARDWARE_LIGHT-1] of Single;
    Space6:        array[0..MAX_HARDWARE_LIGHT-1] of TAffineVector;
  end;

Check host and device block size - it’s both equal 1280 byte.

In result
properly read only first light, with other looks like properties fetched with wrong offset
not GL error, no shader error

Ops :o
I made mistake in host structure. All member must be TVector (vec4)

All right. Multiple vector arrays in uniform block work as need.
Thanks to Dark Photon for idea.

I make two test (on Cedar VGA). Data was update once per frame (1280 byte)

  1. uniform arrays without block - 220 fps
  2. uniform arrays in block - 270 fps

Mmm, I packed scalar properties to vectors, that reduce block size to 896 byte and increase FPS to 300 for four lights. For one light it value ~550. Flooding off in topic…

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.