Compiling shaders with large loops causes "unable to unroll" error

Hi, everyone!

I thought about it a lot before creating this thread because I read a lot of similar things here in the Forums and in the Internet, but I couldn’t find a solution.

Some months ago I wrote a shader that samples a texture multiple times (up to 360) accordingly to a float property.
It works fine in the Editor but when the Buld process compiles it, it always give me this error:

“Shader error in ‘MyShader’: unable to unroll loop, loop does not appear to terminate in a timely manner (15 iterations) or unrolled loop is too large, use the [unroll(n)] attribute to force an exact higher number at line 111 (on d3d11_9x)”

Of course I tried to add [unroll(360)] before the for loop but it didn’t help.
I read a lot of random stuff about loop unrolling during shader compiling and it seems that it’s impossible to compile a dynamic for loop. My for loop looks like this:

for (j = 0; j < _SamplesCount; j++){
    ...
}

where _SamplesCount is just a float property the users can set with Material.SetFloat();
I can’t use a static value in the loop because the wanted effect strictly depends on the _SamplesCount value.

So the question is:
How to make a shader with large dynamic loops compile under d3d?

I hope someone could help me!!

1 Like

The problem is DX9 & ShaderModel 3.0 don’t handle dynamic loop lengths well, so unrolled loops are generally better / faster and the compiler will try to unroll most of the time. However sometimes that’s not really an option, like when unrolling a loop makes a shader more than 65k instructions, or some other both artificial and spec limitations. Instead of [unroll(#)] you can try using [loop] which forces it to not attempt to unroll. The other option is to skip DX9 support entirely, or maybe just “Direct3D 11 9.x feature level” which is for older Windows App Store applications (i.e.: Windows Phone & Surface RT) which is oddly sometimes more limited than actual DX9. Try:

#pragma exclude_renderers d3d11_9x

9 Likes

@bgolus Hey man, you saved my life once again. You deserve a medal!!!

I excluded Direct3D 11 9.x feature level and d3d9 as following:

#pragma exclude_renderers d3d11_9x
#pragma exclude_renderers d3d9

What kind of GPUs can now use this shader? I mean, what limitations does this exclusion cause?

4 Likes

As mentioned dropping d3d11_9x excludes windows phones and the Surface RT, d3d9 drops support for computers 6~7 years old. However if require a shader with a loop as big as you want a GPU from 6 years ago isn’t likely to have the oomph to run your game anyway.

GeForce 300 series or older (2009), Radeon HD 4000 series or older (2010), or Intel HD 2500 / Core i5-3xxx or older (2010).

4 Likes

@bgolus Thanks very much!!! :slight_smile:

2 Likes

This post saved my day. Thanks for this quick and easy solution!

2 Likes

So glad I found this post, since the developer who put out the errant asset on the store doesn’t seem interested in fixing their product. Thanks again!

Hi!
I had a similar issue recently and found this thread, but I also found a solution that doesn’t require exclusion of render targets, by using the min function with a constant value. If you have an int value called _SamplesCount that you set in the material inspector, you can do the following and the compiler is smart enough to understand the maximal iterations that the loop can have, and will compile if the iteration number is below a certain threshold.I use 100 as an example value for the maximal iteration count.

int iterationCount = min(_SamplesCount, 100);
for (int i = 0; i < iterationCount; i++)
{
    // Logic here
}

You could also make a preprocessor definition for the maximal iteration count, for example like this:

Properties
{
   // ... Property definition
}       
CGINCLUDE
#define MAX_LOOP_ITERATIONS 100
ENDCG
6 Likes

I know this is totally random, but I had this exact same issue, but it was because I did not initialize a value that was used deep within the loop. I.e. I had StructName s; instead of StructName s = (StructName)0; preceeding the loop. Sadly, just adding the two exclusions outlined above didn’t work for me.

1 Like