refactor: gate MountainLit NormalMapMix behind _NORMALMAP - #9836
Conversation
normalTS is only consumed under _NORMALMAP (InitializeInputData); in keyword-off variants the splat normal-map sampling chain feeds a value that is never read. D3D11 DXBC for the shipping keyword-off ForwardLit variant is bit-identical before/after - source hygiene plus insurance for future variants that start reading surfaceData.
|
Slack notification sent to #explorer-ext-contributions for external review. |
decentraland-bot
left a comment
There was a problem hiding this comment.
PR Review: perf: gate MountainLit NormalMapMix behind _NORMALMAP
STEP 2 — Root-cause check: PASS
This PR is not fixing a bug — it's making a dead-code path explicit at the source level. The NormalMapMix call produces normalTS, which is only consumed inside #ifdef _NORMALMAP in InitializeInputData (MountainLitForwardPass.hlsl:72). The #else branch (MountainLitForwardPass.hlsl:74) uses input.normalWS directly and ignores normalTS entirely. The DepthNormals pass has its own local normalTS sampling at MountainLitDepthNormalsPass.hlsl:98. The change correctly gates computation to match its consumption guard — not a symptom fix.
STEP 3 — Design & integration: PASS
No new long-lived units, systems, or lifecycle management. This is a 7-line preprocessor guard in an HLSL shader file. The guard is placed at the call site in InitializeSimpleLitSurfaceData (line 254), which mirrors the consumption guard in InitializeInputData (MountainLitForwardPass.hlsl:70-74). This is the correct placement — gating at the call site rather than inside NormalMapMix itself keeps the function reusable for future passes that might need the normal under _NORMALMAP.
The _NORMALMAP keyword is declared as shader_feature_local in MountainLit.shader (ForwardLit and DepthNormals passes), and the sole shipping material runs keyword-off. No design concerns.
STEP 4 — Member audit: N/A
HLSL shader code — no C# public properties or accessors to audit.
STEP 5 — Line-level review
Code correctness:
- ✅
half3(0.0h, 0.0h, 1.0h)is the correct tangent-space identity normal ("no perturbation"). - ✅ The
#ifdef _NORMALMAPguard correctly matches the consumption guard inInitializeInputData. - ✅ The comment is accurate, explains why the guard exists, and does not narrate caller behavior.
- ✅ No resource leaks, no security issues, no performance regressions.
- ✅ No issues found across any of the blocking-issue categories (1–11) or design smell categories.
Observation (not a finding): The NormalMapMix function body (lines ~189–199) still contains commented-out #if defined(_NORMALMAP) / #endif guards from a previous iteration. This is pre-existing dead code, not introduced by this PR. No action required here.
CI: ⚠️ Semantic title check failing
[P2] The semantic / title-matches-convention check is failing because perf is not in the repo's accepted type list:
Available types:
break,chore,docs,feat,fix,refactor,revert,style,test,release
Suggested fix: rename the PR title to refactor: gate MountainLit NormalMapMix behind _NORMALMAP — the change restructures shader code to be explicit about preprocessor guards without changing behavior, which fits refactor semantics.
Security review
No security concerns. This is a pure HLSL preprocessor guard change — no network, authentication, user input, or data processing code is involved.
REVIEW_RESULT: PASS ✅
COMPLEXITY: SIMPLE
COMPLEXITY_REASON: Single HLSL shader file, 7-line preprocessor guard addition — no ECS, async, or C# changes.
QA_REQUIRED: YES
Reviewed by Jarvis 🤖 · Requested by unknown (<@unknown>) via Slack
|
PR #9836, run #32580290117 Builds: Windows change, Windows baseline, macOS change, macOS baseline How to read this table
Intel Core i5
Exception breakdown
Apple M1
|
What
Wraps the
NormalMapMixcall inMountainLitInput.hlslin#ifdef _NORMALMAP(elsenormalTS = (0,0,1)).Why it is provably a no-op today
normalTSis only consumed inside#ifdef _NORMALMAP(MountainLitForwardPass.hlslInitializeInputData); the DepthNormals pass samples its own normal independently. The keyword is already declared (shader_feature_local), and the sole shipping material runs keyword-off.ShaderData.CompileVariant, keyword-off ForwardLit): compiled DXBC is bit-identical before/after — the compiler already dead-code-eliminates the chain.Why land it anyway
Source-level the keyword-off variant reads as paying 2 texture samples + unpack + normalize per terrain pixel for an unread value; the guard makes the deadness explicit and protects future variants (decal/debug paths that start reading
surfaceData) from silently paying it for real. Zero behavior, property, keyword-set, or batching surface.Found during a shader-optimization audit; landing separately from the measurable optimizations since it needs no measurement.