Skip to content

fix: Simpler ShaderGenerator lifetime - #2809

Merged
iwoplaza merged 1 commit into
mainfrom
fix/simpler-generator-lifetime
Aug 11, 2026
Merged

fix: Simpler ShaderGenerator lifetime#2809
iwoplaza merged 1 commit into
mainfrom
fix/simpler-generator-lifetime

Conversation

@iwoplaza

@iwoplaza iwoplaza commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

The main idea behind this change is to create a generator per resolution pass (tgpu.resolve call, root.unwrap(pipeline), ...). This makes it very easy to not have to think about nested resolves (which we want to support because of @typegpu/three)

Copilot AI lite review requested due to automatic review settings August 8, 2026 07:13
@pullfrog

pullfrog Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Run failed. View the logs →

Pullfrog  | Rerun failed job ➔View workflow run | via Pullfrog | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@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.99, 1.93, 4.02, 7.01, 8.30, 11.66, 21.44, 24.22]
  line [1.01, 1.96, 4.10, 6.86, 7.97, 12.88, 22.06, 27.14]
  line [1.00, 2.00, 4.75, 6.80, 7.96, 13.05, 23.96, 26.36]
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.34, 0.52, 0.76, 0.84, 1.19, 1.22, 1.48, 1.57]
  line [0.27, 0.54, 0.68, 0.81, 1.12, 1.24, 1.43, 1.56]
  line [0.31, 0.57, 0.77, 0.90, 1.29, 1.31, 1.58, 1.65]
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.79, 2.15, 4.16, 7.09, 12.60, 26.29, 55.78, 112.76]
  line [0.91, 2.11, 3.93, 7.54, 12.91, 26.20, 56.23, 115.46]
  line [1.02, 2.21, 4.75, 8.22, 13.44, 27.11, 58.70, 119.51]
Loading

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

This PR refactors TypeGPU’s shader generator lifecycle to be instantiated per ResolutionCtx (constructor-injected context) instead of relying on a long-lived singleton instance with a separate initGenerator step. This aligns the unstable unstable_shaderGenerator option with a “class/newable” contract and updates tests/docs accordingly.

Changes:

  • Replace ShaderGenerator.initGenerator(ctx) with constructor injection (new (ctx: ResolutionCtx)), introducing ShaderGeneratorClass.
  • Remove default-export singleton generator instances (wgslGenerator, glslGenerator) and update call sites to pass generator classes.
  • Update tests/docs to reference WgslGenerator and adapt snippet-capture utilities.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/typegpu/tests/utils/parseResolved.ts Updates snippet extraction to use a generator class instead of an instance.
packages/typegpu/tests/tgsl/wgslGenerator.test.ts Renames describe label to WgslGenerator.
packages/typegpu/tests/tgsl/infixOperators.test.ts Renames describe label to WgslGenerator.
packages/typegpu/tests/tgsl/consoleLog.test.ts Renames describe label to WgslGenerator.
packages/typegpu/src/tgsl/wgslGenerator.ts Makes WgslGenerator context-bound via constructor; removes default singleton export.
packages/typegpu/src/tgsl/shaderGenerator.ts Introduces ShaderGeneratorClass; removes initGenerator from the interface.
packages/typegpu/src/shared/normalizeMetadata.ts Updates docs text to reference WgslGenerator.
packages/typegpu/src/resolutionCtx.ts Instantiates a new generator per context; updates option type to ShaderGeneratorClass.
packages/typegpu/src/core/simulate/tgpuSimulate.ts Passes WgslGenerator class to simulation context.
packages/typegpu/src/core/root/init.ts Updates init options to accept ShaderGeneratorClass.
packages/typegpu/src/core/resolve/tgpuResolve.ts Updates resolve options to accept ShaderGeneratorClass.
packages/typegpu-testing-utility/src/capture.ts Updates capture helper to pass a generator class instead of an instance.
packages/typegpu-gl/src/glslGenerator.ts Removes default singleton export of GlslGenerator.
packages/typegpu-gl/src/glOptions.ts Updates WebGL options to pass GlslGenerator class.
apps/typegpu-docs/src/content/docs/advanced/explaining-the-magic.mdx Updates docs wording from wgslGenerator to WgslGenerator.

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

