Visitar URL original
Copy assigned by-value parameters when inlining shader functions by bkaradzic-microsoft · Pull Request #19077 · BabylonJS/Babylon.js · GitHub
Skip to content

Copy assigned by-value parameters when inlining shader functions - #19077

Open
bkaradzic-microsoft wants to merge 11 commits into
BabylonJS:masterfrom
bkaradzic-microsoft:fix/shader-inliner-param-copy
Open

bkaradzic-microsoft wants to merge 11 commits into
BabylonJS:masterfrom
bkaradzic-microsoft:fix/shader-inliner-param-copy

Conversation

@bkaradzic-microsoft

@bkaradzic-microsoft bkaradzic-microsoft commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

ShaderCodeInliner (used by NativeEngine) inlines a function by substituting the call arguments for the parameter names in the function body. If the body assigns to a by-value parameter, the assignment is applied to the caller's variable. sampleRadianceAnisotropic in openpbrIblFunctions.fx does alphaG = alphaB;. After inlining, this became specularAlphaG = alphaB; in openpbrEnvironmentLighting.fx. That zeroed specularAlphaG at full anisotropy and changed the environment specular/diffuse mix that follows. WebGL doesn't inline, so it was unaffected. When the body writes to a by-value parameter (not out/inout/const, not an array), the inliner now copies the argument into a local variable at each call site. Parameters that are only read are still substituted directly, so the generated code is unchanged for every other function. Results: - OpenPBR IBL Anisotropy Metal (#GRQHVV#208) on Babylon Native D3D11: the diff against the reference drops from 3.47% to 0.50%. - No regressions across the rest of the Babylon Native validation suite (698/698). - Added a unit test for the inliner.

Copilot AI balanced review requested due to automatic review settings October 29, 2026 00:44
@bjsplat

bjsplat commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s).
To prevent this PR from going to the changelog marked it with the "skip changelog" label.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The inliner misses writes and writable calls that can mutate callers’ by-value arguments, and copied locals lose precision qualifiers that can affect shader results.

3 open issues
What changed in this PR

This PR updates ShaderCodeInliner to protect caller variables when inlined shader functions assign to by-value parameters, addressing an OpenPBR anisotropy rendering issue in Babylon Native and the WebGPU GLSL path.

Changes:

  • Detects assigned by-value parameters and creates local copies at each call site.
  • Preserves direct substitution for read-only parameters.
  • Adds unit tests for caller-variable protection and read-only substitution.
File Description
packages/​dev/​core/​test/​unit/​Engines/​shaderCodeInliner.test.ts Adds regression tests for assigned and read-only parameters.
packages/​dev/​core/​src/​Engines/​Processors/​shaderCodeInliner.ts Detects parameter writes and generates local argument copies during inlining.

Review effort: Balanced

@deltakosh deltakosh self-assigned this Oct 29, 2026

@deltakosh deltakosh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @bkaradzic-microsoft,

The fix is correct and scoped: directly written by-value parameters get local copies, read-only substitution stays unchanged, and the tests cover both.

An optional, non-blocking follow-up is handling indirect writes through out/inout calls, which still leak to the caller as before.

Thanks!

@deltakosh
deltakosh requested a review from Popov72 October 29, 2026 01:00
@bjsplat

bjsplat commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

Snapshot stored with reference name:
refs/pull/19077/merge

Test environment:
https://snapshots-cvgtc2eugrd3cgfd.z01.azurefd.net/refs/pull/19077/merge/index.html

To test a playground add it to the URL, for example:

https://snapshots-cvgtc2eugrd3cgfd.z01.azurefd.net/refs/pull/19077/merge/index.html#WGZLGJ#4600

Links to test your changes to core in the published versions of the Babylon tools (does not contain changes you made to the tools themselves):

https://playground.babylonjs.com/?snapshot=refs/pull/19077/merge
https://sandbox.babylonjs.com/?snapshot=refs/pull/19077/merge
https://gui.babylonjs.com/?snapshot=refs/pull/19077/merge
https://nme.babylonjs.com/?snapshot=refs/pull/19077/merge

To test the snapshot in the playground with a playground ID add it after the snapshot query string:

https://playground.babylonjs.com/?snapshot=refs/pull/19077/merge#BCU1XR#0

If you made changes to the sandbox or playground in this PR, additional comments will be generated soon containing links to the dev versions of those tools.

