Skip to content

feat: Transfer protocol for TypeGPU resources (on souls) - #2797

Open
reczkok wants to merge 1 commit into
feat/tgpu-soulsfrom
feat/worklet-support-on-souls
Open

feat: Transfer protocol for TypeGPU resources (on souls)#2797
reczkok wants to merge 1 commit into
feat/tgpu-soulsfrom
feat/worklet-support-on-souls

Conversation

@reczkok

@reczkok reczkok commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Replaces #2732, rebuilt on top of #2796. With souls in place the protocol got much simpler: a snapshot is just a structured copy of the resource's soul

  • transferred pipelines arrive precompiled and are never resolved again, so definitions (tgpu.fn, entry functions, tgpu.comptime) stay runtime-local
  • pipelines carrying attachments or a bound pass/encoder throw on transfer (those hold views and encoders local to their runtime)
  • data schemas travel as descriptions of themselves - every schema is callable and host serializers claim functions before asking whether a value is transferable, so there's no way to hand them the schema directly
  • transfer cache keeps identity: sending the same resource twice yields the same object back

@github-actions

github-actions Bot commented Aug 6, 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@75cdd1a768ffed15e32c6329468fd6a143dbf273
https://pkg.pr.new/software-mansion/TypeGPU/tgpu-gen@75cdd1a768ffed15e32c6329468fd6a143dbf273
https://pkg.pr.new/software-mansion/TypeGPU/tinyest-for-wgsl@75cdd1a768ffed15e32c6329468fd6a143dbf273
https://pkg.pr.new/software-mansion/TypeGPU/typegpu@75cdd1a768ffed15e32c6329468fd6a143dbf273
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/cli@75cdd1a768ffed15e32c6329468fd6a143dbf273
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/color@75cdd1a768ffed15e32c6329468fd6a143dbf273
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/gl@75cdd1a768ffed15e32c6329468fd6a143dbf273
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/noise@75cdd1a768ffed15e32c6329468fd6a143dbf273
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/radiance-cascades@75cdd1a768ffed15e32c6329468fd6a143dbf273
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/react@75cdd1a768ffed15e32c6329468fd6a143dbf273
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/sdf@75cdd1a768ffed15e32c6329468fd6a143dbf273
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/three@75cdd1a768ffed15e32c6329468fd6a143dbf273
https://pkg.pr.new/software-mansion/TypeGPU/unplugin-typegpu@75cdd1a768ffed15e32c6329468fd6a143dbf273

benchmark
view benchmark

commit
view commit

@github-actions

github-actions Bot commented Aug 6, 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.86, 1.79, 3.78, 6.41, 7.10, 11.77, 21.18, 24.56]
  line [0.96, 1.86, 4.11, 6.12, 7.66, 12.71, 21.08, 22.54]
  line [0.99, 1.98, 4.31, 6.40, 7.68, 11.27, 21.33, 24.47]
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.29, 0.50, 0.66, 0.75, 1.00, 1.05, 1.29, 1.43]
  line [0.27, 0.49, 0.67, 0.81, 1.15, 1.17, 1.35, 1.51]
  line [0.30, 0.53, 0.71, 0.87, 1.15, 1.24, 1.48, 1.66]
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.81, 2.05, 3.26, 6.95, 11.90, 25.26, 52.16, 107.81]
  line [0.77, 2.09, 3.59, 6.70, 11.92, 25.48, 53.22, 109.90]
  line [0.81, 2.13, 3.86, 6.47, 12.96, 26.03, 55.68, 113.56]
Loading

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

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

🟢 Decreased ➖ Unchanged 🔴 Increased (max 0.24%) ❔ Unknown
0 300 22 1

import * as ... in PR vs import * as ... in target (did bundle size increase?):

Test tsdown
d_isInvariantAttrib.ts 784 B

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

Test tsdown
tgpu_init.ts 267.34 kB ($${\color{green}-3.3\%}$$)
tgpu_initFromDevice.ts 266.80 kB ($${\color{green}-3.5\%}$$)
tgpu_resolve.ts 167.89 kB ($${\color{green}-39.3\%}$$)
tgpu_resolveWithContext.ts 167.82 kB ($${\color{green}-39.3\%}$$)
tgpu_bindGroupLayout.ts 73.57 kB ($${\color{green}-73.4\%}$$)
tgpu_mutableAccessor.ts 68.30 kB ($${\color{green}-75.3\%}$$)
tgpu_accessor.ts 68.29 kB ($${\color{green}-75.3\%}$$)
tgpu_privateVar.ts 66.85 kB ($${\color{green}-75.8\%}$$)
tgpu_workgroupVar.ts 66.85 kB ($${\color{green}-75.8\%}$$)
tgpu_const.ts 66.40 kB ($${\color{green}-76.0\%}$$)
tgpu_lazy.ts 66.21 kB ($${\color{green}-76.1\%}$$)
tgpu_fragmentFn.ts 38.72 kB ($${\color{green}-86.0\%}$$)
tgpu_fn.ts 38.65 kB ($${\color{green}-86.0\%}$$)
tgpu_vertexFn.ts 38.53 kB ($${\color{green}-86.1\%}$$)
tgpu_computeFn.ts 38.24 kB ($${\color{green}-86.2\%}$$)
tgpu_vertexLayout.ts 27.36 kB ($${\color{green}-90.1\%}$$)
tgpu_comptime.ts 14.97 kB ($${\color{green}-94.6\%}$$)
tgpu_unroll.ts 1.68 kB ($${\color{green}-99.4\%}$$)
tgpu_slot.ts 1.63 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.

This is a solid, well-tested addition of the cross-runtime transfer protocol. The snapshot/restore split is clean, the tests cover all the major resource kinds, and the JSI-specific type guards are a nice touch for React Native.

I have a few small suggestions around TgpuBindGroupImpl / bind-group restoration that are worth double-checking before merge, but nothing that blocks the overall approach. The relevant unit tests (serial, serializeDataSchema, computePipeline, renderPipeline, internal/typeGuards, root) all pass locally.

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

this[$soul] = {
type: 'bind-group',
// Undefined only in rootless `tgpu.resolve()`, where the group is never unwrapped
device: root?.device as GPUDevice,

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.

This as GPUDevice hides the fact that device can be undefined when the bind group is created rootlessly via tgpu.resolve(). TgpuDeviceOwningSoul requires device: GPUDevice, so the type system will treat this as a real device even when it isn't. Consider making device optional in the soul type for rootless resources, or using a separate soul shape, so the runtime value matches the type.

for (const key of Object.keys(layout.entries)) {
if (layout.entries[key] !== null && !(key in entries)) {
throw new MissingBindingError(getName(layout), key);
if (!raw) {

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.

Skipping the missing-entry check when raw is provided makes sense for restored/materialized bind groups, but it means the constructor now allows bind groups with incomplete entries as long as a raw GPUBindGroup is passed. Since TgpuBindGroupImpl is exported, this is a bit of a footgun. Could you add a clarifying comment that this path is only for restoration, or consider validating anyway when raw comes from an untrusted source?

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.

1 participant