Oh boy here we go:
So here is how things works in OpenGL:
First we have uniform locations.
every uniform has a location and is how you refer to uniforms between the GPU and client code.
scalars, vector, and matrix types all use one location.
Array types and struct types use multiple locations: basically each element uses a location. so an array of 10 floats uses 10 locations. an array of 10 matrices also only uses 10 locations, despite matrices being bigger than floats.
So if you have 1024 locations, thats enough locations to store 1024 matrices.
this is guaranteed to be at least 1024 locations.
the actual value is returned by GL_MAX_UNIFORM_LOCATIONS.
Note: the location limit is per shader program (all stages combined). Not per stage.
However, there is another limit: the number of component each shader stages can have.
A component is basically a 4 byte word.
A float uses one component. A vec4 uses 4 component. A vec3 is probably only using 3 components.
A mat4 will use 16 components.
opengl3+ guarantees at least 1024 components per stage, but in practice, especially on newer hardware, this is higher.
returned by GL_MAX_<STAGE>_UNIFORM_COMPONENTS.
So, on opengl 3 capable hardware, you are guaranteed to be able to store 1024 floats per stage, provided you lay them out in a way that doesn't use more than 1024 locations.
(This is possible because each stage has its own separate component limit, but locations are shared between stages).
With this whole mess you can see why we moved away from uniforms and use interface blocks (uniform buffers) instead.
Its much easier to just declare a block and bind a buffer rather than mess with uniform locations and components.
Code:
layout(std140, binding = 0) uniform {
float anArray[1024];
vec4 bunchOfVecs[10];
}
Just one thing to keep track of: the binding of the buffer (and make sure your client side buffer layout matches std140).
I really wish GM would modernize a bit and support interface blocks. They were introduces over 11 years ago!