In-Memory Quickstart
This is the product in its purest form: add the ShadowDusk.Compiler package, call CompileAsync, and get .mgfx bytes back in memory — no temp files, no child process, no mgfxc.
1. Add the package
dotnet add package ShadowDusk.Compiler
2. Compile a shader
using ShadowDusk.Compiler;
using ShadowDusk.Core;
string hlsl = File.ReadAllText("MyShader.fx"); // or any HLSL/.fx string
var compiler = new EffectCompiler();
Result<CompiledShader, ShaderError[]> result = await compiler.CompileAsync(
hlsl,
new CompilerOptions
{
Target = PlatformTarget.OpenGL, // or PlatformTarget.DirectX / PlatformTarget.Fna
SourceFileName = "MyShader.fx", // optional — improves error messages
});
if (result.IsSuccess)
{
byte[] mgfx = result.Value.Data; // the .mgfx binary, ready to load
File.WriteAllBytes("MyShader.mgfx", mgfx);
}
else
{
foreach (ShaderError error in result.Error)
Console.Error.WriteLine(error.FxcFormattedMessage);
}
The result is a Result<CompiledShader, ShaderError[]> — a discriminated union. On success, Data is the .mgfx byte array (and Target echoes the platform). On failure you get an array of ShaderError with the file, line, column, code, and message exactly as the underlying compiler emitted them.
3. Load it into your game
The call is the same new Effect(graphicsDevice, bytes) for all three runtimes — only which bytes you pass differs by target.
For MonoGame and KNI, the bytes are a standard .mgfx blob (KNI reads the identical MGFX v10 container):
var effect = new Effect(graphicsDevice, mgfx); // MonoGame / KNI — .mgfx
It renders the same image mgfxc's output would.
Newer runtimes (opt-in). The default v10 container loads on every MonoGame 3.8.2+ and KNI runtime, so you usually do nothing. If you target a newer runtime and want its container, set
CompilerOptions.MgfxVersion = 11(MonoGame 3.8.5+) orCompilerOptions.Container = EffectContainer.Knifx(KNI v4.02+) — both opt-in/experimental, both render identically to v10. See Parameters & Caveats.
For FNA, you pass the .fxb produced by PlatformTarget.Fna (see below); FNA loads it through MojoShader:
var effect = new Effect(graphicsDevice, fxb); // FNA — .fxb
It renders the same image fxc /T fx_2_0's output would.
The default-target caveat (read this)
The library default and the CLI default differ:
| Surface | Default target |
|---|---|
| Library — Target | OpenGL |
CLI — mgfxc /Profile |
DirectX_11 |
So the code above (no explicit Target) compiles for OpenGL, while mgfxc MyShader.fx out.mgfx (no /Profile) compiles for DirectX_11. Always set the target explicitly to avoid surprises. See the CLI Reference.
Choosing the DirectX backend
When Target = PlatformTarget.DirectX, ShadowDusk emits DXBC (SM5) via a backend selected by DxbcBackend:
DxbcBackend.Vkd3d(default) — the cross-platformvkd3d-shaderbackend; works on Linux/macOS/Windows and emits the same bytes on every OS. The vkd3d natives for all four desktop RIDs ship inside the NuGet package — consumers install nothing (self-contained; the repo's restore script is only for building ShadowDusk itself from source).DxbcBackend.D3DCompiler— the Windows-onlyd3dcompiler_47correctness oracle (opt-in; hard-fails off Windows).
You only set the property to opt in to the oracle:
var options = new CompilerOptions
{
Target = PlatformTarget.DirectX,
DxbcBackend = DxbcBackend.D3DCompiler, // opt in to the Windows-only oracle
};
See DirectX DXBC (vkd3d) Path for why DXC is not used here (it emits SM6 DXIL, which MonoGame's DX11 runtime cannot load).
Compiling for FNA
FNA doesn't read the .mgfx container — it loads the legacy D3D9 fx_2_0 .fxb through MojoShader at runtime. Select it with PlatformTarget.Fna; everything else is the same call:
var result = await compiler.CompileAsync(hlsl, new CompilerOptions
{
Target = PlatformTarget.Fna,
SourceFileName = "MyShader.fx",
});
// on success:
byte[] fxb = result.Value.Data; // the .fxb bytes (fx_2_0, SM <= 3)
var effect = new Effect(graphicsDevice, fxb); // FNA's Effect, loaded via MojoShader
Notes specific to the FNA target:
- Same package, no FNA-specific flag.
ShadowDusk.Compilerserves every target; onlyTargetchanges. FNA itself is added to your project as a project reference, not a NuGet — see Installation → Targeting FNA. - Shader Model ≤ 3. fx_2_0 caps at SM3; a shader needing SM4+ features fails loudly with a diagnostic instead of miscompiling.
- Validated. The output loads and renders pixel-equivalent (max Δ ≤ 1/255) to
fxc /T fx_2_0in real FNA across the pixel-shader-only and vertex-shader-driven corpora — multi-pass effects and in-pass render states included.
Reusing the compiler
EffectCompiler is cheap to construct and safe to reuse across many CompileAsync calls. Pass a CancellationToken to bound long compiles.