Skip to content

chore: Refactor GPU function call mechanism - #2919

Open
iwoplaza wants to merge 1 commit into
mainfrom
chore/refactor-fn-calls
Open

chore: Refactor GPU function call mechanism#2919
iwoplaza wants to merge 1 commit into
mainfrom
chore/refactor-fn-calls

Conversation

@iwoplaza

@iwoplaza iwoplaza commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Changes:

  • Struct and array schemas now take care of what happens when somebody calls them.
  • Generating argument snippets out of tinyest nodes is now a seperate step from actually calling a function. This allows other parts of the generator to call helper functions without needing to reach into the deep internals via symbols (helpful in GLSL generation as well as the future matrix std function rewrites)

@iwoplaza
iwoplaza force-pushed the chore/refactor-fn-calls branch from ae39d1b to 08937bf Compare August 25, 2026 11:46
@github-actions

github-actions Bot commented Aug 25, 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.81, 1.66, 3.78, 6.06, 7.32, 10.26, 22.18, 25.58]
  line [0.92, 1.94, 4.08, 6.22, 7.55, 11.47, 23.02, 25.44]
  line [0.97, 1.88, 4.12, 5.63, 7.01, 10.66, 22.26, 24.62]
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.25, 0.44, 0.63, 0.74, 1.01, 1.00, 1.20, 1.36]
  line [0.29, 0.50, 0.70, 0.85, 1.08, 1.16, 1.38, 1.51]
  line [0.29, 0.53, 0.71, 0.85, 1.14, 1.20, 1.40, 1.56]
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.95, 2.17, 3.22, 7.83, 12.50, 25.45, 54.65, 107.45]
  line [0.77, 2.11, 4.32, 7.14, 12.26, 25.43, 53.99, 110.55]
  line [0.77, 2.18, 3.27, 7.60, 12.09, 25.07, 54.29, 109.96]
Loading

@iwoplaza
iwoplaza force-pushed the chore/refactor-fn-calls branch from 08937bf to 2c95c24 Compare August 25, 2026 12:46
@github-actions

Copy link
Copy Markdown

pkg.pr.new

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

https://pkg.pr.new/software-mansion/TypeGPU/typegpu@2c95c245578384624e31d31485b43206e23319af

benchmark
view benchmark

commit
view commit

@github-actions

Copy link
Copy Markdown

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

🟢 Decreased (max -0.02%) ➖ Unchanged 🔴 Increased (max 269.26%) ❔ Unknown
21 2 302 0

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

