Skip to content

fix(@typegpu/three): Allow TgpuFnNode to transitively nest - #2815

Merged
iwoplaza merged 1 commit into
fix/nested-tgpu-resolvefrom
fix/three-reentrancy
Aug 11, 2026
Merged

fix(@typegpu/three): Allow TgpuFnNode to transitively nest#2815
iwoplaza merged 1 commit into
fix/nested-tgpu-resolvefrom
fix/three-reentrancy

Conversation

@iwoplaza

@iwoplaza iwoplaza commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

This can happen when a TSL node calls a TypeGPU function which references a TSL node that calls another TypeGPU function.

Copilot AI lite review requested due to automatic review settings August 8, 2026 19:06
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

pkg.pr.new

packages
Ready to be installed by your favorite package manager ⬇️

https://pkg.pr.new/software-mansion/TypeGPU/eslint-plugin-typegpu@78cbd66aea698d4847d5e613bcdbc2d08274d0ac
https://pkg.pr.new/software-mansion/TypeGPU/tgpu-gen@78cbd66aea698d4847d5e613bcdbc2d08274d0ac
https://pkg.pr.new/software-mansion/TypeGPU/tinyest-for-wgsl@78cbd66aea698d4847d5e613bcdbc2d08274d0ac
https://pkg.pr.new/software-mansion/TypeGPU/typegpu@78cbd66aea698d4847d5e613bcdbc2d08274d0ac
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/cli@78cbd66aea698d4847d5e613bcdbc2d08274d0ac
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/color@78cbd66aea698d4847d5e613bcdbc2d08274d0ac
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/gl@78cbd66aea698d4847d5e613bcdbc2d08274d0ac
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/noise@78cbd66aea698d4847d5e613bcdbc2d08274d0ac
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/radiance-cascades@78cbd66aea698d4847d5e613bcdbc2d08274d0ac
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/react@78cbd66aea698d4847d5e613bcdbc2d08274d0ac
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/sdf@78cbd66aea698d4847d5e613bcdbc2d08274d0ac
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/three@78cbd66aea698d4847d5e613bcdbc2d08274d0ac
https://pkg.pr.new/software-mansion/TypeGPU/unplugin-typegpu@78cbd66aea698d4847d5e613bcdbc2d08274d0ac

benchmark
view benchmark

commit
view commit

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.

Pull request overview

Enables @typegpu/three’s TgpuFnNode generation context to be safely nested transitively (including failure paths), and adds a Vitest-based regression suite in the package to prevent context-leak regressions.

Changes:

  • Restore outer TgpuFnNode generation context after nested traversal via a stack-like helper (withGeneratingFnNodeCtx).
  • Add Vitest setup + tests in packages/typegpu-three covering nested analyze/generate and nested-throw/caught scenarios.
  • Update package scripts and lockfile to include the new test tooling dependencies.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
pnpm-lock.yaml Adds lockfile entries for new test tooling deps used by @typegpu/three.
packages/typegpu-three/vitest.config.mts Introduces Vitest config (aligned with other workspace packages) including TypeGPU plugin + aliases.
packages/typegpu-three/tsconfig.test.json Adds a dedicated TS config to typecheck tests and Vitest config.
packages/typegpu-three/tests/typegpu-node.test.ts Adds regression tests for nested context restoration across analyze/generate and exception scenarios.
packages/typegpu-three/src/typegpu-node.ts Refactors context handling to restore previous context instead of clearing it, enabling safe transitive nesting.
packages/typegpu-three/package.json Adds vitest/jiti/testing utility dev deps and a test script; expands test:types to include test TS config.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/typegpu-three/tests/typegpu-node.test.ts Outdated
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

Resolution Time Benchmark

---
config:
  themeVariables:
    xyChart:
      plotColorPalette: "#E63946, #3B82F6, #059669"
---
xychart
  title "Random Branching (🔴 PR | 🔵 main | 🟢 release)"
  x-axis "max depth" [1, 2, 3, 4, 5, 6, 7, 8]
  y-axis "time (ms)"
  line [0.95, 1.95, 4.53, 6.90, 7.48, 12.26, 23.32, 23.45]
  line [0.95, 1.91, 4.20, 6.01, 7.26, 12.95, 21.94, 26.01]
  line [0.94, 1.99, 4.28, 6.25, 7.66, 12.24, 23.56, 24.52]
Loading
---
config:
  themeVariables:
    xyChart:
      plotColorPalette: "#E63946, #3B82F6, #059669"
---
xychart
  title "Linear Recursion (🔴 PR | 🔵 main | 🟢 release)"
  x-axis "max depth" [1, 2, 3, 4, 5, 6, 7, 8]
  y-axis "time (ms)"
  line [0.30, 0.55, 0.73, 0.87, 1.08, 1.16, 1.36, 1.52]
  line [0.37, 0.47, 0.69, 0.80, 1.06, 1.18, 1.40, 1.54]
  line [0.33, 0.59, 0.72, 0.92, 1.28, 1.30, 1.49, 1.70]
