# Do the alignement and declaration order of uniforms matter?

In the section 6.4 Constant Buffers of the book Practical Rendering & Computation with Direct3D 11 (pages 325, 326) it is mentioned:

By default, the HLSL compiler will attempt to align constants such that they don't span multiple float4 registers. [...] The packing for an HLSL constant buffer can also be manually specified through the packoffset keyword.

I assume a similar rule will apply to the OpenGL equivalent, Uniform Buffer Objects, since they map to the same hardware feature.

What about vanilla uniforms though? What are the rules that apply when declaring uniforms?

uniform vec2 xy; // Can we expect the compiler to pack xy
uniform vec2 zw; // into a same four component register?

uniform vec2 rg;
uniform float foo; // Will this prevent from packing rg and ba?
uniform vec2 ba;   // If so, will foo eat up a full four components register?


If the compiler can do such optimizations, how good are they? Can we explicitly tell the compiler to pack or not, and when should we?

I went looking for an answer, so I downloaded AMD's shader analyzer to view the assembly produced when compiled for GCN. In the assembly below vector registers are v# and scalar registers are s#.

It would appear that the uniforms even vector uniforms are passed into the shader as separate scalars, so a vec3 would use 3 scalar registers. The bit I found confusing was the v0 to v4, I'm not sure if v0 is a full 4 float register or a single float in a register, with a full vector register spanning v0 to v3. One way or another it didn't appear to change between the two versions so I can assume the definition order didn't affect the assembly.

http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/07/AMD_GCN3_Instruction_Set_Architecture.pdf

#version 450

uniform vec2 xy;
uniform vec2 zw;

out vec4 v;

void main(){
v.xy = xy;
v.zw = zw;
}

asic(VI)
type(VS)

v_mov_b32     v0, 0
v_mov_b32     v1, 1.0
exp           pos0, v0, v0, v0, v1 done
s_andn2_b32   s0, s5, 0x3fff0000
s_mov_b32     s1, s0
s_mov_b32     s2, s6
s_mov_b32     s3, s7
s_mov_b32     s0, s4
s_waitcnt     expcnt(0) & lgkmcnt(0)
v_mov_b32     v0, s4
v_mov_b32     v1, s5
v_mov_b32     v2, s0
v_mov_b32     v3, s1
exp           param0, v0, v1, v2, v3
end

#version 450

uniform vec2 xy;
uniform float z;
uniform vec2 zw;

out vec4 v;

void main(){
v.xy = xy;
v.zw = zw;
v.w += z;
}

asic(VI)
type(VS)

v_mov_b32     v0, 0
v_mov_b32     v1, 1.0
s_andn2_b32   s0, s5, 0x3fff0000
exp           pos0, v0, v0, v0, v1 done
s_mov_b32     s1, s0
s_mov_b32     s2, s6
s_mov_b32     s3, s7
s_mov_b32     s0, s4

• The definition order did affect the layout. The relevant part here is the s_buffer_load_dword instructions - those are reading the input uniforms, and the last number in hex is the offset to read from. It shows in the first case xy is at offset 0 and zw at offset 16. In the second case you have xy at offset 0, z at offset 16, and zw at offset 32. It appears all the uniforms are individually 16-byte-aligned, and not packed together or reordered. – Nathan Reed Oct 6 '15 at 17:27