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

Is VFACE register supported in Unity ?

Discussion in 'Shaders' started by sebastien lagarde, Feb 13, 2011.

  1. sebastien lagarde

    sebastien lagarde

    Joined:
    Jul 22, 2010
    Posts:
    15
    Hello,

    I try to use the VFACE register of SM3 model to do double sided lighting material.
    but Unity don't seems to like it.
    First ShaderLab seems only to recognize FACE (cg), not VFACE (D3D).
    Second when I used the FACE register, I get

    Shader error in 'NameofShader': D3D shader assembly failed with: (90): error X2022: scalar registers cannot be swizzled

    Anyone used VFACE in a custom shader before ?

    Thanks
     
  2. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    Sounds like your shader code has an error, because the error implies that you try to use .xyzw in some combination on the result of FACE / VFACE, while these functions return you a single float, which can't be swizzled and doesn't need to be swizzled.
     
  3. sebastien lagarde

    sebastien lagarde

    Joined:
    Jul 22, 2010
    Posts:
    15
    Thanks for the answer,
    but I can't see how the shader code generated can be wrong with such simple code:

    // Main function
    float4 frag (v2f i
    ,float FacingSign : FACE
    ) : COLOR
    {
    float3 normal = normalize(i.normal);

    float sign = FacingSign >= 0 ? +1 : -1;
    normal *= sign;

    There isn't any swizzling...
     
  4. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    And what does the rest of that shader contain? (as it isn't complete like this)


    I assume that the line at fault is normal *= sign; which is actually swizzling, although implicitely, not explicitely as it tries to get a float3 from sign cause you didn't provide it.
    Guess it tries something like sign.xxx in the compilation or alike to get from float to float3 to apply *=. That would at least explain the error.
    (see http://http.developer.nvidia.com/Cg/Cg_language.html - "smearing" of scalars to vectors)

    You could try to overcome it by using something like
    What you are looking for there was likely normal = mul(normal, float3(sign)); or normal *= float3(sign) to take away the "interpretation space" from the compiler
     
  5. sebastien lagarde

    sebastien lagarde

    Joined:
    Jul 22, 2010
    Posts:
    15
    Again thanks for the help.

    Thanks for the pointer, but the problem seems to be more difficult than I first thinking.
    After some try, it seems that the compiler is lost rapidly when I use some kind of code.

    float3 reflectViewDirWS = reflect(viewDirWS, normalWS); => this fail

    float3 reflectViewDirWS = viewDirWS - 2.0f * normalWS * dot(viewDirWS, normalWS); => this fail

    float3 reflectViewDirWS = - 2.0f * dot(viewDirWS, normalWS);
    reflectViewDirWS = reflectViewDirWS * normalWS;
    reflectViewDirWS = viewDirWS - reflectViewDirWS;
    => this work if there only this code. (note that there is smearing here, and this work...)

    Adding a texCube(cubesampler, reflectViewDirWS) make it fail again...

    etc...

    I finally implement this VFACE register with code instead.

    Note : sign.xxx, this was the first thing I try, but change nothing. Like writing float3(2.0f, 2.0f, 2.0f)

    Thanks for your help.
     
  6. sebastien lagarde

    sebastien lagarde

    Joined:
    Jul 22, 2010
    Posts:
    15
    To be more complete, even if I stop to search a solution

    here the cg code

    // Main function
    float4 frag (v2f i
    ,float FacingSign : FACE
    ) : COLOR
    {
    float3 normalWS = normalize(i.normalWS);
    float3 viewDirWS = normalize(i.viewDirWS);

    float sign = FacingSign >= 0 ? 1 : -1;
    normalWS *= sign;
    float3 reflectViewDirWS = reflect(viewDirWS, normalWS);

    return float4(reflectViewDirWS, 1);
    }

    and the full error

    Shader error in 'XXX': D3D shader assembly failed with: (13): error X2022: scalar registers cannot be swizzled

    Shader Assembly: ps_3_0
    ; 12 ALU
    def c0, 1.00000000, 2.00000000, 0, 0
    dcl_texcoord6 v3.xyz
    dcl_texcoord7 v4.xyz
    dcl vFace
    dp3 r0.x, v3, v3
    rsq r0.x, r0.x
    dp3 r0.w, v4, v4
    rsq r0.w, r0.w
    mul r0.xyz, r0.x, v3
    mul r1.xyz, r0.w, v4
    cmp r1.w, vFace.x, c0.x, -c0.x <= work fine here
    mul r1.xyz, r1, r1.w <= no problem here ?
    dp3 r0.w, r1, r0
    mul r1.xyz, r1, r0.w
    mad oC0.xyz, -r1, c0.y, r0
    mov oC0.w, c0.x
     
    Last edited: Feb 13, 2011
  7. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    ok bad news

    I crawled through the various semantics docs at NVIDIA

    FACE exists, BUT its only supported on fp40 and glslf profile.
    But from what I recall, unity actually uses the ps_ profiles (ps_2_x default, ps_3_0 if you add the 3.0 pragma) on cg so they really compile or at least normally do ...

    also the shader above is likely still not the complete one, so its hard to say more as we don't know which line the line actually really is cause i get the error

    when dropping your vert into a more or less barebone shader that has the required v2f input specification and a vert
     
    Last edited: Feb 14, 2011
  8. sebastien lagarde

    sebastien lagarde

    Joined:
    Jul 22, 2010
    Posts:
    15
    > when dropping your vert into a more or less barebone shader that has the required v2f input specification and a vert
    In fact you right, I try several conditionnal definition like

    #if defined(SHADER_API_XBOX360) || defined(SHADER_API_D3D9)
    #define FACE VFACE
    #elif defined(SHADER_API_D3D11) || defined(SHADER_API_D3D10)
    #define FACE SV_IsFrontFace
    #endif

    and used ,float FacingSign : FACE
    but in every combinaison I test, there always a case where FACE should be VFACE and other.
    Sample when I compile for D3D9 only, I should have only VFACE required, but it fail on VFACE semantic...
    Any advice about that from Unity guy ? Why define a cross platform VPOS and not a cross platform VFACE ?

    Anyway, the error I get above can be achieve by compiling only D3D9 renderer and using FACE semantic.

    I try to isolate the error in the smallest possile shader code cause my shader are complex.

    I compile with the following pragma:

    CGPROGRAM
    #pragma only_renderers d3d9
    #pragma target 3.0
    #pragma vertex vert
    #pragma fragment frag
    #pragma multi_compile_builtin_noshadows
    #pragma fragmentoption ARB_precision_hint_fastest

    #include "UnityCG.cginc"
    #include "AutoLight.cginc"

    struct v2f {
    float4 pos : POSITION;
    float3 normal : TEXCOORD0;
    float3 viewDir : TEXCOORD1;

    };

    struct app {
    float4 vertex : SV_POSITION;
    float3 normal : NORMAL;
    };

    v2f vert (app v)
    {
    v2f o;
    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    o.normal = v.normal;
    o.viewDir = ObjSpaceViewDir( v.vertex );

    return o;
    }



    // Main function
    float4 frag (v2f i
    ,float FacingSign : FACE
    ) : COLOR
    {
    float3 normal = normalize(i.normal);
    float3 viewDir = normalize(i.viewDir);

    float sign = FacingSign >= 0 ? 1 : -1;
    normal *= sign;
    float3 reflectViewDir = reflect(viewDir, normal);

    return float4(reflectViewDir, 1);
    }
    ENDCG

    error X2022: scalar registers cannot be swizzled

    Shader Assembly: ps_3_0
    ; 12 ALU
    def c0, 1.00000000, 2.00000000, 0, 0
    dcl_texcoord2 v0.xyz
    dcl_texcoord4 v1.xyz
    dcl vFace
    dp3 r0.x, v1, v1
    rsq r0.y, r0.x
    dp3 r0.x, v0, v0
    mul r1.xyz, r0.y, v1
    rsq r0.x, r0.x
    cmp r0.w, vFace.x, c0.x, -c0.x
    mul r0.xyz, r0.x, v0
    mul r0.xyz, r0, r0.w
    dp3 r0.w, r0, r1
    mul r0.xyz, r0, r0.w
    mad oC0.xyz, -r0, c0.y, r1
    mov oC0.w, c0.x

    Note the D3D9 only, and for this case, VFACE is supported from ps3 profile.
    Cg seems to require to use FACE semantic instead of VFACE but as show in the dissassembly, it found the right register
    for the right profile
    => Shader Assembly: ps_3_0
    => cmp r1.w, vFace.x, c0.x, -c0.x
     
  9. Noisecrime

    Noisecrime

    Joined:
    Apr 7, 2010
    Posts:
    2,051
    Was facing this problem myself today and I think i've found a solution at least for d3d9 and d3d11, not yet been able to test opengl/glsl or d3d11_9.

    Essentially the issue, at least in Unity 4.3.4, is a question of getting the right semantics, type and usage of Face.

    D3D9:
    Must use FACE semantic as a float type, but it can only be used within the function as an input to a conditional check, it will fail with an error if you try to use it with a ternary for example. I was clued into this by using uint as the type upon which I received this error

    D3D11:
    Must use SV_IsFrontFace semantic, but it uses a unit type. Ternary operations do not cause errors.

    OPENGL:
    Has not been tested but I assume it will work with the FACE semantic and using a float type.
    Edit: Sadly it appears the whilst FACE semantic gives no errors in opengl, it is broken, treating every face as backfacing. Seems to be a different bug to the D3D9 issue.

    I've included the relevant parts from my test shader below to illustrate the points above.

    Hope this helps others who face the same problem.

    Code (CSharp):
    1. CGPROGRAM
    2.                 #pragma vertex vert
    3.                 #pragma fragment frag
    4.                 #pragma target 3.0
    5.                 #pragma glsl
    6.            
    7.             //    Testing explicit renderers
    8.             //    #pragma only_renderers d3d9
    9.             //    #pragma only_renderers d3d11          
    10.             //    #pragma only_renderers opengl
    11.                    
    12.                 #include "UnityCG.cginc"
    13.            
    14.                 #if defined(SHADER_API_XBOX360) || defined(SHADER_API_D3D9)
    15.                     #define FACING FACE
    16.                 #elif defined(SHADER_API_D3D11) || defined(SHADER_API_D3D10)
    17.                     #define FACING SV_IsFrontFace
    18.                 #elif defined(SHADER_API_OPENGL) || defined(SHADER_API_GLES)
    19.                     #define FACING FACE
    20.                 #endif        
    21.            
    22.                 #if defined(SHADER_API_XBOX360) || defined(SHADER_API_D3D9)
    23.                 fixed4 frag (v2f i, float facing : FACING) : COLOR
    24.                 {              
    25.                     float strength;
    26.  
    27.                     // IMPORTANT: In D3D9 use of facing must be as a conditional check a ternary will NOT work
    28.                     if (facing > 0)
    29.                         strength = 1;
    30.                     else
    31.                         strength = 0.25;        
    32.                 }          
    33.                 #elif defined(SHADER_API_D3D11) || defined(SHADER_API_D3D10)
    34.                 fixed4 frag (v2f i, uint facing : FACING) : COLOR
    35.                 {
    36.                     float strength = facing > 0 ? 1 : 0.25;      
    37.                 }
    38.                 #else
    39.                 // #elif defined(SHADER_API_OPENGL) || defined(SHADER_API_GLES)          
    40.                 fixed4 frag (v2f i, float facing : FACING) : COLOR
    41.                 {
    42.                     float strength = facing > 0 ? 1 : 0.25;
    43.                 }
    44.                 #endif    
    45.            
    46.             ENDCG
     
    Last edited: Aug 17, 2014
  10. Noisecrime

    Noisecrime

    Joined:
    Apr 7, 2010
    Posts:
    2,051
    Then just like that the d3d9 version breaks and refuses to compile again. So weird it was working perfectly earlier and even tested it multiple times, across API's and shader was doing what was expected, but something has changed and now its back to the old swizzle error.

    I've reported both the d3d9 and opengl issues as bugs to UT.

    Thankfully it can still be fixed by using a pre-compiled shader, but takes a bit more effort and is a real pain to modify the shader later.

    A pre-compiled shader is simply copy and pasting a compiled version of a shader (click on the shaders 'open compiled version' in the inspector) into a new shader file. By doing this its possible to fix some errors but at the expense of no longer being able to re-compile it.

    Basic steps for this case are

    1. Append the following pragma's to your original shader code
    Code (CSharp):
    1. #pragma exclude_renderers d3d9
    2. #pragma only_renderers d3d9
    2. Comment out 'only_renderers d3d9' line above and compile your shader. This means the shader should compile without the swizzle error as d3d9 programs are not being created, they are 'excluded'.

    3. In the inspector click on 'Open compiled shader' button of your shader.

    4. Copy and paste the full compiled shader code into a new shader file. This will be the 'pre-compiled' version and you should rename the shader to indicate that this is the pre-compiled version so as not to have two shaders with the same name.

    5. Return to your original shader, uncomment 'only_renderers d3d9' and comment out 'exclude_renderers d3d9', then compile the shader again.

    This will cause the swizzle error to be reported, but importantly the d3d9 vertex program (vp) and fragment program (fp) will still be generated. However any other shader API's that might normally be compiled after d3d9 will not due to the error report stopping Unity from continuing the compilation. This is why we have to go through this somewhat convoluted process in order to retain all shader API versions.

    6. In the inspector click on 'Open compiled shader' button for the original shader. Make sure its using the newly updated version of the shader and not the previous compiled version file.

    7. Locate the d3d9 vertex program (vp) then copy and paste the entire 'SubProgram' into the 'vp' section of your 'pre-compiled' version.

    Next do the same for the d3d9 fragment program (fp), copy and paste its entire 'SubProgram' into the 'fp' section of your 'pre-compiled' version.

    8. Once you've pasted the fp SubProgram you need to fix the cg vFace error in your 'pre-compiled' version. Find any reference that uses vFace and remove the x component.

    e.g.
    Code (CSharp):
    1. cmp r0.y, -vFace.x, c3.x, c3
    becomes
    Code (CSharp):
    1. cmp r0.y, -vFace, c3.x, c3

    9. Finally comment out 'only_renderers d3d9' and uncomment 'exclude_renderers d3d9', in your original shader to avoid it throwing errors and make sure to save your new pre-compiled version.

    From then on just use the pre-compiled version of the shader.

    Just remember every time you want to change this shader, you must change the original version, then go through the above steps again to create the 'pre-compiled' version, hence why its such a pain.
     
    Last edited: Aug 18, 2014
  11. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
  12. Noisecrime

    Noisecrime

    Joined:
    Apr 7, 2010
    Posts:
    2,051
    Thanks for that Aras, nice to see how on the ball Unity are becoming with fixing these things, a big difference to how things were a few years ago. Guess that means I have to rewrite all my shader workarounds now ;)
     
  13. jistyles

    jistyles

    Joined:
    Nov 6, 2013
    Posts:
    34
    Apologies for resurrecting this thread, but is the VFACE semantic able to be passed through or got at for surface shaders?
     
  14. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Not for surface shaders yet.
     
  15. tomekkie2

    tomekkie2

    Joined:
    Jul 6, 2012
    Posts:
    972
  16. geroppo

    geroppo

    Joined:
    Dec 24, 2012
    Posts:
    140
    lol I just found out about this thread, I will also need some clarification on it's usage because VFACE and SV_isFrontFace are not compiling...I should put that semantic in the fragment shader right ? I'm working with shader model 5.0

    EDIT: nevermind, I had to declare it as a fragment shader input parameter instead of the vertex to fragment structure
     
    Last edited: Mar 15, 2017
  17. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329