Direct3D11: "gradient instruction used in a loop with varying iteration, forcing loop to unroll", warning: X3570

Viewed 3917

I'm working on a graphics engine using Direct3D 11 and Visual Studio 2015. In the HLSL shaders for the main draw calls, I sample shadow maps for directional and point lights with percentage-closer-filtering, i.e. I sample a small square area around the target shadow map texel and average the results to get soft shadows. Now, every call to shadowMap_.Sample(...) creates a warning: "gradient instruction used in a loop with varying iteration, forcing loop to unroll" (X3570). I want to fix this or, if that is not possible, hide the warning as it completely floods my warning output.

I tried searching online for the error message and couldn't find any further descriptions. I couldn't even find an explanation what a gradient instruction is supposed to be. I checked the Microsoft documentation for a different sampler or sampling function that lets me replace the loop with native sampling functionality, but didn't find anything like that either. Here is the function I use for sampling my shadow cube maps for point lights:

float getPointShadowValue(in uint index, in float3 worldPosition)
{
    // (Half-)Radius for percentage closer filtering
    int hFilterRadius = 2;

    // Calculate the vector inside the cube that points to the fragment
    float3 fragToLight = worldPosition.xyz - pointEmitters_[index].position.xyz;

    // Calculate the depth of the current fragment
    float currentDepth = length(fragToLight);

    float sum = 0.0;
    for (float z = -hFilterRadius; z <= hFilterRadius; z++)
    {
        for (float y = -hFilterRadius; y <= hFilterRadius; y++)
        {
            for (float x = -hFilterRadius; x <= hFilterRadius; x++)
            {
                // Offset the currently targeted cube map texel and sample at that position
                float3 off = float3(x, y, z) * 0.05;
                float closestDepth = pointShadowMaps_.Sample(sampler_, float4(fragToLight + off, index)).x * farPlane_;
                sum += (currentDepth - 0.1 > closestDepth ? 1.0 : 0.0);
            }
        }
    }
    // Calculate the average and return the shadow value clamped to [0, 1]
    float shadow = sum / (pow(hFilterRadius * 2 + 1, 3));
    return min(shadow, 1.0);
}

The code still works fine as it is, but I get a huge amount of these warnings and don't know if this causes a relevant performance impact. Any further information about the warning and what can be done about it is greatly appreciated.

Thanks in advance.

2 Answers

Gradient function are all texture sampling methods which are determine the used mip-level by themselves, such as your used method Sample. Therefore they use the ddx (doc) and ddy(doc) internally. Fragments are computed on the gpu in 2x2 chunks, so they can compare the difference in the texture coordinate with each other. The larger the difference the higher mip-map-level is used. With dynamic branching this method no longer works as it is not assured that each fragment uses the same computation path, so gradient functions don't work within dynamic branches. As loops are using branching, the compiler has to make them static to use gradient functions. This is done by unrolling in you case as the loops are always the same. The compiler already detected it and compiles your loops with writing all steps after another automatically to make non-branching code. With the [unroll](doc) statement you can hint the compiler to do so and suppressing warnings.

Another way for your code would be to use sampling methods which aren't gradient functions, such as SampleLevel (doc) where you pass the desired mip-map-level (0 in your case as you shadow map doesn't have mip-map-levels) and the gpu doesn't have to determine it. As far as I know the performance impact is negligible as this happens on a very low level where most functions are processed equally fast on the gpu, but perhaps you should do your own tests.

One addition which not applies to you case, but a further non gradient method to fetch textels is Load (doc) to directly fetch a specific texel by the integer texel index.

As Chuck Walbourn already stated, adding an [unroll] statement before the for loops fixes the warnings. This type of warning is basically the compiler informing you that a loop can't be unrolled or it would be less performant to do so (as can be read in the Microsoft documentation for the HLSL for-loop). I assume this can be safely accepted.

Related