Loading
---
config:
  themeVariables:
    xyChart:
      plotColorPalette: "#E63946, #3B82F6, #059669"
---
xychart
  title "Full Tree (🔴 PR | 🔵 main | 🟢 release)"
  x-axis "max depth" [1, 2, 3, 4, 5, 6, 7, 8]
  y-axis "time (ms)"
  line [0.83, 1.96, 3.98, 6.82, 12.96, 25.89, 53.50, 110.66]
  line [0.79, 2.08, 4.06, 7.14, 13.05, 25.55, 53.91, 109.90]
  line [0.85, 2.24, 4.24, 6.40, 13.46, 26.81, 57.56, 115.70]
Loading

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

Bundle size comparison (import * as ... in PR vs import * as ... in target):

🟢 Decreased (max -0.02%) ➖ Unchanged 🔴 Increased (max 0.03%) ❔ Unknown
21 137 165 0

import { ... } in PR vs import * as ... in PR (is the library tree-Shakeable?):

Test tsdown
tgpu_init.ts 270.23 kB ($${\color{green}-3.3\%}$$)
tgpu_initFromDevice.ts 269.68 kB ($${\color{green}-3.5\%}$$)
tgpu_resolve.ts 170.33 kB ($${\color{green}-39.0\%}$$)
tgpu_resolveWithContext.ts 170.27 kB ($${\color{green}-39.1\%}$$)
tgpu_bindGroupLayout.ts 73.81 kB ($${\color{green}-73.6\%}$$)
tgpu_mutableAccessor.ts 68.54 kB ($${\color{green}-75.5\%}$$)
tgpu_accessor.ts 68.54 kB ($${\color{green}-75.5\%}$$)
tgpu_privateVar.ts 67.23 kB ($${\color{green}-75.9\%}$$)
tgpu_workgroupVar.ts 67.23 kB ($${\color{green}-75.9\%}$$)
tgpu_const.ts 66.65 kB ($${\color{green}-76.2\%}$$)
tgpu_lazy.ts 66.44 kB ($${\color{green}-76.2\%}$$)
tgpu_fragmentFn.ts 38.92 kB ($${\color{green}-86.1\%}$$)
tgpu_fn.ts 38.86 kB ($${\color{green}-86.1\%}$$)
tgpu_vertexFn.ts 38.74 kB ($${\color{green}-86.1\%}$$)
tgpu_computeFn.ts 38.45 kB ($${\color{green}-86.2\%}$$)
tgpu_vertexLayout.ts 27.57 kB ($${\color{green}-90.1\%}$$)
tgpu_comptime.ts 15.18 kB ($${\color{green}-94.6\%}$$)
tgpu_unroll.ts 1.75 kB ($${\color{green}-99.4\%}$$)
tgpu_slot.ts 1.70 kB ($${\color{green}-99.4\%}$$)

If you wish to run a comparison for other, slower bundlers, run the 'Tree-shake test' from the GitHub Actions menu.

@iwoplaza
iwoplaza force-pushed the fix/three-reentrancy branch from 10a50b4 to cc033a5 Compare August 8, 2026 19:10
@iwoplaza
iwoplaza force-pushed the fix/three-reentrancy branch from cc033a5 to 64888e9 Compare August 8, 2026 19:17

@pullfrog pullfrog Bot 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.

Important

Nesting a TgpuFnNode inside another no longer throws, but the enclosing node's nodeFunction is parsed from a contaminated slice of the shared generation buffer, silently returning the inner function's getNodeType()/inputs whenever the two functions have different signatures. The mechanism and a verified repro are in the inline comment; the added tests won't catch it.