@bjsplat

bjsplat commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

@bjsplat

bjsplat commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

🟢 Memory Leak Test Results

4 passed, 0 leaked out of 4 scenarios

🟢 All memory leak tests passed — no leaks detected.

Passed Scenarios (4)
Scenario Package
Core Playground #2FDQT5#1508 @babylonjs/core
Core Playground #T90MQ4#14 @babylonjs/core
Core Playground #8EDB5N#2 @babylonjs/core
Core Playground #LL5BIQ#636 @babylonjs/core

@bjsplat

bjsplat commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

⚡ Performance Test Results

🟢 All performance tests passed — no regressions detected.

@bjsplat

bjsplat commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

@bjsplat

bjsplat commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

@deltakosh deltakosh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @bkaradzic-microsoft,

Please restrict OpaqueTypeRegexp (shaderCodeInliner.ts:15) to complete opaque type names. It is not anchored at the end, so it matches valid structs such as textureState or samplerParams; assigning to their by-value parameters now modifies the caller instead of a local copy. Please add regression coverage for this case.

Thanks!

bkaradzic-microsoft and others added 2 commits October 29, 2026 08:25
ShaderCodeInliner substitutes the call arguments for the parameter names
in the function body. When the body assigns to a by-value parameter, the
assignment is applied to the caller's variable. For example,
sampleRadianceAnisotropic does `alphaG = alphaB;`, which became
`specularAlphaG = alphaB;` in the caller and changed the OpenPBR
anisotropic environment specular/diffuse mix on Babylon Native and WebGPU.

Such parameters are now copied into a local variable at each call site.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
- Detect writes via assignment, ++/--, nested subscripts/swizzles, and passing as out/inout argument (user functions and GLSL builtins)
- Strip comments before analysis
- Preserve precision qualifiers on copies; never copy opaque types
- Collision-free names for copies and return temporaries

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
@bkaradzic-microsoft
bkaradzic-microsoft force-pushed the fix/shader-inliner-param-copy branch from df1a0cc to 8624c01 Compare October 29, 2026 15:25
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
@bkaradzic-microsoft

Copy link
Copy Markdown
Member Author

Fixed in 27e1b64. OpaqueTypeRegexp is now anchored and only matches complete GLSL opaque type names ([iu]?sampler/image/texture + 1D/2D/3D/Cube/2DRect/Buffer/ExternalOES with optional MS/Array/Shadow, plain sampler/samplerShadow, and atomic_uint). Added a regression test with textureState and samplerParams struct parameters, which are now copied.

@bjsplat

bjsplat commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

@deltakosh deltakosh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @bkaradzic-microsoft,

The anchored opaque-type matching and the struct regression test resolve my earlier request.

Thanks!

@bjsplat

bjsplat commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

@bjsplat

bjsplat commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

🟢 Memory Leak Test Results

4 passed, 0 leaked out of 4 scenarios

🟢 All memory leak tests passed — no leaks detected.

Passed Scenarios (4)
Scenario Package
Core Playground #2FDQT5#1508 @babylonjs/core
Core Playground #T90MQ4#14 @babylonjs/core
Core Playground #8EDB5N#2 @babylonjs/core
Core Playground #LL5BIQ#636 @babylonjs/core

@bjsplat

bjsplat commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

@bjsplat

bjsplat commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

⚡ Performance Test Results

🟢 All performance tests passed — no regressions detected.

@bjsplat

bjsplat commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
_replaceNames now skips identifiers preceded by a "."  (allowing whitespace and
comments in between), so a copied parameter named like a swizzle or struct
field (e.g. "x" in "v.x") no longer rewrites the member access.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae

@deltakosh deltakosh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @bkaradzic-microsoft,

Thanks for the quick fix. IsMemberName now keeps member and swizzle names (v.x, v . /* c */ x, s.a) intact while standalone references still use the copy, which resolves the swizzle regression. The regression test covers the whitespace and comment forms.

Non-blocking edge case for a follow-up: the line-comment check at shaderCodeInliner.ts:61-64 looks for the first // on the line, so a // inside a closed block comment earlier on the same line (for example x = 2.0; /* see http://... */ return v.x; with a copied x) is taken as a line comment, and the swizzle is still rewritten. It needs a fairly contrived combination, so I am not blocking on it.

LGTM, thanks!

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
…eters

