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
.mgfxeffect; 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'sEffectconstructor.
- 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
#includedirectives 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):
texkillwith a partial destination writemask (vkd3d writes e.g..x; fxc always writes.xyzwand MojoShader hard-fails anything else: "TEXKILL writemask must be .xyzw").texldwhose 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").deffloat literals with |f| ≥ 2³² — vkd3d'sdiscardsentinel is −2³² (0xCF800000), and MojoShader'sMOJOSHADER_printFloatconverts the magnitude through a 32-bitunsigned longon Windows (LLP64), overflowing to±0.0in the translated source — sotexkill's< 0test 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 canonicaltexkill rK.xyzw(each tested lane now holds one of the originally-tested values), ormov rK, src0.<swizzle>followed bytexld 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.
- EffectParameterInfo
One .mgfx parameter record. MonoGame 3.8.2's
Effect.ReadParametersreads Elements (array elements) and Members (struct members) as RECURSIVE parameter collections — elements first, then members, each a full nested parameter record (mirroring mgfxc'sEffectObject.WriteParameter). A value-typed leaf (Scalar/Vector/Matrix with no elements/members) additionally carries a raw default-value blob ofRowCountColumnCount4bytes.
- 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 realfxc /T fx_2_0output (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
-1mean 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 runtimeEffectreader (full spec:plan/PHASE-35-appendix/knifx-format-spec.md). The headline deltas vs MGFX v10:- A multi-backend directory header (one
.knifxcan 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, aStagebyte (with a compute slot), and acolumnsActualbyte on parameters.
- A multi-backend directory header (one
- 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
-1when absent), and its render state.
- MgfxSamplerInfo
One entry in a shader's sampler table, as MonoGame's
Shaderreader 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 bakedsampler_state { MinFilter = …; AddressU = …; }members (Phase 43, F9); when null the record is written withhasState = 0and MonoGame usesGraphicsDevice.SamplerStatesinstead.
- MgfxSamplerStateInfo
A baked sampler state, field-for-field as MonoGame 3.8.2's
Shaderreader consumes it (AddressU/V/W, BorderColor RGBA, Filter, MaxAnisotropy, MaxMipLevel, MipMapLevelOfDetailBias) and as mgfxc'sShaderData.writer.csemits it. Byte values are MonoGame enum ordinals (TextureAddressMode / TextureFilter).
- MgfxSamplerStateResolver
Resolves parsed
sampler_state { … }members into the baked MgfxSamplerStateInfo mgfxc writes into the.mgfxsampler record (Phase 43, F9). Mirrors mgfxc v3.8.2'sTPGParser/SamplerStateInfoexactly:- State is only present (
hasState = 1) when at least one recognized state member is set — a block containing onlyTexture = <…>resolves to null. - Defaults are MonoGame's
SamplerStateconstructor values: Wrap addressing, white border color, MaxAnisotropy 4, MaxMipLevel 0, LOD bias 0. - The separate Min/Mag/Mip filter members combine into ONE MonoGame
TextureFiltervia mgfxc'sUpdateSamplerStateif-chain ("None" treated like "Point");MipFilter = Noneadditionally forcesMipMapLevelOfDetailBias = -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.
- State is only present (
- 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'sShaderreader expects for a vertex shader.
- MgfxWriter
Serializes a fully assembled ShaderIR into the binary MGFX container MonoGame/KNI's
Effectloads (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 theHas*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.
- 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.
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
Tor an error of typeTError. 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
.fxsource into a compiled effect entirely in memory, with nofxc.exe,mgfxc, Wine, or Windows SDK required. For the MonoGame/KNI targets the output is a.mgfxeffect; 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
BlendFunctionordinals).
- BlendValue
A blend factor applied to a source or destination color (mirrors MonoGame's
Blendordinals).
- CompareFunctionValue
A depth- or stencil-test comparison function (mirrors MonoGame's
CompareFunctionordinals).
- CullModeValue
Which triangle faces are culled (mirrors MonoGame's
CullModeordinals).
- 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.Frameworknamespace, 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
FillModeordinals).
- KnifxBackend
The KNI graphics backend a KNIFX body targets. The value IS the on-disk
GraphicsBackendenum integer KNI's runtime matches against its adapter (verified fromXna.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=1vsMgfxProfile.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
StencilOperationordinals).