Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

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

Discussion in 'Shaders' started by fra3point, Nov 19, 2016.

  1. fra3point

    fra3point

    Joined:
    Aug 20, 2012
    Posts:
    267
    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:

    Code (csharp):
    1. for (j = 0; j < _SamplesCount; j++){
    2.     ...
    3. }
    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!!
     
    OMOH98 likes this.
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,238
    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
     
    bjarkeck, Sebik_, CyrilGhys and 5 others like this.
  3. fra3point

    fra3point

    Joined:
    Aug 20, 2012
    Posts:
    267
    @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:

    Code (CSharp):
    1. #pragma exclude_renderers d3d11_9x
    2. #pragma exclude_renderers d3d9
    What kind of GPUs can now use this shader? I mean, what limitations does this exclusion cause?
     
    chingwa and apelsinex like this.
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,238
    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).
     
    CyrilGhys and fra3point like this.
  5. fra3point

    fra3point

    Joined:
    Aug 20, 2012
    Posts:
    267
    OMOH98 likes this.
  6. AnthonyAckerman

    AnthonyAckerman

    Joined:
    Feb 20, 2015
    Posts:
    16
    This post saved my day. Thanks for this quick and easy solution!
     
    OMOH98 and twitchfactor like this.
  7. twitchfactor

    twitchfactor

    Joined:
    Mar 8, 2009
    Posts:
    356
    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!
     
  8. FM-Productions

    FM-Productions

    Joined:
    May 1, 2017
    Posts:
    72
    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.

    Code (CSharp):
    1. int iterationCount = min(_SamplesCount, 100);
    2. for (int i = 0; i < iterationCount; i++)
    3. {
    4.     // Logic here
    5. }
    You could also make a preprocessor definition for the maximal iteration count, for example like this:

    Code (CSharp):
    1. Properties
    2. {
    3.    // ... Property definition
    4. }      
    5. CGINCLUDE
    6. #define MAX_LOOP_ITERATIONS 100
    7. ENDCG