0
$\begingroup$

My question is fairly simple but I guess it contains two questions, one is how to arrive at an answer (imagine a similar one and you want to deduce it using existing tools like a GLSL compiler or the like, I wasn't able to as I couldn't find any loop unrolling on trivial loops from the compilers I found online) and the question per se is: at what point does doing arithmetic/logic on a uniform becomes non trivial for the driver and stuff like loop unrolling stops happening. Imagine the scenario were you have a uniform on which the kernel size of a convolution depends on, something like


int maxIter = fract( log2( float( whatever ) ) ) == 0. ? 3 : 2;

for (int i = -maxIter; i <= maxIter; ++i) 
{ 

will this get unrolled? I am fairly certain that simply having a min( 0, whatever ); won't unroll the loop as the compiler takes the uniform as a blackbox if I am correct.

$\endgroup$
1
$\begingroup$

It really depends on the individual compiler. The GPU vendor is responsible for the compiler as part of their driver, so it's really a total crapshoot as to how things get optimized, and you would need vendor-specific tools (if available) to get the compiled shader and disassemble it.

If the GLSL is compiled to SPIR-V (i.e. for Vulkan) then you can at least look at the SPIR-V and see what optimizations were done at that level. If the loop gets unrolled by the SPIR-V compiler, it's unlikely that it would be re-rolled by the driver compiler. However, ultimately when it's submitted to the driver it will still go through another compile stage which may transform the code further, so whatever you see in the SPIR-V isn't necessarily definitive.

In general, you're right that the compiler has to assume that uniform values are arbitrary. So if, for instance, you loop from 0 up to some 32-bit int uniform value, then it won't be able to unroll the loop as it could have in principle billions of iterations.

Sometimes, if you put a compiler-visible upper bound on the loop count then the compiler will be able to unroll it:

uniform int iterCount;
...
int count = min(iterCount, 4);
for (int i = 0; i < count; ++i)
{
    ...
}

Here, it's possible for the compiler to prove that the count can't be more than 4, so it would be safe to unroll the loop 4x, with conditionals to jump over parts of it based on the actual value of iterCount. But again, whether the compiler actually does that in any given instance is going to be down to the individual compiler's analysis and heuristics. Generally if the loop body is long, or the iteration count is high, it will be less profitable to unroll it.

By the way, in your example the value of maxIter is always either 2 or 3, and so the loop count is either 5 or 7, and the compiler should be able to see that, so it would be a reasonable expectation that it could unroll that loop if it decides it's a good idea.

Also, if you have access to GL_EXT_control_flow_attributes then you can mark up loops with the [[unroll]] annotation to explicitly request the compiler to unroll it—although the compiler may still refuse to honor that if it doesn't have a compiler-visible upper bound.

$\endgroup$
1
  • $\begingroup$ Marked as the correct answer but I guess it didn't really solve the question per se, not because the answer wasn't well structured but because hardware vendors don't disclose their compilers AFAIK, so basically proved what I thought. The Spir-V trick might be handy though. Regards $\endgroup$ 17 hours ago

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.