Reviewed changes — One commit on fix/nested-tgpu-resolve making TgpuFnNode generation re-entrant: withGeneratingFnNodeCtx now saves and restores the previous currentlyGeneratingFnNodeCtx with finally stack semantics (used in #getNodeFunction and #analyzeFunction), and the obsolete "Nested function generation detected" warning is removed. Also introduces the package's first unit tests and vitest harness.

  • Re-entrant generation context — stacked ctx restore lets a fromTSL accessor (or a nested TgpuFnNode) resolve inside another TgpuFnNode's impl without stranding the outer context. All three new tests fail against the pre-fix behavior, so the regression coverage is real.
  • Test infrastructuretests/typegpu-node.test.ts, vitest.config.mts, tsconfig.test.json, test script, plus jiti / typegpu-testing-utility / vitest devDeps, mirroring the other packages. vitest run, test:types, lint, and format all pass.

⚠️ The tests can't catch the wrong-type regression they should pin

All three tests use a single f32 signature for every node, so they pin "doesn't throw / context restored" but not the PR's headline claim — that nested nodes are correct. With the PR's own nesting shape I reproduced the enclosing node reporting getNodeType() = 'float' instead of 'vec3'; a test with distinct inner/outer return types asserting the outer node's getNodeType (and ideally the emitted WGSL) would actually pin the transitively-nested contract.

Technical details
# Test-only coverage gap masks the wrong-type bug

## Affected sites
- packages/typegpu-three/tests/typegpu-node.test.ts:42-72 — both tests exercise only f32-typed nodes, so the enclosing-node mis-parse (see inline comment on typegpu-node.ts:147) is invisible to CI.

## Required outcome
- A new test where the enclosing `toTSL` returns a different type (e.g. `vec3f`) than a nested `f32` `TgpuFnNode`, asserting `outerNode.getNodeType(builder)` is `'vec3'` and, if feasible, that the generated source yields exactly one `fn` per node.

## Suggested approach (optional)
- This test stays red until the parse extraction is fixed; it is the correct regression pin for "transitively nest".

ℹ️ Nitpicks

  • vi is imported at tests/typegpu-node.test.ts:4 but never used. Test files have eslint/no-unused-vars disabled in oxlint.config.ts, so it won't fail CI, but it's a dead import — drop it.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment thread packages/typegpu-three/src/typegpu-node.ts Outdated
@iwoplaza
iwoplaza force-pushed the fix/three-reentrancy branch from 64888e9 to cbfaf97 Compare August 9, 2026 07:40
@iwoplaza
iwoplaza force-pushed the fix/three-reentrancy branch 2 times, most recently from 9ff9260 to b211d8c Compare August 9, 2026 18:45

@pullfrog pullfrog Bot 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.

ℹ️ No critical issues — the prior IMPORTANT mis-parse finding is resolved by the head rewrite; one optional hardening suggestion inline.

Reviewed changes — Since the prior pullfrog review (10a50b43, IMPORTANT), the head commit b211d8c9 rewrites how TgpuFnNode#getNodeFunction extracts the function declaration:

  • Declaration-based extraction replaced shared-buffer slicingGenerateStageData drops the codeGeneratedThusFar string accumulator; #getNodeFunction now resolves via tgpu.resolveWithContext([this.#impl]), re-resolves the impl in the same namespace to get its identifier, and pulls the exact fn declaration from an accumulated existingDeclarations: ResolvedDeclaration[]. This removes the indexOf('\nfn ' + id) byte-slicing whose prefix-collision (item vs item_1) made the enclosing node mis-parse its type under transitive nesting.
  • ResolvedDeclaration exported from typegpu (indexNamedExports.ts), and GenerateStageData accumulates declarations across the builder/stage so a fn used transitively and then passed directly into toTSL is still found.
  • Unused vi import removed from the test file (addressed the prior nitpick).

The prior IMPORTANT thread (typegpu-node.ts:147) is resolved. I re-verified on this head with scratch tests (removed after):

  • Depth-2 transitive nesting (outer d.vec3f(mid.$) → mid d.vec2f(leaf.$, leaf.$) → leaf d.f32) now returns the correct enclosing type: outer getNodeType() = 'vec3', mid 'vec2', leaf 'float' — previously 'vec2'.
  • The emitted WGSL (builder.getCodes('fragment') after a full outer.build) contains fn item / item_1 / item_2 exactly once each, in correct nesting order — no double-emission from the accumulated-declarations lookup.
  • Direct reuse on the same builder of a tgpu.fn that was already resolved transitively builds without throwing and reports the right type.
  • pnpm --filter @typegpu/three test:types, oxlint, and all three PR tests pass.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment thread packages/typegpu-three/tests/typegpu-node.test.ts
@iwoplaza
iwoplaza force-pushed the fix/three-reentrancy branch from b211d8c to 32f3de2 Compare August 10, 2026 10:06
@iwoplaza
iwoplaza force-pushed the fix/three-reentrancy branch from 32f3de2 to 3f7abea Compare August 10, 2026 10:16

@cieplypolar cieplypolar left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good. Left one nit.

Comment thread packages/typegpu-three/src/typegpu-node.ts Outdated
Comment thread packages/typegpu-three/src/typegpu-node.ts Outdated
@iwoplaza
iwoplaza force-pushed the fix/three-reentrancy branch from 3f7abea to 1cb767d Compare August 11, 2026 09:16
@iwoplaza
iwoplaza force-pushed the fix/three-reentrancy branch 2 times, most recently from a3166dc to e7951d4 Compare August 11, 2026 09:29
@iwoplaza
iwoplaza force-pushed the fix/three-reentrancy branch from e7951d4 to ed23268 Compare August 11, 2026 16:40
@iwoplaza
iwoplaza force-pushed the fix/three-reentrancy branch from ed23268 to 78cbd66 Compare August 11, 2026 18:04
@iwoplaza
iwoplaza merged commit b77398a into main Aug 11, 2026
12 of 16 checks passed
@iwoplaza
iwoplaza deleted the fix/three-reentrancy branch August 11, 2026 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants