Table of Contents

Namespace ShadowDusk.Core

Classes

AnnotationInfo

A single FX annotation (a <key = value;> attached to a parameter or technique), as the MgfxWriter serializes it. Exactly one of the typed value fields is populated, selected by Type.

CapabilityProfile

A named, render-proven runtime contract: a validated point in ShadowDusk's capability space (the effect EffectContainer + MgfxVersion "format" axis and the GL ShaderDialect; later, allowed GL features). The closed set spans every (runtime, format) cell ShadowDusk targets, so one profile names a full contract.

CompiledShader

The successful output of CompileAsync(string, CompilerOptions, CancellationToken): the compiled effect bytes together with the platform they were produced for. For MonoGame/KNI targets the bytes are a .mgfx effect; for Fna they are the D3D9 fx_2_0 effects binary (.fxb). Either way they can be written to a file or fed directly to the consumer runtime's Effect constructor.

CompiledShaderBlob

One compiled shader within an effect: its platform bytecode (Bytes) and the stage it belongs to (Stage), plus the per-shader tables the effect writers serialize (samplers, constant-buffer indices, vertex attributes, shader model).

CompilerOptions

Settings that control a single CompileAsync(string, CompilerOptions, CancellationToken) call: the target backend, how #include directives are resolved, debug output, the MGFX container version, and (for DirectX) which DXBC backend to use.

ConstantBufferInfo

One effect constant buffer as the MGFX writer serializes it: its name, byte size, and the effect parameters it contains (by index into the effect's flattened parameter list) with each parameter's byte offset within the buffer.

D3d9BytecodePatcher

Post-pass over vkd3d's D3D9 SM2/SM3 token streams that canonicalizes the two instruction forms MojoShader rejects but vkd3d 1.17 emits (found by the Phase 39 rung-3/4 FNA harness — real FNA load failures where the fxc oracle loads fine):

  1. texkill with a partial destination writemask (vkd3d writes e.g. .x; fxc always writes .xyzw and MojoShader hard-fails anything else: "TEXKILL writemask must be .xyzw").
  2. texld whose coordinate source MojoShader rejects: a swizzle below SM3 ("TEXLD src0 must not swizzle" for ps_1_x/ps_2_x; SM3 allows it), or a source modifier at ANY SM2+ major ("TEXLD src0 must have no modifiers").
  3. def float literals with |f| ≥ 2³² — vkd3d's discard sentinel is −2³² (0xCF800000), and MojoShader's MOJOSHADER_printFloat converts the magnitude through a 32-bit unsigned long on Windows (LLP64), overflowing to ±0.0 in the translated source — so texkill's < 0 test never fires (Dissolve rendered un-discarded). Clamped in place to the same-signed largest float BELOW 2³² (±4294967040.0, 0x4F7FFFFF), which MojoShader prints exactly; the sentinel's only observable property is its sign. fxc's largest def literals are ±1, so the oracle never trips this. Empirically proven pixel-identical in real FNA by the rung-4 harness's clamped-Dissolve experiment.

The rewrite is semantics-preserving, not a blind mask/swizzle flip: the offending operand is routed through a fresh temporary — mov rK, reg.<replicated-masked-components> followed by the canonical texkill rK.xyzw (each tested lane now holds one of the originally-tested values), or mov rK, src0.<swizzle> followed by texld dst, rK, s#. Blindly widening the texkill mask would test garbage lanes; this never does.

D3D9 SM2+ instruction tokens carry an explicit operand-count field and the format has no byte-offset branches, so inserting instructions is purely mechanical; comment blocks (the CTAB) pass through byte-identical. Streams below SM2 are returned unchanged: MojoShader's ps_1_x rules differ wholesale (and ps_1_x tokens carry no instruction-length fields to walk), and the FNA pipeline rejects literal SM1 profiles upstream (SD0300), so this pass never has to reason about them.

Applied ONLY on the FNA path — the DirectX SM5 path never sees this code, and the blob handed to the fx_2_0 writer stays exactly what MojoShader will consume.

DirectX12ShaderCodeWrapper

Wraps raw SM6 DXIL bytecode in the small header MonoGame 3.8.5's real DirectX12 writer prepends before the per-shader ShaderCode field (see plan/PHASE-54-appendix/dx12-dxil-container-research.md — read from MonoGame's own source, not reverse-engineered). MonoGame's native DX12 Shader_Create looks for the magic marker as the first 4 bytes and, if present, consumes the next two ints as maxSamplerSlot/maxTextureSlot before treating the remainder as the raw shader bytecode handed straight to ID3D12Device::CreatePipelineState.

