- Notifications
You must be signed in to change notification settings - Fork787
Porting shaders from FXC to DXC
This topic should be used as a reference point when porting your existing high-level shader language (HLSL) shaders over from D3DCompiler (FXC) to DXCompiler (DXC). Specifically, this topic provides details about the following:
Enable some of the old FXC compilation behaviors that are disabled by default on DXC. This is supported on DXC through the following compiler flags:
-Gec
: Enable Direct3D 9 backward compatibility (for example, enable support forCOLOR
semantic that's supported only until Direct3D 9).-HV
: Enable FXC backward compatibility by setting the language version to 2016 (that is,-HV 2016
). This enables a certain legacy language semantic that's available on FXC.-flegacy-macro-expansion
: Expand the operands before performing token-pasting operation (FXC behavior).-flegacy-resource-reservation
: Reserve unused explicit register assignments for compatibility with shader model 5.0 and earlier.
Provide information about the old deprecated HLSL syntaxes that were supported on FXC but are no longer supported on DXC.
We recommend using the newIDxcCompiler3::Compile
function for compiling DXC shaders. For more information about helper routines that will make porting existing code to the new interface easier, along with sample code, seeUsing DXC.
Multiplication-only pattern for the intrinsic functionpow
By default, DXC always useslog-mul-exp pattern (explained as follows) to implement thepow function. In contrast, FXC also uses themul-only pattern instead to implement thepow function in some scenarios. For example, for smaller constant values ofy (such aspow(x, 3)), FXC uses themul-only pattern. This could lead to thepow function producing different results when compiled by using FXC and DXC in some cases. For example, for a runtime value ofx=0,pow(x,3) evaluates toNaN and0 on DXC and FXC, respectively. To achieve the FXC behavior with respect to thepow function on DXC, set the language version to 2016 (by using the flag-HV 2016
) during compilation.
pow(x,y) = e^(log(x) * y)
pow(x,3) = (x * x) * x
By default, DXC errors out when finding constant index references to an array, which is out of bound. For example, the following HLSL shader fails to compile with an error message:error: array index 3 is out of bounds
. You could turn this error into a warning by setting the language version to 2016 (that is, by using the-HV 2016
flag) only when the out-of-bound access happens in the unused or dead code.
voidmain(){ uint x[2]; x[3]=1.f;// Out-of-bound access.}
When theunroll attribute is applied on a loop and if the loop-trip–count can't be ascertained at compile-time or if it's above a certain threshold, then by default, DXC fails to compile shaders such as the following. To turn this failure into a warning, set the language version to 2016. When the following shader is compiled on DXC with the-HV 2016
flag, it compiles successfully without unrolling the loop and generates a warning message:warning: Could not unroll the loop.
uintmain(uint x : IN) : OUT{ uint r =0; [unroll]for(uint i=0; i<x; i++) r+=i;return r;}
The semantic COLOR isn't supported on DXC by default. When writing new HLSL shaders targeting DXC, you should instead useSV_Target. However, DXC also provides an option to compile shaders referencing the COLOR semantic by passing it the-Gec
flag. For example, the following HLSL code should compile successfully on DXC when the-Gec
flag is used.
floatmain() : COLOR{return1.f;}
TheLength
property on constant arrays is supported on DXC under the-Gec
flag as shown in the following example.
uintmain() : OUT{int x[5];return x.Length;}
Global variables are treated as constants in HLSL, but just like FXC, DXC also enables support for writing to globals with the-Gec
flag as shown in the following example.
float gvar;// Write enabled with "-Gec"float4main(uint a : A) : SV_Target{ gvar = a *2.0f;return (float4) gvar;}
For shader model 5.0 or earlier, FXC would factor in the register allocation of unused resources when doing register allocation for used ones. For example, when compiling the following shader on FXC with SM5.0 and 5.1,Tex1[1]
is bound tot4
andt0
, respectively.When using DXC, to achieve the FXC resource allocation behavior with SM5.0 or earlier, use the-flegacy-resource-reservation
flag.
Texture2D Tex0[2] :register(t1);Texture2D Tex1[2];float4main() : SV_Target{return Tex1[1].Load((uint3)0);}
// Resource bindings://// Name Type Format Dim HLSL Bind Count// ------------------------------ ---------- ------- ----------- -------------- ------// Tex1[1] texture float4 2d t4 1
// Resource bindings://// Name Type Format Dim ID HLSL Bind Count// ------------------------------ ---------- ------- ----------- ------- -------------- ------// Tex1 texture float4 2d T0 t0 2
// Resource bindings://// Name Type Format Dim ID HLSL Bind Count// ------------------------------ ---------- ------- ----------- ------- -------------- ------// Tex1 texture f32 2d T0 t0 2
// Resource bindings://// Name Type Format Dim ID HLSL Bind Count// ------------------------------ ---------- ------- ----------- ------- -------------- ------// Tex1 texture f32 2d T0 t3 2
The following HLSL shader compiles successfully on FXC, but errors out on DXC asb##slot
is replaced withbSLOT_VAL
instead ofb3
, thus causing compilation failure. To achieve the FXC token replacement behavior on DXC, compile with the additional flag "-flegacy-macro-expansion
".
#defineSLOT_VAL3#defineCBUFFER_ALIGNED( name, slot) cbuffer name :register( b##slot )CBUFFER_ALIGNED( MyCBuffer, SLOT_VAL ){ float4 val;};
You can avoid the usage of the -flegacy-macro-expansion flag in this case by following standard preprocessor rules.
#defineSLOT_VAL3#definePASTE1(a, b) a##b#definePASTE(a, b) PASTE1(a, b)#defineCBUFFER_ALIGNED( name, slot) cbuffer name :register( PASTE(b, slot) )CBUFFER_ALIGNED( MyCBuffer, SLOT_VAL ){ float4 val;};
-flegacy-macro-expansion doesn't help for cases where the tokens are not macro arguments, though fxc still expands these. The following will result in an error even if -flegacy-macro-expansion is used.
#defineC4#defineA4 float4#defineTYPE A##C#defineFUNC(ty) tymain() : OUT {return0; }FUNC(TYPE)
The following change will bring the HLSL into compatibility with standard preprocessor rules, no longer requiring the -flegacy-macro-expansion option.
#defineC4#defineA4 float4#definePASTE1(a, b) a##b#definePASTE(a, b) PASTE1(a, b)#defineTYPEPASTE(A, C)#defineFUNC(ty) tymain() : OUT {return0; }FUNC(TYPE)
Features related to the effects framework are no longer supported.
Uniform parameters on entry functions are no longer supported.
There is a rewriter utility that may help port shaders that use this feature to DXC. The command line tool is called dxr.exe, and the API is indxctools.h.
The option to use is-extract-entry-uniforms
, along with the entry name, and any other options necessary to parse the shader, such as-I
include and-D
define options. Seedxr.exe -?
for a full option list. For the API, use the IDxcRewriter2::RewriteWithOptions method.
Rewritten HLSL will have the uniform resource and values extracted into the global scope for a single entry point. Name collisions are possible if parameter names collide with global names. It's meant to be used on one entry for one compilation, not on all entries in one file, otherwise name collisions will likely occur. Additionally, resources will only be extracted if identified with theuniform
keyword at this time.
Uniform values extracted into global space will be placed in a constant buffer named_Params
, since the equivalent in FXC would have been to put them in a constant buffer named$Params
.
Example:
float4main(float4 color : INPUT, uniformfloat scale, uniform SamplerState sam, uniform Texture2D<float2> tex, uniform RWStructuredBuffer<float2> buf :register(u1) ) : SV_TARGET{ buf[int(color.x)] = tex.SampleLevel(sam, color.xy * scale,0).xy;return color;}
Use the rewriter tool:
dxr.exe example.hlsl -extract-entry-uniforms -E main -Fo output.hlsl
To produce something like this inoutput.hlsl
:
uniform SamplerState sam;uniform Texture2D<float2> tex;uniform RWStructuredBuffer<float2> buf :register(u1);cbuffer _Params {uniformfloat scale;}float4main(float4 color : INPUT) : SV_TARGET { buf[int(color.x)] = tex.SampleLevel(sam, color.xy * scale,0).xy;return color;}
Interfaces are no longer supported.
Support fortex1D,tex2D, and similar functions for sampling are now deprecated. To learn how to port to supported sampling functions, see the following example.
sampler FontTextureSampler :register(s0);structVS_OUT{ float2 TexCoord0 : TEXCOORD0;};float4PSMain_Unsupported( VS_OUT In ) : SV_Target{returntex2D( FontTextureSampler, In.TexCoord0 ).zyxw;// tex2D is unsupported.}
SamplerState FontTextureSampler :register(s0);Texture2D FontTexture2D :register( t0 );// Step 1. Declare texture.structVS_OUT{ float2 TexCoord0 : TEXCOORD0;};float4PSMain( VS_OUT In ) : SV_Target{// Step 2. Use "Sample" method of the texture declared in the previous step.return FontTexture2D.Sample( FontTextureSampler, In.TexCoord0 ).zyxw;}
DXC disallows the use of duplicate semantics. For example, the following shader will fail compilation on DXC.
uintmain(uint a : SV_VertexID, uint b : SV_VertexID) : OUT{return a + b;}
Starting with SM5.1, HLSL supportsindexable constant buffers by using theConstantBuffer template construct. Therefore,ConstantBuffer is a reserved keywordand DXC disallows using it as a buffer name.
cbuffer ConstantBuffer :register(b0)// ConstantBuffer as buffer name not allowed.{ float2 field1;float field2;}