Table of Contents

Class FxPreParser

Namespace
ShadowDusk.HLSL
Assembly
ShadowDusk.HLSL.dll

FX9 pre-parser: strips technique, pass, and sampler_state blocks from .fx source and extracts all FX9 metadata needed by the compilation pipeline. DXC rejects these constructs, so they must be removed before invoking DXC. Stripped output preserves original line numbers by replacing removed lines with blank lines. FxSourceMode selects how legacy D3D9 constructs in the shader body are treated: rewritten forward to SM4 for DXC (the default), or preserved verbatim for an SM1–3 backend (the FNA fx_2_0 target, compiled by vkd3d).

public sealed class FxPreParser
Inheritance
FxPreParser
Inherited Members

Methods

CollectGlSamplerSlots(string, string, FxParseResult)

Both halves of the OpenGL sampler-register decision, read off ONE preprocessed view of the source, the way mgfxc reads them (issues #283 and #299):

Why the explicit map needs the preprocessed view too (issue #299). The pre-parser records a legacy sampler's register from the raw tokens, so it sees both arms of an #if and no macro expansion. Measured against the pinned mgfxc 3.8.4.1 /Profile:OpenGL: #if OPENGL / sampler S = sampler_state { … }; / #else / sampler S : register(s1) = sampler_state { … }; is ps_s0 (the raw reading said ps_s1, off SpriteBatch's unit 0), and #define REG s1 / sampler S : register(REG); is ps_s1 (the raw reading missed it: ps_s0).

Which declarations count is unchanged from the raw reading, only the text is different: a sampler-type keyword, the name, then the exact register ( sN ) clause; and only a sampler the SM4 rewrite bound to a texture (one a legacy intrinsic such as tex2D reads) gets an entry. The sampler-to-texture join comes from parsed, the parse of the source that is actually compiled, because that is the texture the rewritten HLSL samples through. Both names of that join are resolved through the preprocessor first, so a sampler or texture whose NAME is a macro (#define SAMP MySampler / sampler SAMP : register(s1);) still finds its declaration in the view and its texture in the SPIR-V.

public static Result<GlSamplerSlots, ShaderError> CollectGlSamplerSlots(string flattenedSource, string sourceFile, FxParseResult parsed)

Parameters

flattenedSource string

The RAW effect source with #includes inlined and the compile's macros prepended as #define lines (the output of ShadowDusk.Core.Preprocessor.Preprocessor.Flatten).

sourceFile string

Display name used in diagnostics.

parsed FxParseResult

The pre-parse of the same effect that feeds the compile.

Returns

Result<GlSamplerSlots, ShaderError>

Both maps, or an SD0009 error when the preprocessed view cannot be built.

CollectReservedGlSamplerSlots(string, string)

The OpenGL sampler registers reserved by modern SamplerState X : register(sN) declarations, decided on the PREPROCESSED source, the way mgfxc decides it (issue #283).

ReservedGlSamplerSlots is read from the tokens the pre-parser was given, which for a compile is the raw source: a register that only exists in an inactive #if branch is counted there, one written through a macro (#define SLOT(n) : register(n)) is missed, and one in an #included file is never seen. This entry point first builds the preprocessed view of flattenedSource (conditionals evaluated, macros expanded, with the #defines the flattener prepended for the target and the user's defines), then runs the same declaration matcher over it. It is pure managed code, so the answer is identical on every host, the browser included.

public static Result<IReadOnlySet<int>, ShaderError> CollectReservedGlSamplerSlots(string flattenedSource, string sourceFile)

Parameters

flattenedSource string

The effect source with #includes inlined and the compile's macros prepended as #define lines (the output of ShadowDusk.Core.Preprocessor.Preprocessor.Flatten).

sourceFile string

Display name used in diagnostics.

Returns

Result<IReadOnlySet<int>, ShaderError>

The reserved register indices, or an SD0009 error when the preprocessed view cannot be built (a directive or #if expression this preprocessor does not model).

DeclareUndeclaredStateTextures(string, string, FxParseResult)

For each candidate texture (UndeclaredStateTextures) that the macro-expanded, include-inlined flattened text declares nowhere, adds Texture2D T; once, ahead of the first top-level code in the file. "Declares" is a declaration scan of the preprocessed view, not a spelling match: any type whose name starts with texture (any case: texture, Texture2D, TextureCube, Texture2DArray, texture2D), optional template arguments (Texture2D<float4>, MonoGame's own Macros.fxh spelling), then a comma-separated declarator list with arrays, registers and semantics. The declaration goes at the start of the file rather than beside the sampler, because the sampler can be declared once per #if branch and the first one in the text may be in the branch that does not compile; it is put on the line of the first code outside every conditional, so no line number moves.

public static Result<string, ShaderError> DeclareUndeclaredStateTextures(string flattened, string sourceFileName, FxParseResult parsed)

Parameters

flattened string

The rewritten source with includes inlined and the compile's macros prepended.

sourceFileName string

Display name for diagnostics.

parsed FxParseResult

The pre-parse that produced the rewrite (its UndeclaredStateTextures).

Returns

Result<string, ShaderError>

The text to compile, or SD0009 when the preprocessed view cannot be built.

HasLegacySamplerResidue(string, string)

Whether flattenedSource, once PREPROCESSED, still holds legacy D3D9 sampler syntax that DXC does not compile: a sampler type it has no keyword for (sampler2D, samplerCUBE, sampler_state …) or a call to a legacy sampling intrinsic (tex2D and its family).

This is the test for "the pre-parser's SM4 rewrite missed something" (issue #308). The rewrite reads the raw tokens of the main file, so a legacy sampler declared in an #included file, one whose register clause or whole declaration comes out of a macro (DECLARE_TEXTURE(S, 1)), or a tex2D hidden in a macro body (SAMPLE_TEXTURE(S, uv)) reaches the compiler unrewritten. Pass the text the compiler was actually given (the rewritten source, #includes inlined); it is preprocessed here first, because on raw text the same syntax can sit in a branch the target never compiles.

public static Result<LegacySamplerResidueCheck, ShaderError> HasLegacySamplerResidue(string flattenedSource, string sourceFile)

Parameters

flattenedSource string

The compiler's input with #includes inlined and the compile's macros prepended (the output of ShadowDusk.Core.Preprocessor.Preprocessor.Flatten).

sourceFile string

Display name used in diagnostics.

Returns

Result<LegacySamplerResidueCheck, ShaderError>

Whether such syntax remains, and the compiler-predefined macro a conditional tested if one did (the view evaluated it as undefined, so the answer may not be what the compiler saw); or an SD0009 error when the preprocessed view cannot be built.

IsDirectX11Profile(string)

Returns true when profile (a recognized profile, already lowercased) is one MonoGame's DirectX_11 shader profile accepts. Anything else is below that target's floor and is rejected by mgfxc with "must be SM 4.0 level 9.1 or higher!"; ShadowDusk reports the same condition as SD0015. See the empirical note on the backing set.

public static bool IsDirectX11Profile(string profile)

Parameters

profile string

Returns

bool

IsKnownProfile(string)

Returns true when the given profile string (already lowercased) is a recognized shader profile.

public static bool IsKnownProfile(string profile)

Parameters

profile string

Returns

bool

LooksLikeProfile(string)

Returns true when token is SHAPED like a shader profile (a vs_/ps_/gs_/hs_/ds_/cs_ stage prefix immediately followed by a digit) REGARDLESS of whether it is a recognized profile. This distinguishes a typo'd real-shaped profile (ps_9_9) — which is unconditionally invalid and can be rejected without any macro expansion — from a macro NAME (PS_SHADERMODEL), which may still expand to a valid profile. It is NOT a substitute for IsKnownProfile(string): a profile-shaped token is not necessarily a real profile.

public static bool LooksLikeProfile(string token)

Parameters

token string

Returns

bool

MentionsLegacySamplerSyntax(string)

A plain word search for the same legacy sampler syntax HasLegacySamplerResidue(string, string) looks for, with NO preprocessing: it also sees a macro body and an inactive branch. Only good for "this effect uses that syntax somewhere", which is all the caller needs when the preprocessed view cannot be built.

public static bool MentionsLegacySamplerSyntax(string source)

Parameters

source string

Any effect text.

Returns

bool

MentionsLegacySamplerSyntaxInCode(string)

MentionsLegacySamplerSyntax(string) on CODE only: a legacy-sampler word inside a comment (// was tex2D(...)) does not count. For callers that already have the preprocessed text and must not change behavior because of a comment.

public static bool MentionsLegacySamplerSyntaxInCode(string source)

Parameters

source string

Any effect text.

Returns

bool

Parse(string, string)

Parses an FX9 .fx source file, strips FX9-specific blocks, and extracts metadata, rewriting legacy D3D9 constructs forward to SM4 (RewriteToSm4).

public static Result<FxParseResult, FxParseError> Parse(string source, string sourceFile)

Parameters

source string

Full text of the .fx file.

sourceFile string

Display name used in diagnostics (file path or virtual name).

Returns

Result<FxParseResult, FxParseError>

Parse(string, string, FxSourceMode)

Parses an FX9 .fx source file, strips FX9-specific blocks, and extracts metadata.

public static Result<FxParseResult, FxParseError> Parse(string source, string sourceFile, FxSourceMode mode)

Parameters

source string

Full text of the .fx file.

sourceFile string

Display name used in diagnostics (file path or virtual name).

mode FxSourceMode

How legacy D3D9/SM3 constructs in the shader body are treated (rewritten forward for DXC, or preserved verbatim for an SM1–3 backend).

Returns

Result<FxParseResult, FxParseError>

ParsePreprocessed(string, string)

Pre-parses the PREPROCESSED form of an effect (issue #308): the legacy-sampler recovery for an effect the compiler rejected because the raw pre-parse could not see a legacy sampler declaration or tex2D call that an #include or a macro supplies.

flattenedRawSource is preprocessed by the managed ShadowDusk.HLSL.Preprocessing.FxMacroPreprocessor (conditionals evaluated, macros expanded, one output line per input line, #line and #pragma passed through), and the ordinary pre-parse then runs on THAT text, which is how mgfxc itself works: it preprocesses first and parses techniques and samplers second. The returned StrippedHlsl is the text to hand to the compiler in place of the flattened raw source; every span in the result, and any error, is mapped back onto the author's lines through the #line directives.

It is a RECOVERY, never the first attempt: an effect that compiles from its raw source keeps compiling from exactly that source, so no output byte moves for it.

public static Result<FxPreprocessedParse, ShaderError> ParsePreprocessed(string flattenedRawSource, string sourceFile)

Parameters

flattenedRawSource string

The RAW effect source (before any pre-parse) with #includes inlined and the compile's macros prepended (the output of ShadowDusk.Core.Preprocessor.Preprocessor.Flatten).

sourceFile string

Display name used in diagnostics.

Returns

Result<FxPreprocessedParse, ShaderError>

The parse, or an error: SD0009 when the preprocessed text cannot be built, or the pre-parser's own FXnnnn diagnostic for the preprocessed text.

ParseTechniquesPreprocessed(string, string)

The techniques and passes of an effect, read off its PREPROCESSED translation unit, the way mgfxc reads them (issue #407).

The ordinary pre-parse reads techniques from the raw tokens of the main file, so a technique inside an inactive #if was still compiled, a technique in an #included file was never found, and a macro used as a compile target (compile VS_SHADERMODEL V()) was recorded as its unexpanded name. flattenedRawSource is preprocessed here first by the managed ShadowDusk.HLSL.Preprocessing.FxMacroPreprocessor (conditionals evaluated with the compile's macros, macros expanded, one output line per input line), and only the technique blocks are then parsed from it. Every span is mapped back onto the author's lines.

A conditional on a compiler-predefined macro (__HLSL_VERSION …) is evaluated as undefined, which is what mgfxc's own effect preprocessor does when it finds the techniques: that preprocessor never defines the compiler's macros either.

public static Result<FxPreprocessedTechniques, ShaderError> ParseTechniquesPreprocessed(string flattenedRawSource, string sourceFile)

Parameters

flattenedRawSource string

The RAW effect source (before any pre-parse) with #includes inlined and the compile's macros prepended (the output of ShadowDusk.Core.Preprocessor.Preprocessor.Flatten).

sourceFile string

Display name used in diagnostics.

Returns

Result<FxPreprocessedTechniques, ShaderError>

The techniques, or an error: SD0009 when the preprocessed text cannot be built, or the pre-parser's own FXnnnn diagnostic for a technique block in it (a duplicate technique name is FX0005 here, where it is real).

StripTechniqueBlocks(string, string)

Blanks every technique block written literally in source (a technique / technique10 / technique11 keyword at global scope, an optional name and annotation block, then a balanced brace body), keeping every line break so no line number moves. Used on the compiler's input once techniques come from the preprocessed translation unit (issue #407): the main file's blocks are already stripped by the pre-parse, so what this removes is a technique in an #included file, which the compiler would otherwise be handed as effect syntax. A macro call (TECHNIQUE(...)) and a block whose braces do not balance are left as they are.

public static string StripTechniqueBlocks(string source, string sourceFile)

Parameters

source string

Any effect text, typically the flattened compiler input.

sourceFile string

Display name used by the lexer.

Returns

string

source itself when it holds no such block.