Comment thread packages/typegpu-testing-utility/src/capture.ts Outdated
Comment thread packages/typegpu/tests/utils/parseResolved.ts Outdated
Comment thread packages/typegpu-gl/src/glslGenerator.ts
@iwoplaza
iwoplaza force-pushed the fix/simpler-generator-lifetime branch 2 times, most recently from 638b221 to dd30022 Compare August 8, 2026 15: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@3359a487dfe41cf2dffbcf107beb2a68a85438d3
https://pkg.pr.new/software-mansion/TypeGPU/tgpu-gen@3359a487dfe41cf2dffbcf107beb2a68a85438d3
https://pkg.pr.new/software-mansion/TypeGPU/tinyest-for-wgsl@3359a487dfe41cf2dffbcf107beb2a68a85438d3
https://pkg.pr.new/software-mansion/TypeGPU/typegpu@3359a487dfe41cf2dffbcf107beb2a68a85438d3
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/cli@3359a487dfe41cf2dffbcf107beb2a68a85438d3
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/color@3359a487dfe41cf2dffbcf107beb2a68a85438d3
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/gl@3359a487dfe41cf2dffbcf107beb2a68a85438d3
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/noise@3359a487dfe41cf2dffbcf107beb2a68a85438d3
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/radiance-cascades@3359a487dfe41cf2dffbcf107beb2a68a85438d3
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/react@3359a487dfe41cf2dffbcf107beb2a68a85438d3
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/sdf@3359a487dfe41cf2dffbcf107beb2a68a85438d3
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/three@3359a487dfe41cf2dffbcf107beb2a68a85438d3
https://pkg.pr.new/software-mansion/TypeGPU/unplugin-typegpu@3359a487dfe41cf2dffbcf107beb2a68a85438d3

benchmark
view benchmark

commit
view commit

@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 ➖ Unchanged 🔴 Increased (max 0.07%) ❔ Unknown
0 301 21 0

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

Test tsdown
tgpu_init.ts 267.85 kB ($${\color{green}-3.3\%}$$)
tgpu_initFromDevice.ts 267.32 kB ($${\color{green}-3.5\%}$$)
tgpu_resolve.ts 168.42 kB ($${\color{green}-39.2\%}$$)
tgpu_resolveWithContext.ts 168.35 kB ($${\color{green}-39.2\%}$$)
tgpu_bindGroupLayout.ts 73.79 kB ($${\color{green}-73.4\%}$$)
tgpu_mutableAccessor.ts 68.51 kB ($${\color{green}-75.3\%}$$)
tgpu_accessor.ts 68.51 kB ($${\color{green}-75.3\%}$$)
tgpu_privateVar.ts 67.20 kB ($${\color{green}-75.7\%}$$)
tgpu_workgroupVar.ts 67.20 kB ($${\color{green}-75.7\%}$$)
tgpu_const.ts 66.62 kB ($${\color{green}-75.9\%}$$)
tgpu_lazy.ts 66.42 kB ($${\color{green}-76.0\%}$$)
tgpu_fragmentFn.ts 38.92 kB ($${\color{green}-85.9\%}$$)
tgpu_fn.ts 38.86 kB ($${\color{green}-86.0\%}$$)
tgpu_vertexFn.ts 38.73 kB ($${\color{green}-86.0\%}$$)
tgpu_computeFn.ts 38.44 kB ($${\color{green}-86.1\%}$$)
tgpu_vertexLayout.ts 27.57 kB ($${\color{green}-90.0\%}$$)
tgpu_comptime.ts 15.17 kB ($${\color{green}-94.5\%}$$)
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.

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

Caution

This PR currently fails to typecheck — pnpm --filter typegpu test:types reports 4 errors — so it cannot merge as-is. The root cause is the unstable_shaderGenerator option now typed as ShaderGeneratorClass while the root's internal plumbing still works with a ShaderGenerator instance. See the inline init.ts comment for the exact fix.

Reviewed changes

The new commit 638b221 ("fix: Simpler ShaderGenerator lifetime") finalized the generator-lifetime refactor on top of the previously reviewed state:

  • Replaced TgpuRootWebGL's shared #shaderGenerator singleton with direct GlslGenerator class references in each tgpu.resolve call.
  • Made CapturingGenerator / ExtractingGenerator abstract and scoped their captured state to per-call closures via anonymous subclasses, addressing the non-reentrancy concern from the prior review.
  • Exported ShaderGeneratorClass from typegpu/~internal.

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

Comment thread packages/typegpu/src/core/root/init.ts Outdated
Comment thread packages/typegpu/tests/utils/parseResolved.ts Outdated
@iwoplaza
iwoplaza force-pushed the fix/simpler-generator-lifetime branch from dd30022 to b397138 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.

ℹ️ The blocking typecheck issue from the prior review is fixed and no new issues were found; the one remaining prior item (a minor test-utility nit) is still open, so this review isn't a full approval.

Reviewed changes

The branch was force-pushed to a single commit (b397138) on the rebased base, so this run re-reviewed the full generator-lifetime refactor plus the correction since the prior review.

  • Fixed the prior blocking typecheck break — TgpuRootImpl.shaderGenerator (field + constructor param in init.ts) and ExperimentalTgpuRoot.shaderGenerator (rootTypes.ts) are now typed ShaderGeneratorClass, matching the unstable_shaderGenerator option; no more ShaderGenerator-instance plumbing into resolve/ResolutionCtxImpl.
  • Completed the lifetime refactor — WgslGenerator/GlslGenerator are now constructor-injected per ResolutionCtx (new (opts.shaderGenerator ?? WgslGenerator)(this) in resolutionCtx.ts:452), initGenerator is removed, and the singleton default exports (wgslGenerator, glslGenerator) are deleted; every unstable_shaderGenerator call-site passes a generator class and the capture/snippet-test helpers use closure-scoped per-call state.

Verified: pnpm --filter typegpu test:types, @typegpu/gl test:types, and the testing-utility tsc all pass; the tgsl suite (317 tests) and @typegpu/gl suite (28 tests) pass.

The prior review's only still-open thread — the dead "Cannot inspect multiple return values" guard in packages/typegpu/tests/utils/parseResolved.ts (latestReturnedSnippet is backed by a setter-only accessor, so the guard read at line 29 always sees undefined and the last return silently wins) — is unchanged by the new commit. It's a test-utility edge rather than a correctness/merge blocker, but per the open thread it's not treated as addressed here.

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

@iwoplaza
iwoplaza force-pushed the fix/simpler-generator-lifetime branch from b397138 to 7c2cc8f Compare August 9, 2026 07:40

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

ℹ️ The incremental change — making getTargetShaderLanguage() report the active generator's actual language — is clean, correctly implemented, and well-tested, with no new issues found. This isn't a full approval only because the prior minor parseResolved.ts nit remains open.

Reviewed changes

Since the prior pullfrog review (b397138):

  • Made getTargetShaderLanguage() return ctx.gen.languageKey instead of a hardcoded 'wgsl', so code generating GLSL now correctly reports 'glsl' (blocked off in simulate/top-level by the existing getExecMode guard).
  • Added readonly languageKey: string to the ShaderGenerator interface and wired it as a prototype property on WgslGenerator ('wgsl') and GlslGenerator ('glsl'), using declare + a static block so subclass overrides resolve correctly through the prototype chain.
  • Added a regression test in @typegpu/gl asserting a GLSL resolve reports 'glsl' — this test fails under the prior hardcoded behavior, so it's real coverage.
  • Updated the std.getTargetShaderLanguage JSDoc and the utils.mdx docs table to describe the language-agnostic semantics.

Verified: typegpu and @typegpu/gl typechecks pass; tests/std + tests/tgsl (691 tests) and glslGenerator.test.ts pass.

The prior thread at packages/typegpu/tests/utils/parseResolved.ts:52 (setter-only accessor leaving the "Cannot inspect multiple return values" guard dead) is untouched by this delta and remains open — a test-utility nit, not a merge blocker.

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

@iwoplaza
iwoplaza force-pushed the fix/simpler-generator-lifetime branch from 7c2cc8f to 17b58ae Compare August 9, 2026 17:59

@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 functional issues found in the incremental change — the pivot to instance-per-resolution with an eager initGenerator guard is internally coherent, typechecks, and passes the tgsl/std/@typegpu/gl suites I ran (767 + 29 tests). Two rough edges on the unstable API surface below.

Reviewed changes

Since the prior pullfrog review (7c2cc8f):

  • Reversed the generator lifetime from constructor injection (new (ctx)) to a fresh instance per resolution: ResolutionCtxImpl now defaults to opts.shaderGenerator ?? new WgslGenerator() and calls initGenerator(this) eagerly in its constructor, and WgslGenerator.initGenerator gained a double-init guard that throws.
  • Slimmed ShaderGeneratorClass down to new (): ShaderGenerator, kept it for root storage only, and made render/compute pipelines instantiate new class() per resolve.
  • Renamed the root field shaderGeneratorshaderGeneratorClass (updating the nonTransferablePriors key and the TgpuRoot Pick list accordingly).
  • Restored unstable_shaderGenerator for tgpu.resolve to an instance, switched TgpuRootWebGL/glOptions to pass a fresh new GlslGenerator() per resolve call, and deleted the wgslGenerator/glslGenerator default singleton exports.
  • Reverted parseResolved.ts/capture.ts to the simple fresh-instance-per-call form (dropping them out of the PR), which also resurrects the previously-dead "Cannot inspect multiple return values" guard.

The previously-open thread on the dead parseResolved.ts guard is addressed by that revert and was resolved.

ℹ️ Nitpicks

  • The single-use contract of a generator instance is only enforced at runtime, not expressed by the type: constructing new GlslGenerator() is fine, but handing the same instance to two tgpu.resolve calls throws "Cannot initialize shader generators twice" at the second call (wgslGenerator.ts:224). A cached glOptions() result, or an options object reused across resolves, would trip it in a place the type system can't flag. The error message does point users at the right remedy, so this is fine for an unstable API — just noting that the guard silently turns what used to be a harmless reuse into a hard failure.

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

Comment thread packages/typegpu/src/core/root/init.ts Outdated
Comment thread packages/typegpu/src/core/root/init.ts Outdated
Base automatically changed from fix/merge-generation-ctx-and-resolution-ctx to main August 10, 2026 10:06
@iwoplaza
iwoplaza force-pushed the fix/simpler-generator-lifetime branch 2 times, most recently from eea883d to fc67705 Compare August 10, 2026 10:15
@iwoplaza
iwoplaza force-pushed the fix/simpler-generator-lifetime branch from fc67705 to 3359a48 Compare August 11, 2026 09:16
@iwoplaza
iwoplaza merged commit bcaf1a2 into main Aug 11, 2026
8 of 9 checks passed
@iwoplaza
iwoplaza deleted the fix/simpler-generator-lifetime branch August 11, 2026 09:25
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.

5 participants