Click to reveal the results table (103 entries).
Test tsdown
d_struct.ts 13.69 kB ($${\color{red}+269.3\%}$$)
d_Void.ts 810 B ($${\color{red}+6.7\%}$$)
d_sampler.ts 818 B ($${\color{red}+6.6\%}$$)
d_isPtr.ts 828 B ($${\color{red}+6.6\%}$$)
d_comparisonSampler.ts 829 B ($${\color{red}+6.6\%}$$)
d_isSizeAttrib.ts 830 B ($${\color{red}+6.5\%}$$)
d_isWgslArray.ts 830 B ($${\color{red}+6.5\%}$$)
d_isAlignAttrib.ts 831 B ($${\color{red}+6.5\%}$$)
d_isAtomic.ts 831 B ($${\color{red}+6.5\%}$$)
d_isWgslStruct.ts 831 B ($${\color{red}+6.5\%}$$)
d_isBuiltinAttrib.ts 833 B ($${\color{red}+6.5\%}$$)
d_isDecorated.ts 834 B ($${\color{red}+6.5\%}$$)
d_isLocationAttrib.ts 834 B ($${\color{red}+6.5\%}$$)
d_isInvariantAttrib.ts 835 B ($${\color{red}+6.5\%}$$)
d_isInterpolateAttrib.ts 837 B ($${\color{red}+6.5\%}$$)
d_atomic.ts 855 B ($${\color{red}+6.3\%}$$)
d_ptrHandle.ts 927 B ($${\color{red}+5.8\%}$$)
d_ptrUniform.ts 928 B ($${\color{red}+5.8\%}$$)
d_ptrStorage.ts 932 B ($${\color{red}+5.8\%}$$)
d_ptrPrivate.ts 934 B ($${\color{red}+5.8\%}$$)
d_ptrFn.ts 935 B ($${\color{red}+5.8\%}$$)
d_ptrWorkgroup.ts 936 B ($${\color{red}+5.8\%}$$)
d_textureExternal.ts 949 B ($${\color{red}+5.7\%}$$)
d_textureStorage1d.ts 1.09 kB ($${\color{red}+4.8\%}$$)
d_textureStorage2d.ts 1.09 kB ($${\color{red}+4.8\%}$$)
d_textureStorage3d.ts 1.09 kB ($${\color{red}+4.8\%}$$)
d_textureStorage2dArray.ts 1.10 kB ($${\color{red}+4.7\%}$$)
d_isDisarray.ts 1.19 kB ($${\color{red}+4.4\%}$$)
d_isUnstruct.ts 1.19 kB ($${\color{red}+4.4\%}$$)
d_isLooseDecorated.ts 1.19 kB ($${\color{red}+4.4\%}$$)
d_isLooseData.ts 1.23 kB ($${\color{red}+4.2\%}$$)
d_isWgslData.ts 1.38 kB ($${\color{red}+3.7\%}$$)
d_arrayOf.ts 28.95 kB ($${\color{red}+3.4\%}$$)
d_builtin.ts 31.41 kB ($${\color{red}+3.1\%}$$)
d_unstruct.ts 1.70 kB ($${\color{red}+3.0\%}$$)
common_fullScreenTriangle.ts 43.55 kB ($${\color{red}+2.9\%}$$)
d_isData.ts 1.88 kB ($${\color{red}+2.7\%}$$)
d_deepEqual.ts 2.27 kB ($${\color{red}+2.2\%}$$)
STATIC_common.ts 60.31 kB ($${\color{red}+2.1\%}$$)
STATIC_d.ts 78.97 kB ($${\color{red}+1.5\%}$$)
d_ref.ts 5.04 kB ($${\color{red}+0.9\%}$$)
std_dot.ts 56.63 kB ($${\color{red}+0.7\%}$$)
std_length.ts 56.63 kB ($${\color{red}+0.7\%}$$)
std_distance.ts 56.63 kB ($${\color{red}+0.7\%}$$)
std_dot4U8Packed.ts 56.63 kB ($${\color{red}+0.7\%}$$)
std_fract.ts 56.63 kB ($${\color{red}+0.7\%}$$)
std_frexp.ts 56.63 kB ($${\color{red}+0.7\%}$$)
std_mix.ts 56.63 kB ($${\color{red}+0.7\%}$$)
std_modf.ts 56.63 kB ($${\color{red}+0.7\%}$$)
std_abs.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_acos.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_acosh.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_asin.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_asinh.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_atan.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_atan2.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_atanh.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_ceil.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_clamp.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_cos.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_cosh.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_countLeadingZeros.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_countOneBits.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_countTrailingZeros.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_cross.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_degrees.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_determinant.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_dot4I8Packed.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_exp.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_exp2.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_extractBits.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_faceForward.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_firstLeadingBit.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_firstTrailingBit.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_floor.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_fma.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_insertBits.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_intdiv.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_inverseSqrt.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_ldexp.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_log.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_log2.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_max.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_min.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_normalize.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_pow.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_quantizeToF16.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_radians.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_reflect.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_refract.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_reverseBits.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_round.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_saturate.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_sign.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_sin.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_sinh.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_smoothstep.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_sqrt.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_step.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_tan.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_tanh.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_transpose.ts 56.64 kB ($${\color{red}+0.7\%}$$)
std_trunc.ts 56.64 kB ($${\color{red}+0.7\%}$$)

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

