Table of Contents

Class FxParseResult

Namespace
ShadowDusk.HLSL.Ast
Assembly
ShadowDusk.HLSL.dll

The output of the FX9 pre-parser: stripped HLSL plus all extracted FX9 metadata.

public sealed record FxParseResult : IEquatable<FxParseResult>
Inheritance
FxParseResult
Implements
Inherited Members

Properties

ExplicitGlSamplerSlots

TEXTURE name -> the OpenGL texture unit an explicit register(sN) on its LEGACY sampler declaration pins it to. Keyed on the texture (synthesized X_SDTexture, or the one a sampler_state block references) because that is what the GL sampler table joins on. Empty when nothing declared a register.

This exists because the register clause is otherwise destroyed before DXC sees it: the SM4 rewrite turns sampler X : register(s2); into Texture2D X_SDTexture; SamplerState X;, so neither reflection nor the SPIR-V can recover the 2. The clause is recorded here rather than preserved in the rewritten HLSL deliberately: emitting it would change what DXC compiles and move the DirectX, DX12, Vulkan and FNA bytes, none of which have a reported defect. Recording it changes the OpenGL slot allocation only.

Legacy form only, and that is measured, not an oversight (2026-08-02). mgfxc honours the annotation exactly here, because compiled at ps_3_0 a legacy sampler IS the combined sampler and register(sN) pins its SM3 sampler register. For the modern spelling it does not: given Texture2D T : register(t3); SamplerState S : register(s2); the mgfxc OpenGL build puts the pair on slot 0 regardless, allocating by texture declaration order. Recording modern registers here would therefore make us DIVERGE.

public IReadOnlyDictionary<string, int> ExplicitGlSamplerSlots { get; init; }

Property Value

IReadOnlyDictionary<string, int>

ParameterAnnotations

Annotation blocks attached to global parameter declarations.

public required IReadOnlyList<ParameterAnnotation> ParameterAnnotations { get; init; }

Property Value

IReadOnlyList<ParameterAnnotation>

ReservedGlSamplerSlots

OpenGL sampler registers that an explicit register(sN) on a MODERN SamplerState declaration takes out of circulation, so a synthesized combined sampler must be allocated around them rather than onto them.

Compiling for OpenGL means compiling at ps_3_0, where a texture and a sampler are ONE object sharing a single register namespace. A modern SamplerState still sits at its declared register, but the combined sampler fxc synthesizes for each (texture, sampler) pair cannot reuse it. Measured: with one texture and SamplerState S : register(s0), mgfxc emits ps_s1, not ps_s0.

This is what makes the LEGACY case stop looking like a special case. There the sampler is the combined object, so it both reserves its register and occupies it, landing the pair exactly on N. Same allocator, different starting facts.

public IReadOnlySet<int> ReservedGlSamplerSlots { get; init; }

Property Value

IReadOnlySet<int>

Samplers

All sampler declarations with sampler_state blocks extracted from the source.

public required IReadOnlyList<SamplerInfo> Samplers { get; init; }

Property Value

IReadOnlyList<SamplerInfo>

StrippedHlsl

HLSL source with all FX9 blocks stripped, preserving line numbers for error reporting.

public required string StrippedHlsl { get; init; }

Property Value

string

Techniques

All technique blocks extracted from the source.

public required IReadOnlyList<TechniqueInfo> Techniques { get; init; }

Property Value

IReadOnlyList<TechniqueInfo>