_replaceNames now skips struct bodies, which only contain type and field
names. Previously a local struct field named like a parameter was renamed
while its member accesses were preserved, producing invalid GLSL.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae

@deltakosh deltakosh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @bkaradzic-microsoft,

The struct-field exclusion is the right idea, but FindStructBodies (shaderCodeInliner.ts:78-86) runs its regex on the raw code, comments included. A function body containing a commented-out // struct S { makes ExtractBetweenMarkers start at that { (it starts mid-comment, so it does not know it is inside one) and close on the function's own }. Everything after the comment is then treated as a struct body, so parameter (and return) substitution is skipped there and the inlined shader no longer compiles.

Please mask comments with spaces (same length, so offsets stay valid) before scanning for struct bodies, and add a regression test with a commented-out struct S { followed by a use of the parameter.

Thanks!

A commented-out "struct S {" no longer starts a struct body, and comments in a
struct header ("struct S /* c */ {") are treated as whitespace.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae
@bkaradzic-microsoft

Copy link
Copy Markdown
Member Author

@deltakosh Fixed in 44aabb1. FindStructBodies now runs on a comment-masked copy of the code (comments replaced by spaces of the same length, line breaks kept). A commented-out // struct S { or /* struct T { */ no longer opens a struct body, so parameter substitution still runs after it.

Added the regression "ignores struct declarations inside comments": a function with both comment forms followed by return a * 2.0;, called as g(x), must produce x * 2.0. It fails without the fix. Engines unit tests: 320 passed + 1 expected fail.

@deltakosh deltakosh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @bkaradzic-microsoft,

MaskComments blanks line and block comments while keeping offsets, and FindStructBodies now scans the masked code, so a commented-out struct S { no longer swallows the function body. The regression tests cover it. Approved.

Thanks!

@bjsplat

bjsplat commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s).
To prevent this PR from going to the changelog marked it with the "skip changelog" label.

bkaradzic-microsoft and others added 2 commits November 2, 2026 11:42
The UMD bundles must stay ES6 (test:escheck), and lookbehind is ES2018.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae

@deltakosh deltakosh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @bkaradzic-microsoft,

The \b replacements keep the same matches without the lookbehinds, which is safer for older targets. Still approved.

Thanks!

@bjsplat

bjsplat commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s).
To prevent this PR from going to the changelog marked it with the "skip changelog" label.

@bjsplat

bjsplat commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Reviewer - this PR has made changes to one or more package.json files.

@bjsplat

bjsplat commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Snapshot stored with reference name:
refs/pull/19077/merge

Test environment:
https://snapshots-cvgtc2eugrd3cgfd.z01.azurefd.net/refs/pull/19077/merge/index.html

To test a playground add it to the URL, for example:

https://snapshots-cvgtc2eugrd3cgfd.z01.azurefd.net/refs/pull/19077/merge/index.html#WGZLGJ#4600

Links to test your changes to core in the published versions of the Babylon tools (does not contain changes you made to the tools themselves):

https://playground.babylonjs.com/?snapshot=refs/pull/19077/merge
https://sandbox.babylonjs.com/?snapshot=refs/pull/19077/merge
https://gui.babylonjs.com/?snapshot=refs/pull/19077/merge
https://nme.babylonjs.com/?snapshot=refs/pull/19077/merge

To test the snapshot in the playground with a playground ID add it after the snapshot query string:

https://playground.babylonjs.com/?snapshot=refs/pull/19077/merge#BCU1XR#0

If you made changes to the sandbox or playground in this PR, additional comments will be generated soon containing links to the dev versions of those tools.

@bjsplat

bjsplat commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

@bjsplat

bjsplat commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

🟢 Memory Leak Test Results

4 passed, 0 leaked out of 4 scenarios

🟢 All memory leak tests passed — no leaks detected.

Passed Scenarios (4)
Scenario Package
Core Playground #2FDQT5#1508 @babylonjs/core
Core Playground #T90MQ4#14 @babylonjs/core
Core Playground #8EDB5N#2 @babylonjs/core
Core Playground #LL5BIQ#636 @babylonjs/core

@bjsplat

bjsplat commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

⚡ Performance Test Results

🟢 All performance tests passed — no regressions detected.

@bjsplat

bjsplat commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

@bjsplat

bjsplat commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 60c2ec68-6de1-445d-9fc9-b699db737eae

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants