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):
- Reserved: the registers modern
SamplerState X : register(sN)declarations take out of circulation, exactly what CollectReservedGlSamplerSlots(string, string) returns. - Explicit: texture name -> the register an
explicit
register(sN)on its LEGACY sampler declaration pins it to, the preprocessed counterpart of ExplicitGlSamplerSlots.
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
flattenedSourcestringThe RAW effect source with
#includes inlined and the compile's macros prepended as#definelines (the output ofShadowDusk.Core.Preprocessor.Preprocessor.Flatten).sourceFilestringDisplay name used in diagnostics.
parsedFxParseResultThe pre-parse of the same effect that feeds the compile.
Returns
- Result<GlSamplerSlots, ShaderError>
Both maps, or an
SD0009error 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
flattenedSourcestringThe effect source with
#includes inlined and the compile's macros prepended as#definelines (the output ofShadowDusk.Core.Preprocessor.Preprocessor.Flatten).sourceFilestringDisplay name used in diagnostics.
Returns
- Result<IReadOnlySet<int>, ShaderError>
The reserved register indices, or an
SD0009error when the preprocessed view cannot be built (a directive or#ifexpression 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
flattenedstringThe rewritten source with includes inlined and the compile's macros prepended.
sourceFileNamestringDisplay name for diagnostics.
parsedFxParseResultThe pre-parse that produced the rewrite (its UndeclaredStateTextures).
Returns
- Result<string, ShaderError>
The text to compile, or
SD0009when 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
flattenedSourcestringThe compiler's input with
#includes inlined and the compile's macros prepended (the output ofShadowDusk.Core.Preprocessor.Preprocessor.Flatten).sourceFilestringDisplay 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
SD0009error 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
profilestring
Returns
IsKnownProfile(string)
Returns true when the given profile string (already lowercased) is a recognized shader profile.
public static bool IsKnownProfile(string profile)
Parameters
profilestring
Returns
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
tokenstring
Returns
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
sourcestringAny effect text.
Returns
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
sourcestringAny effect text.
Returns
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
sourcestringFull text of the .fx file.
sourceFilestringDisplay name used in diagnostics (file path or virtual name).
Returns
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
sourcestringFull text of the .fx file.
sourceFilestringDisplay name used in diagnostics (file path or virtual name).
modeFxSourceModeHow legacy D3D9/SM3 constructs in the shader body are treated (rewritten forward for DXC, or preserved verbatim for an SM1–3 backend).
Returns
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
flattenedRawSourcestringThe RAW effect source (before any pre-parse) with
#includes inlined and the compile's macros prepended (the output ofShadowDusk.Core.Preprocessor.Preprocessor.Flatten).sourceFilestringDisplay name used in diagnostics.
Returns
- Result<FxPreprocessedParse, ShaderError>
The parse, or an error:
SD0009when the preprocessed text cannot be built, or the pre-parser's ownFXnnnndiagnostic 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
flattenedRawSourcestringThe RAW effect source (before any pre-parse) with
#includes inlined and the compile's macros prepended (the output ofShadowDusk.Core.Preprocessor.Preprocessor.Flatten).sourceFilestringDisplay name used in diagnostics.
Returns
- Result<FxPreprocessedTechniques, ShaderError>
The techniques, or an error:
SD0009when the preprocessed text cannot be built, or the pre-parser's ownFXnnnndiagnostic for a technique block in it (a duplicate technique name isFX0005here, 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
sourcestringAny effect text, typically the flattened compiler input.
sourceFilestringDisplay name used by the lexer.
Returns
- string
sourceitself when it holds no such block.