Test tsdown
tgpu_init.ts 263.07 kB ($${\color{green}-3.5\%}$$)
tgpu_initFromDevice.ts 262.53 kB ($${\color{green}-3.7\%}$$)
tgpu_resolve.ts 161.72 kB ($${\color{green}-40.7\%}$$)
tgpu_resolveWithContext.ts 161.66 kB ($${\color{green}-40.7\%}$$)
tgpu_bindGroupLayout.ts 62.70 kB ($${\color{green}-77.0\%}$$)
tgpu_mutableAccessor.ts 57.41 kB ($${\color{green}-78.9\%}$$)
tgpu_accessor.ts 57.41 kB ($${\color{green}-78.9\%}$$)
tgpu_privateVar.ts 56.11 kB ($${\color{green}-79.4\%}$$)
tgpu_workgroupVar.ts 56.10 kB ($${\color{green}-79.4\%}$$)
tgpu_const.ts 55.52 kB ($${\color{green}-79.6\%}$$)
tgpu_lazy.ts 55.32 kB ($${\color{green}-79.7\%}$$)
tgpu_fragmentFn.ts 39.98 kB ($${\color{green}-85.3\%}$$)
tgpu_vertexFn.ts 39.79 kB ($${\color{green}-85.4\%}$$)
tgpu_fn.ts 39.71 kB ($${\color{green}-85.4\%}$$)
tgpu_computeFn.ts 39.50 kB ($${\color{green}-85.5\%}$$)
tgpu_vertexLayout.ts 28.33 kB ($${\color{green}-89.6\%}$$)
tgpu_comptime.ts 16.01 kB ($${\color{green}-94.1\%}$$)
tgpu_unroll.ts 1.79 kB ($${\color{green}-99.3\%}$$)
tgpu_slot.ts 1.75 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 marked this pull request as ready for review August 25, 2026 19:56
Copilot AI lite review requested due to automatic review settings August 25, 2026 19:56

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

Refactors TypeGPU’s GPU-callable invocation model by moving call behavior onto schemas/functions themselves and separating strict signature metadata from the call implementation, with WGSL generation updated to route all calls through a unified ShaderGenerator.call(...) path.

Changes:

  • Replace the $gpuCallable object-with-.call(...) pattern with a direct callable symbol function and introduce $gpuCallableStrictSignature for strict signature metadata.
  • Centralize call handling in WgslGenerator.call(...), including strict-signature-based argument conversion and improved argument count diagnostics.
  • Update schemas (struct/array) and standard library helpers to implement the new calling convention; adjust tests to match updated error messages and typing behavior.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/typegpu/tests/tgsl/wgslGenerator.test.ts Updates inline snapshot for new strict-signature argument count error message.
packages/typegpu/tests/struct.test.ts Updates snapshot message and removes non-null assertions to match new call/type behavior.
packages/typegpu/tests/bindGroupLayout.test.ts Removes non-null assertion in schema call usage.
packages/typegpu/tests/array.test.ts Updates inline snapshot for new strict-signature argument count error message.
packages/typegpu/src/types.ts Introduces GPUCallableStrictSignature, reshapes GPUCallable, and tightens generateLog args type to readonly.
packages/typegpu/src/tgsl/wgslGenerator.ts Adds centralized call(...) method and refactors call-expression generation to use it.
packages/typegpu/src/tgsl/shaderGenerator.ts Adds call(callee, args) to the ShaderGenerator interface.
packages/typegpu/src/tgsl/forOfUtils.ts Updates arrayLength GPU-callable invocation to the new $gpuCallable(ctx, args) form.
packages/typegpu/src/std/numeric.ts Adjusts variadic helper typing to accept readonly tuples.
packages/typegpu/src/std/matrix.ts Updates std matrix wrappers to bind the new $gpuCallable function directly.
packages/typegpu/src/std/environment.ts Converts $gpuCallable implementation to a direct function.
packages/typegpu/src/shared/symbols.ts Adds $gpuCallableStrictSignature symbol.
packages/typegpu/src/data/wgslTypes.ts Refactors WgslStruct/WgslArray types to extend DualFn for unified call typing.
packages/typegpu/src/data/vertexFormatData.ts Converts $gpuCallable implementation to a direct function.
packages/typegpu/src/data/struct.ts Moves struct schema call behavior into $gpuCallable and provides strict signature metadata.
packages/typegpu/src/data/snippet.ts Makes MapValueToSnippet<T> readonly to better align with readonly argument flows.
packages/typegpu/src/data/schemaCallWrapper.ts Updates schema GPU-call wrapper to use $gpuCallable(ctx, args) form.
packages/typegpu/src/data/ref.ts Converts _ref GPU-callable implementation to a direct function.
packages/typegpu/src/data/array.ts Moves array schema call behavior into $gpuCallable and provides strict signature metadata.
packages/typegpu/src/core/unroll/tgpuUnroll.ts Converts unroll GPU-callable implementation to a direct function.
packages/typegpu/src/core/slot/accessor.ts Updates GPU-callable accessors to use $gpuCallable(ctx, []).
packages/typegpu/src/core/function/dualImpl.ts Splits strict signature metadata onto $gpuCallableStrictSignature and updates $gpuCallable to a direct function.
packages/typegpu/src/core/function/createCallableSchema.ts Converts callable schema GPU-callable implementation to a direct function.
packages/typegpu/src/core/function/comptime.ts Moves comptime GPU-callable behavior onto a shared prototype and updates internal metadata shape.

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

pushBlockScope(): void;
popBlockScope(): void;
generateLog(op: SupportedLogOp, args: Snippet[]): Snippet;
generateLog(op: SupportedLogOp, args: readonly Snippet[]): Snippet;
Comment on lines +36 to +39
/**
* Can be called on the GPU
*/
export const $gpuCallableStrictSignature = Symbol(`typegpu:${version}:$gpuCallableStrictSignature`);

@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 new issues found.

Reviewed changes — single commit 2c95c245 ("chore: Refactor GPU function call mechanism"): struct and array schemas now own their call semantics through $gpuCallable, the $gpuCallable symbol changed shape (callable (ctx, args) => Snippet plus a separate $gpuCallableStrictSignature), and argument-snippet generation was split out of call resolution into a new WgslGenerator.call(callee, args) funnel that _expression and generators can share.

Verified end-to-end: the full packages/typegpu suite (176 files / 2362 tests) and @typegpu/gl (58 tests) pass, and test:types is green on both packages. The struct/array codegen that moved out of _expression into the schema impls maps onto typeInstantiation with identical output for the WGSL generator and the GLSL GlslGenerator override (verified by tracing the array/struct paths through typeInstantiation), so downstream behavior is unchanged. The rewritten over-arity error messages have their snapshots updated in all four touched tests. No leftover consumers of the old object-shaped $gpuCallable anywhere, and no other package implements the symbol.

ℹ️ Redundant second conversion of strict-signature arguments

On AST-backed calls to a strict-signature callable, argument snippets are converted twice: once in the _expression branch via _typedExpression(arg, argType) (which also sets ctx.expectedType, needed for literal typing) and again inside WgslGenerator.call at the isGPUCallable/strict-signature branch. The second pass is a type-preserving no-op (tryConvertSnippet short-circuits on target === dataType), and the schema impls re-run tryConvertSnippet a third time on the already-typed value, so the behavior is correct — just redundant. This includes the pre-existing arrangement, so it is not a regression, but since call() is now the documented funnel for generators, it is worth deciding where conversion is owned once.

Technical details
# Strict-signature argument conversion happens three times

## Affected sites
- packages/typegpu/src/tgsl/wgslGenerator.ts:819-836 (_expression strict-signature branch) — converts via `_typedExpression`
- packages/typegpu/src/tgsl/wgslGenerator.ts:461-471 (call()'s strict-signature branch) — converts via `tryConvertSnippet`
- packages/typegpu/src/data/array.ts:137 and packages/typegpu/src/data/struct.ts:100 — schema impls convert a third time

## Required outcome
- No behavior change; evaluate whether the second and third conversions can be removed without losing the `expectedType` hint that `_typedExpression` provides.

ℹ️ Naming: call sits ambiguously next to emitCall

The new ShaderGenerator.call(callee, args) (returns a Snippet) reads confusingly against the existing emitCall(name, templateParams, args) (returns a string that emits the resolved call text), which std codegenImpls and GlslGenerator override. Since ShaderGenerator is already marked unstable, a more distinct name for the invoke-and-resolve path (e.g. callGpu/resolveCall) would spare downstream generator authors a footgun.

Technical details
# Naming ambiguity: `call` vs `emitCall`

## Affected sites
- packages/typegpu/src/tgsl/shaderGenerator.ts:126 — `call(callee: AnyFn, args: readonly Snippet[]): Snippet`
- packages/typegpu/src/tgsl/shaderGenerator.ts:127 — `emitCall(name: string, templateParams: readonly Snippet[], args: readonly Snippet[]): string`

## Required outcome
- Decide on a name for `call` that does not collide conceptually with `emitCall`; renames only touch the unstable interface and the typegpu-gl generator, which inherits from WgslGenerator.

Also nice touch: dropping the now-unnecessary ! non-null assertions in the two struct/bind-group tests under the new DualFn-based WgslArray typing.

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

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.

2 participants