Class FxParseResult
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
DeferredLegacyIntrinsicCalls
The tex2Dlod / tex2Dbias / tex2Dproj calls this RAW-text parse left
verbatim because the right rewrite depends on which #if branch compiles (one body
under two conditional headers, or the bound texture declared with a different type per
branch), each as the FX0012 it would be if no preprocessed re-parse could resolve
it. A compile with any of these must be redone from the preprocessed source (the
legacy-sampler recovery), where every conditional is resolved; DirectX 11, whose compiler
would otherwise take the unrewritten call silently, does so up front. Always empty on a
preprocessed parse.
public IReadOnlyList<FxParseError> DeferredLegacyIntrinsicCalls { get; init; }
Property Value
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.
This is the reading of whatever text the parser was given, which for a compile
is the raw source: a register that only exists in an inactive #if branch is counted
and one spelled through a macro is missed (issue #299). The OpenGL target and the raylib
converter therefore take this map from
CollectGlSamplerSlots(string, string, FxParseResult), which
reads it off the preprocessed view the way mgfxc does.
public IReadOnlyDictionary<string, int> ExplicitGlSamplerSlots { get; init; }
Property Value
ParameterAnnotations
Annotation blocks attached to global parameter declarations.
public required IReadOnlyList<ParameterAnnotation> ParameterAnnotations { get; init; }
Property Value
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
RestructuredHelpers
The helpers StrippedHlsl defines for tex2Dlod / tex2Dbias /
tex2Dproj calls: each name, the intrinsic it stands for, and where its definition
sits in StrippedHlsl (1-based line and columns, end exclusive). Lets a
diagnostic about a call name the intrinsic the author wrote, while one located inside a
definition (the helper's own placement) is left as the compiler wrote it.
public IReadOnlyList<RestructuredHelperDefinition> RestructuredHelpers { get; init; }
Property Value
Samplers
All sampler declarations with sampler_state blocks extracted from the source.
public required IReadOnlyList<SamplerInfo> Samplers { get; init; }
Property Value
StrippedHlsl
HLSL source with all FX9 blocks stripped, preserving line numbers for error reporting.
public required string StrippedHlsl { get; init; }
Property Value
SynthesizedSamplerTextures
SAMPLER name -> the Texture2D the SM4 rewrite SYNTHESIZED for it, for every legacy
sampler that binds no texture of its own (sampler2D A;, sampler A : register(s1);,
sampler2D A = sampler_state { MinFilter = Point; };). The rewrite has to give the
texture a name of its own (A_SDTexture), because HLSL cannot declare a
Texture2D A beside the SamplerState A; but to mgfxc the legacy
sampler is ONE object, and the effect parameter it emits for it is named A on every
profile. The compiler uses this map to give the parameter that name back, so
effect.Parameters["A"] finds the texture exactly as it does under mgfxc.
Empty when nothing was synthesized (including the FNA fx_2_0 mode, which keeps
the legacy declaration verbatim).
public IReadOnlyDictionary<string, string> SynthesizedSamplerTextures { get; init; }
Property Value
Techniques
All technique blocks extracted from the source.
public required IReadOnlyList<TechniqueInfo> Techniques { get; init; }
Property Value
UndeclaredStateTextures
SAMPLER name -> the texture its sampler_state block names (Texture = <T>),
for every legacy sampler the rewrite turned into SamplerState whose texture the MAIN
file's code never declares or mentions. mgfxc reads the name off the state block and
emits a T parameter even when nothing declares T, while the rewritten
T.Sample(...) needs a declaration. These are CANDIDATES: an #included file or a
macro can still declare T, so the compiler adds Texture2D T; only when the
preprocessed source (includes inlined, macros expanded) declares no T.
public IReadOnlyDictionary<string, string> UndeclaredStateTextures { get; init; }