cancel
Showing results for 
Search instead for 
Did you mean: 

PC Graphics

Saniajuneor
Journeyman III

Compiler Bug on AMD Radeon 6400 GPU

Summary


The AMD Radeon RX 6400 miscompiles an HLSL pixel shader program and produces incorrect rendering effects. The built-in HLSL function `frac` calculates fractional part of a floating-point number, i.e., `frac(x) = x - floor(x)`. For example, `frac(1.6) = 0.6`, `frac(0.7) = 0.7`. However, in an HLSL program, the compiler incorrectly reasons that `frac(x)` is zero, even though the actual value of `frac(x)` is 0.4.

Configurations


- Operating System: Windows 10 Pro, version 21H2, OS build: 19044.1826

- Hardware:

  CPU:  12th Gen Intel(R) Core(TM) i5-12400   2.50 GHz

  RAM:  32.0 GB

  System type:  64-bit operating system, x64-based processor
 
  GPU:
  1. Intel(R) UHD Graphics 730, with driver version 30.0.101.1994
  2. AMD Radeon RX 6400, with driver version 30.0.15021.11005 (used during the experiment)

- Software:

  Direct3D 11 with Shader Model 5

  IDE: Microsoft Visual Studio Community 2022 (64-bit) Version 17.2.6

  Browser: Chrome Version 104.0.5112.81 (Official Build) (64-bit)


A quick way to reproduce the bug in the browser

Prerequisite: Windows system, Chrome, AMD Radeon 6400
Idea: the Chrome browser by default used Direct3D for executing OpenGL ES shader programs on Windows systems. We can utilize Chrome to quickly reproduce the bug in HLSL compiler.
Steps:
Open Chrome, and go to the website https://www.shadertoy.com/view/fttyRB.
Click the "Compile" button to compile the program. Then click "Pause" button to stop the variable iTime from changing. Next, click "Reset time" button to reset iTime to 0. The correct output should be white. However, due to the incorrect compilation, you would see the output to be black.

Reproduce the bug on desktop

A minimal pixel shader to reproduce the bug

 

float4 PS(VertexOut pIn): SV_Target {
    float4 fragColor;
    float t;
    for (int i = 0; i < 2; i++)
        for (int j = 0; j < 2; j++) {
            t = iTime + 0.4;
            fragColor = (frac(t) == 0.0) ? float4(0.0, 0.0, 0.0, 1.0) : float4(1.0, 1.0, 1.0, 1.0);
        }
    return fragColor;
}

 

In the above pixel shader program, the variable iTime is a constant buffer (a kind of variable similar to user-input and assigned by the calling CPU program at runtime) that is provided with value 0 at runtime. So the value of varialbe t is 0.4. Since the semantics of the built-in function frac is to retrieve the fractional part of its input, frac(t) should be 0.4. Hence, frac(t) == 0.0 should be false, and fragColor should be assigned with float4(1.0, 1.0, 1.0, 1.0), which means that the output color is white. However, the compiler incorrectly reasons that frac(t) == 0.0 is true, and assigns fragColor with float4(0.0, 0.0, 0.0, 1.0) (color black).

 

Steps to reproduce the bug

Prerequisite: Windows 10 system installed with Visual Studio 2022
2. Open D3DApp.sln with Visual Studio.
3. Check correct result: change line 3 of d3dUtils.cpp to "#define NO_OPTMIZATION 1" to disable all the optimizations of hlsl compiler. Click "Start Without Debugging". You will see a window with white body popped up. This means that the return value of the pixel shader is float4(1.0, 1.0, 1.0, 1.0) (color white).
4. Check buggy result: change line 3 of d3dUtils.cpp to "#define NO_OPTMIZATION 0" to enable the optimization of hlsl compiler. Click "Start without Debugging". You will see a small window popped up. The display area of the window is black, meaning that the return value of the pixel shader in HLSL/pixel.hlsl is float4(0.0, 0.0, 0.0, 1.0) (color black).

 

A brief explanation of the code

What the code does is quite basic. In lines 66~74 of GameApp.cpp, the program compiles the vertex shader HLSL/vertex.hlsl and the pixel shader HLSL/pixel.hlsl, and outputs the compiled *.cso file to the folder HLSL/. It prepares a square for displaying the output of the pixel shader. It sets a constant buffer variable iTime (later used in the pixel shader) in line 125 of GameApp.cpp. Then it executes and displays the rendering output in line 22 of Main.cpp.

 

Debugging with RenderDoc

If you use RenderDoc and follow the steps in https://renderdoc.org/docs/how/how_debug_shader.html to debug the compiled shader program, or use any of the dissembly tools to inspect the compiled shader program, you will see that the compiled shader directly assigns the output value with zero: mov o0.xyz, l(0, 0, 0, 0). It means that the compiler reasons that "fract(a) == 0.0" is always true and assigns the output variable fragColor with black color.
0 Likes
0 Replies