Replace dxc -spirv with oneshader in a Makefile, CMake or script build: which DXC flags it takes unchanged, what it adds (MSL and WGSL targets, reflection JSON, a dependency file), and which DXC options it rejects.
oneshader, from RecursiveAI.OneShader.Tool, takes DXC’s core flags, so a DXC command line that emits SPIR-V for a vertex, pixel or compute shader mostly works unchanged. It needs the .NET 10 runtime:
dotnet tool install -g RecursiveAI.OneShader.Tool --version 1.2.0 oneshader --version
A Makefile, before and after
shaders/post.hlsl includes shaders/common/color.hlsli and uses a macro from the command line:
#include "color.hlsli" Texture2D<float4> Scene : register(t0); SamplerState Linear : register(s1); float4 main(float2 uv : TEXCOORD0) : SV_Target { return float4(Tonemap(Scene.Sample(Linear, uv).rgb * EXPOSURE), 1); }
The only change to the Makefile is the compiler’s name, plus --depfile, so that editing the include rebuilds the shader:
# Was: DXC = dxc DXC = oneshader FLAGS = -spirv -O3 -HV 2021 -WX -I shaders/common -D EXPOSURE=1.5 build/post.spv: shaders/post.hlsl @mkdir -p build $(DXC) $(FLAGS) -T ps_6_0 -E main $< -Fo $@ --depfile build/post.d -include build/post.d
make, then cat build/post.d, then make again:
oneshader -spirv -O3 -HV 2021 -WX -I shaders/common -D EXPOSURE=1.5 -T ps_6_0 -E main shaders/post.hlsl -Fo build/post.spv --depfile build/post.d build/post.spv: /tmp/oneshader-docs/cookbook/Dxc/shaders/post.hlsl /tmp/oneshader-docs/cookbook/Dxc/shaders/common/color.hlsli make: 'build/post.spv' is up to date.
After touch shaders/common/color.hlsli, make compiles again. The output passes spirv-val, and DXC compiles the same command line (without --depfile) too.
The flags
From the tool’s documentation, checked against oneshader --help 1.2.0:
| DXC flag | In oneshader |
|---|---|
-T <profile> | vs_6_x, ps_6_x, cs_6_x (also _5_x). The stage comes from the profile; the model number is accepted and otherwise unused. |
-E <name> | Entry point. With -T and no -E it’s main, as in DXC. |
-D NAME[=VALUE], -I <dir> | As in DXC. |
-Fo <file> | Output file. With several entry points or targets, the names are derived from it (name.<entry>.spv, .metal, .wgsl). Without -Fo, a single output goes to standard output. |
-spirv | SPIR-V, which is also the default. (DXC’s default is DXIL, which oneshader doesn’t write.) |
-O0, -Od, -O1..-O3 | Optimizer off or on (on by default). |
-Zpr, -Zpc | Row-major or column-major default matrix packing. |
-HV 2021 | Accepted (202x too). Other versions are rejected. |
-WX | Warnings are errors. |
-enable-16bit-types | half, min16* and the int16_t family are native 16-bit types, as in DXC (since 1.2.0). See 16-bit and 64-bit types. |
-fspv-target-env=vulkan1.2 | Accepted (vulkan1.3 too). The output is valid for Vulkan 1.2 and newer. |
-nologo | Accepted and ignored. |
What oneshader adds:
| Option | Meaning |
|---|---|
--target spirv|msl|wgsl | Target; repeat for several. |
--all-entries | Compile every [shader("vertex"|"pixel"|"compute")] entry point. Also what happens without -T. |
--reflect <file.json> | Reflection as JSON, deterministic and without machine paths. See Read shader reflection. |
--depfile <file> | A Make-style dependency file, written on success. |
--no-names | Leave source names out of the output. |
Diagnostics go to standard error as file(line,col): error OSHnnnn: message, the form IDEs and CMake parse. The exit code is 0 for success, 1 when the shader doesn’t compile, 2 for a wrong command line.
What it rejects
Every other -fvk-* and -fspv-* option, and DXC options such as -Zi or -Fh, stop with a message that names the option (exit code 2):
oneshader: error: option '-Zi' is not supported: debug info is not implemented. Remove it, or use --no-names to drop names from the output. Run 'oneshader --help' for the options. oneshader: error: option '-fvk-use-dx-layout' is not supported. oneshader always uses DXC's default Vulkan layout and binding rules; use [[vk::binding]], [[vk::location]] and [[vk::offset]] in the shader instead. Run 'oneshader --help' for the options. oneshader: error: -T gs_6_0 is not a supported profile. Use vs_6_x, ps_6_x or cs_6_x; geometry, tessellation, mesh and library profiles are not supported. Run 'oneshader --help' for the options.
- Layout and binding remapping (
-fvk-b-shift,-fvk-use-dx-layoutand the like) isn’t supported. oneshader always applies DXC’s default Vulkan rules, so move the numbers into the shader with[[vk::binding]],[[vk::location]]and[[vk::offset]]. - DXIL, and geometry, tessellation, mesh, ray-tracing and library profiles, aren’t supported. Keep DXC for those.
Speed
Started once per shader, as in a Makefile, the tool’s speed depends on how it’s built. The repository measures a NativeAOT build at about 2.4 times faster than DXC’s release binary per process, warm. Run as an ordinary .NET process, like the .NET tool installed above, it’s slower than DXC: about 6 times with ReadyToRun and 14 times with the JIT, because starting the runtime dominates a compile that takes 0.34 ms at the median. To get the NativeAOT binary, publish it from the repository with dotnet publish src/OneShader.Tool -c Release -r linux-x64 -o out. The numbers and the method are in Performance against DXC.
How DXC, glslang and OneShader differ on bindings and target environments: HLSL to SPIR-V compiler compared.