EffectParameterInfo

One .mgfx parameter record. MonoGame 3.8.2's Effect.ReadParameters reads Elements (array elements) and Members (struct members) as RECURSIVE parameter collections — elements first, then members, each a full nested parameter record (mirroring mgfxc's EffectObject.WriteParameter). A value-typed leaf (Scalar/Vector/Matrix with no elements/members) additionally carries a raw default-value blob of RowCountColumnCount4 bytes.

Fx2EffectDesc

The input IR for Fx2EffectWriter — everything needed to author a D3D9 Effects Framework binary ("fx_2_0", token 0xFEFF0901) for FNA. This is deliberately a separate shape from ShaderIR: the fx_2_0 container carries typed effect parameters with default values and sampler-state blocks that MGFX has no analog for. Field encodings follow docs/fx2-binary-format.md (MojoShader's parser is the spec).

Fx2EffectWriter

Emits the D3D9 Effects Framework binary ("fx_2_0", version token 0xFEFF0901) that FNA consumes via FNA3D/MojoShader — the FNA analog of MgfxWriter. There is no public spec for this format; the byte layout implemented here follows MojoShader's parser as documented field-by-field in docs/fx2-binary-format.md, cross-checked against real fxc /T fx_2_0 output (tests/fixtures/golden/FNA/).

Layout recap: 8-byte header (token + pool size), then a "data pool" holding every typedef/string/value blob addressed by offsets relative to file offset 8, then the structured stream (counts → parameter records → technique/pass/state records → small / large object sections). MojoShader performs no bounds checking on pool offsets, so this writer is solely responsible for their validity.

Fx2Parameter

One fx_2_0 effect parameter. Three kinds share this record, distinguished by Class/Type (raw D3DXPARAMETER / MojoShader symbol values, see docs/fx2-binary-format.md §6.1): numerics (class 0–3, type bool/int/float), textures (class 4, type 5–9), and samplers (class 4, type 10–14, carrying SamplerStates).

Fx2Pass

One fx_2_0 pass. Shader indices of -1 mean the stage is absent — the writer then omits the VertexShader/PixelShader state entirely (MojoShader keeps the previously bound shader; emitting a "NULL shader" state is the unresolved F4 ambiguity).

Fx2RenderState

One pass render-state assignment. Operation is the MojoShader renderStateType file value (NOT a D3DRS number); Value carries the raw dword (D3D9-domain enum value, 0/1 bool, or IEEE-754 bits when IsFloat). The writer restricts ops to the set FNA's runtime honors — anything else makes FNA throw at load time.

Fx2SamplerState

One sampler-state assignment inside a sampler parameter's value blob. Exactly one of TextureParameterName (op 164/Texture), FloatValue, or IntValue is set.

Fx2Shader

One compiled SM1–3 shader blob (bare D3D9 token stream with CTAB).

Fx2Technique

One fx_2_0 technique.

KnifxWriter

Serializes a ShadowDusk ShaderIR into KNI's KNIFX v11 effect container, the additive newer format KNI v4.02+ loads (signature "KNIF"). This is the KNIFX analogue of MgfxWriter: it carries the same MojoShader-dialect GLSL body ShadowDusk already produces for MGFX v10 (KNIFX is a container over a still-MojoShader body), re-serialized into KNIFX's binary layout.

Byte format reverse-engineered from KNI's own KNIFXWriter11 + the runtime Effect reader (full spec: plan/PHASE-35-appendix/knifx-format-spec.md). The headline deltas vs MGFX v10:

  • A multi-backend directory header (one .knifx can carry several backend bodies; the runtime picks the match). This writer emits one backend.
  • Counts and most indices are written as packed ints (zigzag + 7-bit).
  • New fields: a per-shader ShaderVersion, a Stage byte (with a compute slot), and a columnsActual byte on parameters.

The pass render-state blocks are byte-identical to v10, so MgfxWriter's writers are reused verbatim. Default product output stays MGFX v10; KNIFX is additive.

KnifxWriterOptions

Options for KnifxWriter.

MgfxPassInfo

One pass within a technique: its name, FX annotations, the vertex/pixel shader it binds (by index into the effect's shader list, or -1 when absent), and its render state.

MgfxSamplerInfo

One entry in a shader's sampler table, as MonoGame's Shader reader expects it. Name must match the sampler uniform's name in the emitted GLSL (e.g. ps_s0) so MonoGame's GL backend can bind it. Parameter indexes the texture parameter in the effect's global parameter table. State carries the baked sampler_state { MinFilter = …; AddressU = …; } members (Phase 43, F9); when null the record is written with hasState = 0 and MonoGame uses GraphicsDevice.SamplerStates instead.

MgfxSamplerStateInfo

A baked sampler state, field-for-field as MonoGame 3.8.2's Shader reader consumes it (AddressU/V/W, BorderColor RGBA, Filter, MaxAnisotropy, MaxMipLevel, MipMapLevelOfDetailBias) and as mgfxc's ShaderData.writer.cs emits it. Byte values are MonoGame enum ordinals (TextureAddressMode / TextureFilter).

MgfxSamplerStateResolver

Resolves parsed sampler_state { … } members into the baked MgfxSamplerStateInfo mgfxc writes into the .mgfx sampler record (Phase 43, F9). Mirrors mgfxc v3.8.2's TPGParser/SamplerStateInfo exactly:

  • State is only present (hasState = 1) when at least one recognized state member is set — a block containing only Texture = <…> resolves to null.
  • Defaults are MonoGame's SamplerState constructor values: Wrap addressing, white border color, MaxAnisotropy 4, MaxMipLevel 0, LOD bias 0.
  • The separate Min/Mag/Mip filter members combine into ONE MonoGame TextureFilter via mgfxc's UpdateSamplerState if-chain ("None" treated like "Point"); MipFilter = None additionally forces MipMapLevelOfDetailBias = -16 (mgfxc's only mip-disable mechanism).
  • Unrecognized KEYS are ignored (the parser accepted them; fxc tolerates many D3D9 states MonoGame has no analog for), but a recognized key with an unparseable VALUE fails loudly (SD0024) — mgfxc's grammar would reject it, so silently dropping it would diverge from the mgfxc build.
MgfxTechniqueInfo

One effect technique as the MGFX writer serializes it: its name, any FX annotations, and its ordered passes.

MgfxVertexAttributeInfo

One vertex-attribute table entry (GL profile only) mapping a GLSL attribute to a VertexElementUsage+index, as MonoGame's Shader reader expects for a vertex shader.

MgfxWriter

Serializes a fully assembled ShaderIR into the binary MGFX container MonoGame/KNI's Effect loads (the "MGFX" header, constant buffers, shader blobs, parameters, and techniques). Counts and sizes that the format stores in a single byte or int16 are guarded so an oversized effect fails loudly (SD002x) rather than silently truncating into a corrupt file. The emitted version is controlled via MgfxWriterOptions (default v10, the most backwards-compatible choice).

MgfxWriterOptions

Settings for the MgfxWriter: the backend MgfxProfile to stamp into the header and the MGFX container version to emit.

RenderStateBlock

The render states declared inside a single FX pass, parsed from the source by RenderStateParser. Every state is nullable so a state left unset in the source stays absent (rather than defaulting), which lets the MgfxWriter decide via the Has* gates whether to emit each of the three optional MGFX state-object headers. The FNA-only states (and KnownFnaThrowingStates) are consumed solely by the FNA fx_2_0 path and are deliberately kept out of the Has* gates so they never alter MGFX v10 output.

RenderStateParser

Parses the render-state assignments inside an FX pass block into a typed RenderStateBlock. Recognized states map to nullable fields (left unset when absent in the source); states fxc accepts but FNA's runtime would throw on are recorded in KnownFnaThrowingStates so the FNA path can fail loudly while the MGFX paths preserve fxc/mgfxc's silent-ignore behavior.

RuntimeProfileDetector

The runtime-detection advisory (Phase 35 auto-select seam 6): given a consumer's loaded XNA framework assembly, recommend the CapabilityProfile to compile for. This is the "auto-detect the runtime, give the newest proven experience" helper the consumer's game calls; the resulting profile is passed to Profile.

ShaderCompilerValidationExtensions

Validate / ValidateAsync for any IShaderCompiler — extension methods rather than interface members so they are reachable from a variable declared as the CONCRETE compiler type (e.g. var compiler = new EffectCompiler();, the pattern the README/quickstarts use for CompileAsync(string, CompilerOptions, CancellationToken)), not only from one declared as IShaderCompiler. A C# default interface method would compile only through the interface-typed reference — a footgun for exactly this "just add the package and call the API" promise. Every current and future IShaderCompiler implementation gets these for free, with nothing to override.

ShaderError

A single diagnostic produced during compilation, carrying the source location and the underlying compiler's message verbatim. Errors are returned (never swallowed or reformatted) so callers can surface the exact file, line, column, and message.

ShaderFeatureSupport

The single source of truth for which ShaderFeatures a shipping runtime is render-proven to consume, and the guard that enforces ShadowDusk's hard rule: never emit bytes no runtime can load. Requesting a feature with no downstream support is rejected loudly (SD0201) rather than silently compiled into unloadable output.

ShaderIR

ShadowDusk's backend-neutral intermediate representation of a whole effect, sitting between the parsed/reflected HLSL and the final MGFX emission: the constant buffers, per-pass compiled shader blobs, flattened effect parameters, and techniques the MgfxWriter serializes. It carries no platform-specific bytecode shape, so the same IR drives every MGFX-targeting backend.

ShaderTargetValidation

One target's slice of a ShaderValidationReport: whether the shader compiles for Target, the errors when it does not, and the non-fatal warnings when it does.

ShaderValidationReport

Everything wrong — and everything worth knowing — about one shader, across one or more targets. Produced by ValidateAsync(IShaderCompiler, string, CompilerOptions?, CancellationToken) / Validate(IShaderCompiler, string, CompilerOptions?, CancellationToken). The simplest use is to print it: ToString() renders the complete human-readable report (per-target status, every error with its source location and the compiler's verbatim text, every warning). For structured access walk Targets.

VulkanShaderCodeWrapper

Wraps raw SPIR-V bytecode in the Vulkan descriptor-layout header MonoGame 3.8.5's real Vulkan writer prepends before the per-shader ShaderCode field (see plan/DONE/PHASE-32-appendix/vulkan-mgfx-format-spec.md — read from MonoGame's own source, not reverse-engineered). MonoGame's DesktopVK Effect reader expects this exact shape for the Vulkan profile; every other profile writes raw bytecode unwrapped.

XnbWriter

Wraps a compiled effect payload in the XNA/MonoGame XNB content container, so a consumer can drop the file where their mgfxc-built .xnb used to sit and keep calling Content.Load<Effect>("Foo") without changing a line of code (issue #199, Phase 60).

This writer is deliberately only a container. The payload it wraps is the exact .mgfx / .knifx / .fxb ShadowDusk already produces and has render-proven, so no shader-compilation behaviour changes and no existing output byte moves. It is pure managed code with no native dependency, so it runs on every host including WASM and Android.

Byte layout (uncompressed single-effect asset), measured from real dotnet-mgcb 3.8.4.1 output for /platform: Windows, DesktopGL, Android, iOS and MacOSX — not transcribed from documentation:

'X' 'N' 'B'                 magic
<platform>            one of the runtime's accepted identifiers (see PlatformIdentifierFor)
0x05                        format version
0x00                        flags (bit 0x80 = LZX, 0x40 = LZ4; this writer emits uncompressed)
int32                       TOTAL file size, header included
7-bit                       type-reader count (always 1 for an effect)
  7-bit                     reader name length
  bytes                     reader name (EffectReaderTypeName)
  int32                     reader version (0)
7-bit                       shared-resource count (0)
7-bit                       type id of the primary object (1 = 1-based index into the readers)
int32                       payload length
bytes                       the effect payload, verbatim

Structs

GlslSource

A small wrapper around a GLSL source string produced by the transpiler.

Result<T, TError>

A lightweight discriminated union representing either a success value of type T or an error of type TError. ShadowDusk uses this instead of exception-as-control-flow: compilation outcomes are returned, not thrown.

Unit

A type with exactly one value, used as the success type of a Result<T, TError> when an operation has no meaningful return value but can still fail (the functional equivalent of void).

Interfaces

IShaderCompiler

The core consumer contract: compiles HLSL .fx source into a compiled effect entirely in memory, with no fxc.exe, mgfxc, Wine, or Windows SDK required. For the MonoGame/KNI targets the output is a .mgfx effect; for Fna it is the D3D9 fx_2_0 effects binary (.fxb) FNA loads. This is the product's public entry point — add the library to a project and call CompileAsync(string, CompilerOptions, CancellationToken) at runtime or build time.

Enums

BlendFunctionValue

How source and destination blend terms are combined (mirrors MonoGame's BlendFunction ordinals).

BlendValue

A blend factor applied to a source or destination color (mirrors MonoGame's Blend ordinals).

CompareFunctionValue

A depth- or stencil-test comparison function (mirrors MonoGame's CompareFunction ordinals).

CullModeValue

Which triangle faces are culled (mirrors MonoGame's CullMode ordinals).

DetectedRuntime

The XNA-reimplementation runtime a consumer's game is running on, classified from the loaded framework assembly's simple name (all three share the Microsoft.Xna.Framework namespace, so the assembly name is the only reliable fork discriminator). Used by RuntimeProfileDetector to recommend a CapabilityProfile.

DxbcBackend

Selects which backend compiles HLSL to SM5 DXBC for the DirectX target.

EffectContainer

Which effect container ShadowDusk serializes the compiled effect into.

Mgfx (the default) is the MGFX container, the universally loadable format every MonoGame and KNI runtime accepts (version controlled by MgfxVersion, default v10). This is the seamless product default and is never something a consumer must change to get correct output.

Knifx emits KNI's newer KNIFX v11 container (additive, opt-in) so consumers on KNI v4.02+ can use the newer container's features. It is an escape hatch / additive target, not a replacement for the v10 default. Ignored for Fna, whose output is always the D3D9 fx_2_0 container.

FillModeValue

How triangles are rasterized (mirrors MonoGame's FillMode ordinals).

KnifxBackend

The KNI graphics backend a KNIFX body targets. The value IS the on-disk GraphicsBackend enum integer KNI's runtime matches against its adapter (verified from Xna.Framework.Graphics/Graphics/GraphicsBackend.cs @ KNI main). Desktop SDL2.GL reports OpenGL; the browser reports WebGL.

MgfxProfile

The profile byte written into the MGFX binary header, identifying the shader backend an effect was compiled for. These byte values are deliberately NOT the same as PlatformTarget ordinals (PlatformTarget.DirectX=0, OpenGL=1 vs MgfxProfile.OpenGL=0, DirectX11=1) — always use these values when writing the format's profile byte.

PlatformTarget

The consumer runtime/loader an effect is compiled for. Each target is a distinct emitted artifact loaded by a different runtime path, so a shader must be compiled (and validated) per target.

ShaderDialect

How GLSL is emitted for an OpenGL-family target. This is the "dialect" capability axis of a CapabilityProfile: it selects whether ShadowDusk down-converts SPIR-V/GLSL into the MonoGame/KNI MojoShader dialect that every shipping GL runtime consumes today, or emits the un-down-converted modern GLSL a future runtime might consume. Non-GL targets (DirectX DXBC, FNA fx_2_0) carry NotApplicable.

ShaderErrorKind

Categorizes the kind of failure a ShaderError represents.

ShaderErrorSeverity

The severity level of a ShaderError.

ShaderFeatures

GL shader capabilities that ShadowDusk's MojoShader-dialect down-convert rejects today (every shipping MonoGame/KNI GL runtime is MojoShader-capped at SM2-3). A CapabilityProfile declares which it permits via AllowedFeatures, but a feature is only honored once a runtime is render-proven to consume it: ShaderFeatureSupport rejects any feature no shipping runtime supports, so ShadowDusk never emits bytes no runtime can load. As of 2026-06 NONE of these are runtime-supported (KNI's GL backend is still MojoShader), so every proven profile declares None.

ShaderStage

Which programmable shader stage a single compile request targets. The ordinals are stable (Vertex = 0, Pixel = 1) and are used to pick the matching shader profile/entry point when invoking the backend compiler.

StencilOperationValue

What happens to a stencil value on a test outcome (mirrors MonoGame's StencilOperation ordinals).