Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Bug DirectX not respecting shader semantics

Discussion in 'Shaders' started by Peter-S-Hollander, May 4, 2021.

  1. Peter-S-Hollander

    Peter-S-Hollander

    Joined:
    Apr 28, 2016
    Posts:
    11
    Original title: Fragment shader seemingly not respecting input semantics


    Hello friends,

    I am working on a modular tessellation shader, and have run into an issue with passing the output of my domain shader as one struct (defined in my modular
    tessellation.cginc
    file) to my fragment shader as a different struct (defined in its own file). When using matching semantics between the domain and fragment shaders (which I strongly believe are appropriate semantics for fragment shader input), the packaging of information into the fragment shader appears to only respect matching the
    SV_Position
    semantic, and mixes up or fails to relay the remaining non-system value semantics (such as
    TEXCOORD#
    ) when they have been defined in a different order from the originating struct.

    To isolate this problem, I reduced the code down to a single shader small enough to comfortably include at the bottom of this post.

    Ultimately, I believe I am experiencing a problem where it is not semantics which dictate how variables align & match up between a vertex output and fragment input, but instead the order of definition which links two variables together. This behaviour is counter-intuitive to how I understand the purpose of semantics (ie. to prevent against a situation like this).

    Any insight (or confirmation of reproduction) towards this issue would be greatly appreciated! I strive to understand the nuances of writing shaders, and look forward to learning the mechanisms behind this behaviour that I am experiencing.

    Code (CSharp):
    1. Shader "Debug/Semantic Test" {
    2.  
    3.     SubShader {
    4.  
    5.         Pass {
    6.  
    7.             CGPROGRAM
    8.              
    9.                 #pragma vertex vert
    10.                 #pragma fragment frag
    11.  
    12.                 struct ObjectVertexData {
    13.                     float4 position : POSITION;
    14.                      half3   normal : NORMAL;
    15.                 };
    16.  
    17.                 struct ClipSurfaceData {
    18.                     float4 clipPos : SV_Position;
    19.                     float4 worldPos : TEXCOORD0;
    20.                      half3 worldNormal : TEXCOORD1;
    21.                 };
    22.  
    23.                 struct AlternateClipSurfaceData {
    24.                     // Changing the order of definition to match ClipSurfaceData will render the desired output
    25.                      half3 worldNormal : TEXCOORD1;
    26.                     float4 clipPos : SV_Position;
    27.                     float4 worldPos : TEXCOORD0;
    28.                 };
    29.  
    30.                 ClipSurfaceData vert (ObjectVertexData vertex) {
    31.                     ClipSurfaceData modified;
    32.                     modified.worldPos = mul(UNITY_MATRIX_M, vertex.position);
    33.                     modified.clipPos = mul(UNITY_MATRIX_VP, modified.worldPos);
    34.                     modified.worldNormal = mul((float3x3)UNITY_MATRIX_M, vertex.normal);
    35.                     return modified;
    36.                 }
    37.  
    38.                 fixed4 frag (AlternateClipSurfaceData interpolated) : SV_Target {
    39.                     // Changing from interpolated.worldNormal.xyz to interpolated.worldPos.xyz renders the originally packaged worldNormal values
    40.                     return float4((interpolated.worldNormal.xyz + 1) / 2, 1);
    41.                 }
    42.  
    43.             ENDCG
    44.  
    45.         }
    46.     }
    47. }
     
    Last edited: May 5, 2021
  2. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    2,983
    Hi!
    On which platform does this happen?
     
  3. Peter-S-Hollander

    Peter-S-Hollander

    Joined:
    Apr 28, 2016
    Posts:
    11
    Hi Aleksandrk! I am targeting Windows x86_64, and my development environment is Unity 2021.1.0f1 on Windows 10 LTSC 1809. If you have tried running this shader: do these semantics behave as expected for you, or are you able to reproduce this issue? If it works as expected, I'd love to know the platform you're targeting (and development environment information) so I can try to achieve parity with you on this.
     
  4. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    2,983
    I just checked on a mac, it renders normals for me properly.
    I also looked at the disassembly that is produced for DX11, and it seems fine as well - it uses the custom semantic index 1 in PS (this is what the normal gets written to in VS).
    Which graphics API are you using?
     
  5. Peter-S-Hollander

    Peter-S-Hollander

    Joined:
    Apr 28, 2016
    Posts:
    11
    Thank you for sharing that you have it working on your Mac! My compiled shader output is also showing D3D11.

    Here is the disassembled output for the vertex shader:

    Code (CSharp):
    1. // Generated by Microsoft (R) D3D Shader Disassembler
    2. //
    3. //
    4. // Input signature:
    5. //
    6. // Name                 Index   Mask Register SysValue  Format   Used
    7. // -------------------- ----- ------ -------- -------- ------- ------
    8. // POSITION                 0   xyzw        0     NONE   float   xyzw
    9. // NORMAL                   0   xyz         1     NONE   float   xyz
    10. //
    11. //
    12. // Output signature:
    13. //
    14. // Name                 Index   Mask Register SysValue  Format   Used
    15. // -------------------- ----- ------ -------- -------- ------- ------
    16. // SV_Position              0   xyzw        0      POS   float   xyzw
    17. // TEXCOORD                 0   xyzw        1     NONE   float   xyzw
    18. // TEXCOORD                 1   xyz         2     NONE   float   xyz
    And then this is for the fragment shader:

    Code (CSharp):
    1. // Generated by Microsoft (R) D3D Shader Disassembler
    2. //
    3. //
    4. // Input signature:
    5. //
    6. // Name                 Index   Mask Register SysValue  Format   Used
    7. // -------------------- ----- ------ -------- -------- ------- ------
    8. // TEXCOORD                 1   xyz         0     NONE   float   xyz
    9. // SV_Position              0   xyzw        1      POS   float    
    10. // TEXCOORD                 0   xyzw        2     NONE   float    
    11. //
    12. //
    13. // Output signature:
    14. //
    15. // Name                 Index   Mask Register SysValue  Format   Used
    16. // -------------------- ----- ------ -------- -------- ------- ------
    17. // SV_Target                0   xyzw        0   TARGET   float   xyzw
    18. //
    19.       ps_4_0
    20.       dcl_input_ps linear v0.xyz
    21.       dcl_output o0.xyzw
    22.    0: mad o0.xyz, v0.xyzx, l(0.500000, 0.500000, 0.500000, 0.000000), l(0.500000, 0.500000, 0.500000, 0.000000)
    23.    1: mov o0.w, l(1.000000)
    24.    2: ret
    (And just to be safe, I gathered this information from a fresh Unity project with default project settings)

    So in the output signature of the vertex shader and input signature of the fragment shader both appear to be behaving as expected (their
    TEXCOORD
    indices are defined correctly even when declared out of order). But when the fragment shader uses
    v0.xyz
    , I see a uniformly grey cube. But if I change my shader to use
    interpolated.worldPos.xyz
    for rendering normals, the fragment shader disassembles to:

    Code (CSharp):
    1.       ps_4_0
    2.       dcl_input_ps linear v2.xyz
    3.       dcl_output o0.xyzw
    4.    0: mad o0.xyz, v2.xyzx, l(0.500000, 0.500000, 0.500000, 0.000000), l(0.500000, 0.500000, 0.500000, 0.000000)
    5.    1: mov o0.w, l(1.000000)
    6.    2: ret
    With
    v2.xyz
    being our input. This is also expected... But what's not expected is that this is when I see rendered normals! And so the only thing I can think of is that this is pulling from the
    Register
    index, and not the
    Index
    ... index.

    Later today I can try reproducing this on a separate Windows machine.
     
    Last edited: May 5, 2021
  6. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    2,983
    Do you have the latest DX version and drivers installed?
     
  7. Peter-S-Hollander

    Peter-S-Hollander

    Joined:
    Apr 28, 2016
    Posts:
    11
    dxdiag
    reports DirectX 12, and I have just updated my NVidia GPU drivers to their most recent version - I am still experiencing the anomaly.
    (Is this sufficient for the information you were asking about?)
     
  8. Peter-S-Hollander

    Peter-S-Hollander

    Joined:
    Apr 28, 2016
    Posts:
    11
    Also I can confirm reproduction of this issue on a different machine running the most recent version of Windows 10, and Unity 2019.2.16f1.

    And to clarify, without making any edits to the shader I provided above, this is what I see:

    https://imgur.com/a/satqkRS

    Whereas, based on semantics, I am expecting to see conventional pastel normal visualization.

    Then, the reason I suspect something with regard to
    Register
    indexing is going on, when I replace
    interpolated.worldNormal.xyz
    with
    interpolated.worldPos.xyz
    (where the fragment shader's
    worldPos
    is contained in the same register index as the vertex shader output's
    worldNormal
    ), the normals render as desired:

    https://imgur.com/a/d6XLsA5

    And just to reiterate, I have produced this issue on two separate Windows 10 machines running DirectX 12, using different versions of Unity, both targeting Windows. I do not have access to a Mac.
     
    Last edited: May 5, 2021
  9. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    2,983
    In this case please file a bug report :)
     
  10. Peter-S-Hollander

    Peter-S-Hollander

    Joined:
    Apr 28, 2016
    Posts:
    11
    Okie doke, I will update this thread with a link to the bug report once it clears for reference.

    Hopefully this is a situation that others are able to reproduce!
     
  11. Peter-S-Hollander

    Peter-S-Hollander

    Joined:
    Apr 28, 2016
    Posts:
    11
    Oh, great news! I have isolated the problem to be with DirectX specifically. When changing the Windows Graphics API in Project Settings to Vulkan, OpenGLCore, OpenGLES2, and OpenGLES3, semantics are respected as expected :D

    While this is great to have a temporary workaround, it definitely still is a bug worth investigating as to why (at least in certain cases) DirectX is not respecting shader semantics on Windows... Shame I considered this important piece of information right after submitting my bug report :rolleyes: I'll go ahead and submit another one with further clarifications.
     
    Last edited: May 5, 2021
  12. aleksandrk

    aleksandrk

    Unity Technologies

    Joined:
    Jul 3, 2017
    Posts:
    2,983
    You can add more information to the bug report you already submitted :)
     
  13. Peter-S-Hollander

    Peter-S-Hollander

    Joined:
    Apr 28, 2016
    Posts:
    11
    I did not see an option for that in the FogBugz portal link that was emailed to me, but perhaps I would need to log in for that to be a feature! I will keep this in mind for the future
     
  14. Peter-S-Hollander

    Peter-S-Hollander

    Joined:
    Apr 28, 2016
    Posts:
    11
    Hello! Following up one month later to provide the link to the bug report on Unity's Issue Tracker, for anyone looking to track this issue further in the future:

    https://issuetracker.unity3d.com/is...ency-between-vertex-output-and-fragment-input

    If anyone needs to work around the issue in the meantime, I have found using Vulkan or OpenGL (ie. not using DirectX) behaves as expected, and should be able to hold me over until a fix is pushed.