microsoft / DirectXShaderCompiler

This repo hosts the source for the DirectX Shader Compiler which is based on LLVM/Clang.
Other
3.08k stars 686 forks source link

Unexpected pixel shader results with -Od #3795

Closed jdswebb closed 2 years ago

jdswebb commented 3 years ago

Hi,

I have the following pixel shader (simplified down as much as I could to show the issue) that normalises input pixel coordinates for a 192x108 texture:

struct VertexOutput
{
    float4 position : SV_POSITION;
}

inline float normalizeUV(float u)
{ 
    return (u - 0.5f / 192.0f) * (192.0f / (192.0f - 1.0f));
}

inline float normalizeUV2(float u, float res)
{ 
    return (u - 0.5f / res) * (res / (res - 1.0f));
}

float4 PS(VertexOutput Input) : SV_TARGET
{
    float uvx = Input.position.x / 192.0;
    float x = normalizeUV(uvx);
    float y = normalizeUV2(uvx, 192.0f);
    float r = sqrt(1.0 - x * x);
    float g = sqrt(1.0 - y * y);
    return float4(r, g, x, y);
}

When compiling without specifying any optimisation flags or using -O3 it works as expected. Looking at the bottom right of the output in RenderDoc:

image

But when compiling with -Od, r != g when x==y==1. g == NaN rather than 0 as expected.:

image

The normalizeUV2 is significant here - switch it to normalizeUV and r==g even with -Od. I would have expected them to give the same results?

I'm using the latest nightly dxc build and have tried slightly older versions too.

Thanks

jaebaek commented 3 years ago

Did you generate SPIR-V and run a Vulkan application?

jdswebb commented 3 years ago

I'm targeting DXIL with these flags: -flegacy-macro-expansion -res-may-alias -WX.

I've not tried SPIR-V/Vulkan but I can do if it helps?

jaebaek commented 3 years ago

The code generations for DXIL and SPIR-V are independent. You do not need to try SPIR-V/Vulkan for this bug. I just wanted to clarify which backend you are interested in. Definitely, please report them if SPIR-V has similar issues.

adam-yang commented 2 years ago

Hi @jdswebb, the issue should be fixed with the latest compiler. The Od compilation used to produce two different versions of arithmetics for normalizeUV and normalizeUV2, which is no longer the case.

Looking at the output from the old compiler, the only difference between then and now is that the intermediate computateion steps used to be preserved, but it's not clear to me how y could have been > 1. It's possible that the backend compiler in the driver did some reordering that caused the problem. If you see any more issues that could be related to floating point arithmetics, you can try marking the variable as precise or use the /Gis flag to force strict IEEE floating point arithmetics to see if it helps, or be safe and clamp y between 0 and 1.