Skip to content

impr: Less work on the shader generation happy path - #3240

Open
iwoplaza wants to merge 1 commit into
mainfrom
impr/faster-shader-generation
Open

iwoplaza wants to merge 1 commit into
mainfrom
impr/faster-shader-generation

Conversation

@iwoplaza

Copy link
Copy Markdown
Collaborator

Shader generation runs during an app's first frame, on code the JS engine hasn't optimized yet, so every bit of unneeded work there delays the first frame. These changes remove work the generator did even when nothing goes wrong, without changing the generated WGSL.

  • Reserved words: each namespace copied ~500 reserved WGSL words into its own Set. isIdentifierTaken now checks the shared sets directly.
  • Error messages: invariant messages that stringify AST nodes (e.g. on every numeric literal) are passed as functions, so they're only built when the check fails.
  • Externals: each external is coerced to a snippet once per function instead of on every reference. Function externals aren't copied when there are no arguments or outputs to merge in.
  • Conversion ranks: cached per pair of schemas in a WeakMap (schemas are immutable once created; the invariant is documented next to the cache).
  • Small helpers: stitch uses plain loops instead of filter/map/join. The var/let placeholders in a function body are filled with one fixed regex instead of one built per function. Memoized items are matched against slot values without copying the slot map into an array (and the duplicated check became one method).
  • Type guards and proxies: the hottest guards (isDecorated, isLooseDecorated, isPtr) test the cheap type property first, and texture views reuse their value proxy.

tgpu.resolve of the shaders of four docs examples in Node 22: median of interleaved runs in fresh processes, main and this branch built side by side. "First" is the first resolve in a process; "later" is the median of the following 29.

Example First: main → this PR Later: main → this PR
boids 23.1 → 19.6 ms (−15%) 2.72 → 2.02 ms (−26%)
stable-fluid 39.7 → 38.0 ms (−4%) 11.18 → 9.54 ms (−15%)
slime-mold-3d 53.5 → 42.1 ms (−21%) 12.78 → 10.80 ms (−15%)
jelly-slider 74.6 → 62.2 ms (−17%) 21.50 → 16.54 ms (−23%)

16 rounds per example, 30 for stable-fluid. Two identical builds measured the same way differ by ~1.5%. The generated WGSL is identical to main's for all 22 shader modules.

@github-actions

github-actions Bot commented Oct 21, 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/typegpu@2e2bb7d5f62ca7ce3cae6849765d779274e80ec9
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/react@2e2bb7d5f62ca7ce3cae6849765d779274e80ec9

benchmark
view benchmark

commit
view commit

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

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

🟢 Decreased ➖ Unchanged 🔴 Increased (max 1.00%) ❔ Unknown
0 39 316 0

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

Click to reveal the results table (266 entries).
Test tsdown
d_bool.ts 20.24 kB ($${\color{red}+1.0\%}$$)
d_f16.ts 20.24 kB ($${\color{red}+1.0\%}$$)
d_f32.ts 20.24 kB ($${\color{red}+1.0\%}$$)
d_i32.ts 20.24 kB ($${\color{red}+1.0\%}$$)
d_u32.ts 20.24 kB ($${\color{red}+1.0\%}$$)
d_ref.ts 20.25 kB ($${\color{red}+1.0\%}$$)
d_u16.ts 20.26 kB ($${\color{red}+1.0\%}$$)
d_abstractInt.ts 20.31 kB ($${\color{red}+1.0\%}$$)
d_abstractFloat.ts 20.32 kB ($${\color{red}+1.0\%}$$)
d_textureDepth2d.ts 20.68 kB ($${\color{red}+1.0\%}$$)
d_textureDepthCube.ts 20.69 kB ($${\color{red}+1.0\%}$$)
d_texture1d.ts 20.69 kB ($${\color{red}+1.0\%}$$)
d_texture2d.ts 20.69 kB ($${\color{red}+1.0\%}$$)
d_texture3d.ts 20.69 kB ($${\color{red}+1.0\%}$$)
d_textureCube.ts 20.70 kB ($${\color{red}+1.0\%}$$)
d_textureDepth2dArray.ts 20.70 kB ($${\color{red}+1.0\%}$$)
d_textureDepthCubeArray.ts 20.71 kB ($${\color{red}+1.0\%}$$)
d_textureDepthMultisampled2d.ts 20.71 kB ($${\color{red}+1.0\%}$$)
d_texture2dArray.ts 20.71 kB ($${\color{red}+1.0\%}$$)
d_textureCubeArray.ts 20.72 kB ($${\color{red}+1.0\%}$$)
d_textureMultisampled2d.ts 20.72 kB ($${\color{red}+1.0\%}$$)
std_isKnownAtComptime.ts 20.86 kB ($${\color{red}+1.0\%}$$)
tgpu_comptime.ts 21.59 kB ($${\color{red}+0.9\%}$$)
std_subgroupElect.ts 21.43 kB ($${\color{red}+0.9\%}$$)
std_subgroupAll.ts 21.44 kB ($${\color{red}+0.9\%}$$)
std_subgroupAny.ts 21.44 kB ($${\color{red}+0.9\%}$$)
std_quadSwapX.ts 21.44 kB ($${\color{red}+0.9\%}$$)
std_quadSwapY.ts 21.44 kB ($${\color{red}+0.9\%}$$)
std_subgroupOr.ts 21.44 kB ($${\color{red}+0.9\%}$$)
std_subgroupAdd.ts 21.44 kB ($${\color{red}+0.9\%}$$)
std_subgroupAnd.ts 21.44 kB ($${\color{red}+0.9\%}$$)
std_subgroupMax.ts 21.44 kB ($${\color{red}+0.9\%}$$)
std_subgroupMin.ts 21.44 kB ($${\color{red}+0.9\%}$$)
std_subgroupMul.ts 21.44 kB ($${\color{red}+0.9\%}$$)
std_subgroupXor.ts 21.44 kB ($${\color{red}+0.9\%}$$)
std_quadSwapDiagonal.ts 21.45 kB ($${\color{red}+0.9\%}$$)
std_subgroupExclusiveAdd.ts 21.46 kB ($${\color{red}+0.9\%}$$)
std_subgroupExclusiveMul.ts 21.46 kB ($${\color{red}+0.9\%}$$)
std_subgroupInclusiveAdd.ts 21.46 kB ($${\color{red}+0.9\%}$$)
std_subgroupInclusiveMul.ts 21.46 kB ($${\color{red}+0.9\%}$$)
std_subgroupBroadcastFirst.ts 21.46 kB ($${\color{red}+0.9\%}$$)
std_storageBarrier.ts 21.48 kB ($${\color{red}+0.9\%}$$)
std_textureBarrier.ts 21.48 kB ($${\color{red}+0.9\%}$$)
std_workgroupBarrier.ts 21.49 kB ($${\color{red}+0.9\%}$$)
std_discard.ts 21.49 kB ($${\color{red}+0.9\%}$$)
std_textureNumLayers.ts 21.51 kB ($${\color{red}+0.9\%}$$)
std_textureNumLevels.ts 21.51 kB ($${\color{red}+0.9\%}$$)
std_textureNumSamples.ts 21.51 kB ($${\color{red}+0.9\%}$$)
std_copy.ts 21.74 kB ($${\color{red}+0.9\%}$$)
std_textureSampleCompare.ts 21.54 kB ($${\color{red}+0.9\%}$$)
std_textureSampleCompareLevel.ts 21.56 kB ($${\color{red}+0.9\%}$$)
std_textureStore.ts 21.56 kB ($${\color{red}+0.9\%}$$)
std_workgroupUniformLoad.ts 21.58 kB ($${\color{red}+0.9\%}$$)
std_extensionEnabled.ts 21.81 kB ($${\color{red}+0.9\%}$$)
std_atomicLoad.ts 21.62 kB ($${\color{red}+0.9\%}$$)
std_atomicOr.ts 21.66 kB ($${\color{red}+0.9\%}$$)
std_atomicAdd.ts 21.66 kB ($${\color{red}+0.9\%}$$)
std_atomicAnd.ts 21.66 kB ($${\color{red}+0.9\%}$$)
std_atomicMax.ts 21.66 kB ($${\color{red}+0.9\%}$$)
std_atomicMin.ts 21.66 kB ($${\color{red}+0.9\%}$$)
std_atomicSub.ts 21.66 kB ($${\color{red}+0.9\%}$$)
std_atomicXor.ts 21.66 kB ($${\color{red}+0.9\%}$$)
std_atomicExchange.ts 21.67 kB ($${\color{red}+0.9\%}$$)
std_atomicStore.ts 21.70 kB ($${\color{red}+0.9\%}$$)
std_quadBroadcast.ts 21.71 kB ($${\color{red}+0.9\%}$$)
std_arrayLength.ts 21.71 kB ($${\color{red}+0.9\%}$$)
std_subgroupShuffleUp.ts 21.71 kB ($${\color{red}+0.9\%}$$)
std_subgroupShuffle.ts 21.71 kB ($${\color{red}+0.9\%}$$)
std_subgroupShuffleXor.ts 21.71 kB ($${\color{red}+0.9\%}$$)
std_subgroupShuffleDown.ts 21.71 kB ($${\color{red}+0.9\%}$$)
std_subgroupBroadcast.ts 21.72 kB ($${\color{red}+0.9\%}$$)
std_range.ts 22.08 kB ($${\color{red}+0.9\%}$$)
std_isBeingTranspiled.ts 22.12 kB ($${\color{red}+0.9\%}$$)
std_getShaderStage.ts 22.18 kB ($${\color{red}+0.9\%}$$)
std_getTargetShaderLanguage.ts 22.19 kB ($${\color{red}+0.9\%}$$)
std_typeOf.ts 22.23 kB ($${\color{red}+0.9\%}$$)
d_disarrayOf.ts 22.25 kB ($${\color{red}+0.9\%}$$)
d_deepEqual.ts 23.02 kB ($${\color{red}+0.9\%}$$)
d_schemasEqualNominally.ts 23.02 kB ($${\color{red}+0.9\%}$$)
d_schemasEqualStructurally.ts 23.02 kB ($${\color{red}+0.9\%}$$)
std_atomicCompareExchangeWeak.ts 24.21 kB ($${\color{red}+0.8\%}$$)
std_subgroupBallot.ts 27.90 kB ($${\color{red}+0.7\%}$$)
std_textureSampleBaseClampToEdge.ts 27.99 kB ($${\color{red}+0.7\%}$$)
std_textureSampleGrad.ts 28.00 kB ($${\color{red}+0.7\%}$$)
std_textureSampleBias.ts 28.06 kB ($${\color{red}+0.7\%}$$)
d_uint8.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_float16.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_float16x2.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_float16x4.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_float32.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_float32x2.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_float32x3.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_float32x4.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_sint16.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_sint16x2.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_sint16x4.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_sint32.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_sint32x2.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_sint32x3.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_sint32x4.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_sint8.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_sint8x2.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_sint8x4.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_snorm16.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_snorm16x2.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_snorm16x4.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_snorm8.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_snorm8x2.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_snorm8x4.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_uint16.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_uint16x2.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_uint16x4.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_uint32.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_uint32x2.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_uint32x3.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_uint32x4.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_uint8x2.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_uint8x4.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_unorm10_10_10_2.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_unorm16.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_unorm16x2.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_unorm16x4.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_unorm8.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_unorm8x2.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_unorm8x4.ts 28.20 kB ($${\color{red}+0.7\%}$$)
d_unorm8x4_bgra.ts 28.20 kB ($${\color{red}+0.7\%}$$)
std_textureSample.ts 28.08 kB ($${\color{red}+0.7\%}$$)
std_textureSampleLevel.ts 28.09 kB ($${\color{red}+0.7\%}$$)
std_textureGatherCompare.ts 28.12 kB ($${\color{red}+0.7\%}$$)
std_textureDimensions.ts 28.15 kB ($${\color{red}+0.7\%}$$)
d_formatToWGSLType.ts 28.19 kB ($${\color{red}+0.7\%}$$)
d_packedFormats.ts 28.22 kB ($${\color{red}+0.7\%}$$)
d_isPackedData.ts 28.26 kB ($${\color{red}+0.7\%}$$)
std_textureGather.ts 28.32 kB ($${\color{red}+0.7\%}$$)
d_alignmentOf.ts 29.15 kB ($${\color{red}+0.7\%}$$)
std_textureLoad.ts 29.94 kB ($${\color{red}+0.7\%}$$)
std_bitcast.ts 30.72 kB ($${\color{red}+0.7\%}$$)
std_pack2x16float.ts 31.73 kB ($${\color{red}+0.6\%}$$)
std_unpack2x16unorm.ts 31.73 kB ($${\color{red}+0.6\%}$$)
std_unpack2x16float.ts 31.74 kB ($${\color{red}+0.6\%}$$)
std_pack4xI8.ts 31.74 kB ($${\color{red}+0.6\%}$$)
std_unpack4xI8.ts 31.74 kB ($${\color{red}+0.6\%}$$)
std_pack4xU8.ts 31.75 kB ($${\color{red}+0.6\%}$$)
std_unpack4xU8.ts 31.75 kB ($${\color{red}+0.6\%}$$)
std_unpack2x16snorm.ts 31.75 kB ($${\color{red}+0.6\%}$$)
std_unpack4x8unorm.ts 31.77 kB ($${\color{red}+0.6\%}$$)
std_unpack4x8snorm.ts 31.82 kB ($${\color{red}+0.6\%}$$)
d_isBuiltin.ts 31.88 kB ($${\color{red}+0.6\%}$$)
std_pack2x16snorm.ts 31.91 kB ($${\color{red}+0.6\%}$$)
std_pack2x16unorm.ts 31.91 kB ($${\color{red}+0.6\%}$$)
std_pack4xU8Clamp.ts 31.92 kB ($${\color{red}+0.6\%}$$)
d_sizeOf.ts 31.92 kB ($${\color{red}+0.6\%}$$)
std_pack4xI8Clamp.ts 31.93 kB ($${\color{red}+0.6\%}$$)
d_isContiguous.ts 31.93 kB ($${\color{red}+0.6\%}$$)
d_getLongestContiguousPrefix.ts 31.94 kB ($${\color{red}+0.6\%}$$)
std_pack4x8snorm.ts 31.98 kB ($${\color{red}+0.6\%}$$)
std_pack4x8unorm.ts 31.98 kB ($${\color{red}+0.6\%}$$)
d_arrayOf.ts 33.67 kB ($${\color{red}+0.6\%}$$)
d_size.ts 33.61 kB ($${\color{red}+0.6\%}$$)
d_align.ts 33.61 kB ($${\color{red}+0.6\%}$$)
d_location.ts 33.62 kB ($${\color{red}+0.6\%}$$)
d_interpolate.ts 33.62 kB ($${\color{red}+0.6\%}$$)
tgpu_vertexLayout.ts 33.93 kB ($${\color{red}+0.6\%}$$)
d_invariant.ts 33.99 kB ($${\color{red}+0.6\%}$$)
std_identity2.ts 34.41 kB ($${\color{red}+0.6\%}$$)
std_identity3.ts 34.41 kB ($${\color{red}+0.6\%}$$)
std_identity4.ts 34.41 kB ($${\color{red}+0.6\%}$$)
std_rotationX4.ts 34.41 kB ($${\color{red}+0.6\%}$$)
std_rotationY4.ts 34.41 kB ($${\color{red}+0.6\%}$$)
std_rotationZ4.ts 34.41 kB ($${\color{red}+0.6\%}$$)
std_scaling4.ts 34.41 kB ($${\color{red}+0.6\%}$$)
std_translation4.ts 34.41 kB ($${\color{red}+0.6\%}$$)
d_matToArray.ts 34.54 kB ($${\color{red}+0.6\%}$$)
common_writeSoA.ts 34.53 kB ($${\color{red}+0.6\%}$$)
std_dot4I8Packed.ts 35.79 kB ($${\color{red}+0.6\%}$$)
std_dot4U8Packed.ts 35.79 kB ($${\color{red}+0.6\%}$$)
std_firstLeadingBit.ts 35.80 kB ($${\color{red}+0.6\%}$$)
d_memoryLayoutOf.ts 35.50 kB ($${\color{red}+0.6\%}$$)
std_determinant.ts 35.88 kB ($${\color{red}+0.6\%}$$)
std_dpdxFine.ts 35.88 kB ($${\color{red}+0.6\%}$$)
std_dpdyFine.ts 35.88 kB ($${\color{red}+0.6\%}$$)
std_dpdxCoarse.ts 35.89 kB ($${\color{red}+0.6\%}$$)
std_dpdyCoarse.ts 35.89 kB ($${\color{red}+0.6\%}$$)
std_fwidthFine.ts 35.89 kB ($${\color{red}+0.6\%}$$)
std_dpdx.ts 35.89 kB ($${\color{red}+0.6\%}$$)
std_dpdy.ts 35.89 kB ($${\color{red}+0.6\%}$$)
std_fwidthCoarse.ts 35.89 kB ($${\color{red}+0.6\%}$$)
std_fwidth.ts 35.90 kB ($${\color{red}+0.6\%}$$)
std_trunc.ts 35.95 kB ($${\color{red}+0.6\%}$$)
std_reverseBits.ts 35.96 kB ($${\color{red}+0.6\%}$$)
std_quantizeToF16.ts 35.96 kB ($${\color{red}+0.6\%}$$)
std_countOneBits.ts 35.96 kB ($${\color{red}+0.6\%}$$)
std_extractBits.ts 35.97 kB ($${\color{red}+0.6\%}$$)
std_firstTrailingBit.ts 35.97 kB ($${\color{red}+0.6\%}$$)
std_countLeadingZeros.ts 35.98 kB ($${\color{red}+0.6\%}$$)
std_countTrailingZeros.ts 35.98 kB ($${\color{red}+0.6\%}$$)
std_insertBits.ts 35.98 kB ($${\color{red}+0.6\%}$$)
std_faceForward.ts 35.98 kB ($${\color{red}+0.6\%}$$)
std_intdiv.ts 35.99 kB ($${\color{red}+0.6\%}$$)
std_tan.ts 36.03 kB ($${\color{red}+0.6\%}$$)
std_degrees.ts 36.04 kB ($${\color{red}+0.6\%}$$)
std_radians.ts 36.04 kB ($${\color{red}+0.6\%}$$)
std_inverseSqrt.ts 36.07 kB ($${\color{red}+0.6\%}$$)
std_refract.ts 35.90 kB ($${\color{red}+0.6\%}$$)
std_round.ts 36.08 kB ($${\color{red}+0.6\%}$$)
d_builtin.ts 36.13 kB ($${\color{red}+0.6\%}$$)
std_step.ts 36.21 kB ($${\color{red}+0.6\%}$$)
std_ldexp.ts 36.21 kB ($${\color{red}+0.6\%}$$)
std_fma.ts 36.21 kB ($${\color{red}+0.6\%}$$)
std_transpose.ts 36.28 kB ($${\color{red}+0.6\%}$$)
std_not.ts 36.44 kB ($${\color{red}+0.6\%}$$)
std_abs.ts 36.45 kB ($${\color{red}+0.6\%}$$)
std_neg.ts 36.50 kB ($${\color{red}+0.6\%}$$)
std_or.ts 36.51 kB ($${\color{red}+0.6\%}$$)
std_and.ts 36.59 kB ($${\color{red}+0.6\%}$$)
std_lt.ts 36.59 kB ($${\color{red}+0.6\%}$$)
std_eq.ts 36.60 kB ($${\color{red}+0.6\%}$$)
std_cos.ts 36.62 kB ($${\color{red}+0.6\%}$$)
std_exp.ts 36.62 kB ($${\color{red}+0.6\%}$$)
std_log.ts 36.62 kB ($${\color{red}+0.6\%}$$)
std_sin.ts 36.62 kB ($${\color{red}+0.6\%}$$)
std_exp2.ts 36.62 kB ($${\color{red}+0.6\%}$$)
std_sign.ts 36.62 kB ($${\color{red}+0.6\%}$$)
std_acos.ts 36.62 kB ($${\color{red}+0.6\%}$$)
std_asin.ts 36.62 kB ($${\color{red}+0.6\%}$$)
std_atan.ts 36.62 kB ($${\color{red}+0.6\%}$$)
std_ceil.ts 36.62 kB ($${\color{red}+0.6\%}$$)
std_cosh.ts 36.62 kB ($${\color{red}+0.6\%}$$)
std_log2.ts 36.62 kB ($${\color{red}+0.6\%}$$)
std_sinh.ts 36.62 kB ($${\color{red}+0.6\%}$$)
std_sqrt.ts 36.62 kB ($${\color{red}+0.6\%}$$)
std_tanh.ts 36.62 kB ($${\color{red}+0.6\%}$$)
std_acosh.ts 36.63 kB ($${\color{red}+0.6\%}$$)
std_asinh.ts 36.63 kB ($${\color{red}+0.6\%}$$)
std_atanh.ts 36.63 kB ($${\color{red}+0.6\%}$$)
std_floor.ts 36.63 kB ($${\color{red}+0.6\%}$$)
std_fract.ts 36.63 kB ($${\color{red}+0.6\%}$$)
std_gt.ts 36.82 kB ($${\color{red}+0.6\%}$$)
std_ge.ts 36.66 kB ($${\color{red}+0.6\%}$$)
std_ne.ts 36.67 kB ($${\color{red}+0.6\%}$$)
std_le.ts 36.74 kB ($${\color{red}+0.6\%}$$)
std_saturate.ts 36.77 kB ($${\color{red}+0.6\%}$$)
std_pow.ts 36.80 kB ($${\color{red}+0.6\%}$$)
std_atan2.ts 36.81 kB ($${\color{red}+0.6\%}$$)
std_clamp.ts 36.90 kB ($${\color{red}+0.6\%}$$)
std_max.ts 36.92 kB ($${\color{red}+0.6\%}$$)
std_min.ts 36.92 kB ($${\color{red}+0.6\%}$$)
tgpu_fn.ts 45.58 kB ($${\color{red}+0.5\%}$$)
std_smoothstep.ts 37.02 kB ($${\color{red}+0.5\%}$$)
std_mix.ts 37.17 kB ($${\color{red}+0.5\%}$$)
tgpu_computeFn.ts 45.38 kB ($${\color{red}+0.5\%}$$)
std_mod.ts 37.45 kB ($${\color{red}+0.5\%}$$)
std_div.ts 37.49 kB ($${\color{red}+0.5\%}$$)
std_add.ts 37.57 kB ($${\color{red}+0.5\%}$$)
std_select.ts 37.42 kB ($${\color{red}+0.5\%}$$)
tgpu_vertexFn.ts 45.93 kB ($${\color{red}+0.5\%}$$)
tgpu_fragmentFn.ts 46.12 kB ($${\color{red}+0.5\%}$$)
std_frexp.ts 38.49 kB ($${\color{red}+0.5\%}$$)
std_modf.ts 38.62 kB ($${\color{red}+0.5\%}$$)
std_bitShiftLeft.ts 39.75 kB ($${\color{red}+0.5\%}$$)
std_bitShiftRight.ts 39.76 kB ($${\color{red}+0.5\%}$$)
std_all.ts 39.77 kB ($${\color{red}+0.5\%}$$)
common_fullScreenTriangle.ts 48.81 kB ($${\color{red}+0.5\%}$$)
std_dot.ts 40.05 kB ($${\color{red}+0.5\%}$$)
std_length.ts 40.15 kB ($${\color{red}+0.5\%}$$)
std_any.ts 40.59 kB ($${\color{red}+0.5\%}$$)

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

@github-actions

github-actions Bot commented Oct 21, 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.84, 1.65, 3.75, 5.75, 6.63, 11.54, 21.88, 20.89]
  line [1.10, 2.16, 5.01, 8.02, 8.74, 13.12, 27.10, 29.03]
  line [1.01, 2.08, 4.61, 7.15, 8.33, 13.01, 27.56, 26.37]
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.26, 0.44, 0.57, 0.69, 0.96, 0.99, 1.15, 1.32]
  line [0.33, 0.53, 0.79, 0.93, 1.27, 1.33, 1.55, 1.75]
  line [0.31, 0.49, 0.68, 0.87, 1.16, 1.27, 1.50, 1.62]
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, 1.88, 3.63, 6.59, 12.15, 21.89, 47.65, 98.36]
  line [1.06, 2.37, 4.65, 8.23, 15.11, 31.55, 64.42, 130.43]
  line [0.99, 2.42, 3.95, 7.58, 13.33, 27.50, 58.79, 120.15]
Loading

@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

A performance pass over the shader-generation hot path; the PR asserts the generated WGSL is unchanged. I verified all ten files and ran the source suite plus the downstream generator packages.

  • Reserved-word lookup — Namespace no longer copies bannedTokens/builtins/templateEnumerants into takenGlobalIdentifiers; isIdentifierTaken checks the shared sets directly. takenGlobalIdentifiers is only read by isIdentifierTaken/reserveIdentifier, so removing the seed is safe.
  • Lazy invariant messages — the AST-stringifying invariant message for numeric literals is now a thunk.
  • Externals — mergeFunctionExternals returns the base map directly when args/out are both undefined; function scopes cache coerced external snippets in a new externalSnippets map. Block scopes are still scanned before the function scope, so local shadowing is unaffected, and no consumer writes to externalMap.
  • Conversion ranks — ranks memoized per (src, dest) pair in two WeakMaps. computeConversionRank only reads immutable schema facts, and no schema field is mutated after construction.
  • Hot-path helpers — stitch uses explicit loops; #slotsHaveValues replaces the array-copy every; the var/let placeholders are resolved with one fixed /#VAR_\d+#/g regex (generation sites all emit #VAR_${n}#); isLooseDecorated/isPtr/isDecorated test the cheap type field first; both texture view classes reuse their value proxy.

Verification: pnpm vitest run --project=!browser packages/typegpu/tests (2921 passed) and packages/typegpu-gl/tests packages/unplugin-typegpu/test packages/typegpu-sdf/tests packages/typegpu-radiance-cascades/tests packages/typegpu-sort/tests (357 passed, 2 skipped).

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

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

External snippet caching can retain stale getter values after compile-time updates and change generated WGSL.

1 open issue
What changed in this PR

Reduces shader-generation overhead to improve first-frame performance, aiming to preserve generated WGSL.

Changes:

  • Caches conversion ranks, external snippets, and texture value proxies.
  • Removes unnecessary allocations and defers diagnostic formatting.
  • Streamlines identifier checks, placeholder replacement, and type guards.
File Description
packages/​typegpu/​src/​types.ts Adds per-function external snippet storage.
packages/​typegpu/​src/​tgsl/​wgslGenerator.ts Defers diagnostics and simplifies placeholder replacement.
packages/​typegpu/​src/​tgsl/​conversion.ts Caches conversion ranks by schema pair.
packages/​typegpu/​src/​resolutionCtx.ts Caches externals and streamlines identifier and slot checks.
packages/​typegpu/​src/​data/​wgslTypes.ts Reorders hot type-guard checks.
packages/​typegpu/​src/​data/​dataTypes.ts Optimizes loose-decorated checks.
packages/​typegpu/​src/​core/​texture/​texture.ts Reuses texture value proxies.
packages/​typegpu/​src/​core/​resolve/​stitch.ts Replaces array transformations with loops.
packages/​typegpu/​src/​core/​resolve/​namespace.ts Avoids copying reserved-word sets.
packages/​typegpu/​src/​core/​resolve/​externals.ts Skips unnecessary external-map copies.

Review effort: Balanced

Comment thread packages/typegpu/src/resolutionCtx.ts

@aleksanderkatan aleksanderkatan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice job! No issues correctness-wise.

I ran an agent to check the resolution benefit of each of the changes.

The following all have noticeable impact: var-let-regex, merge-externals-noop, conversion-rank-cache, type-guard-order, external-snippet-cache, lazy-invariant-msg and stitch-loops.

The rest seems to have no noticeable effect, but they still are nice cleanup, and are worth keeping IMO: reserved-words, texture-proxy-reuse, slot-match-no-copy.

Comment thread packages/typegpu/src/data/dataTypes.ts Outdated
Comment thread packages/typegpu/src/data/wgslTypes.ts Outdated
Comment thread packages/typegpu/src/data/wgslTypes.ts Outdated
@iwoplaza
iwoplaza force-pushed the impr/faster-shader-generation branch from 43021ff to 8a2e4f5 Compare October 21, 2026 12:54
Shader generation runs during an app's first frame, on code the JS engine hasn't optimized yet, so every bit of unneeded work there delays the first frame. These changes remove work the generator did even when nothing goes wrong, without changing the generated WGSL.

- **Reserved words:** each namespace copied ~500 reserved WGSL words into its own Set. `isIdentifierTaken` now checks the shared sets directly.
- **Error messages:** `invariant` messages that stringify AST nodes (e.g. on every numeric literal) are passed as functions, so they're only built when the check fails.
- **Externals:** each external is coerced to a snippet once per function instead of on every reference. Function externals aren't copied when there are no arguments or outputs to merge in.
- **Conversion ranks:** cached per pair of schemas in a `WeakMap` (schemas are immutable once created; the invariant is documented next to the cache).
- **Small helpers:** `stitch` uses plain loops instead of `filter`/`map`/`join`. The `var`/`let` placeholders in a function body are filled with one fixed regex instead of one built per function. Memoized items are matched against slot values without copying the slot map into an array (and the duplicated check became one method).
- **Type guards and proxies:** the hottest guards (`isDecorated`, `isLooseDecorated`, `isPtr`) test the cheap `type` property first, and texture views reuse their value proxy.

`tgpu.resolve` of the shaders of four docs examples in Node 22: median of interleaved runs in fresh processes, `main` and this branch built side by side. "First" is the first resolve in a process; "later" is the median of the following 29.

| Example | First: main → this PR | Later: main → this PR |
|---|---|---|
| boids | 23.1 → 19.6 ms (−15%) | 2.72 → 2.02 ms (−26%) |
| stable-fluid | 39.7 → 38.0 ms (−4%) | 11.18 → 9.54 ms (−15%) |
| slime-mold-3d | 53.5 → 42.1 ms (−21%) | 12.78 → 10.80 ms (−15%) |
| jelly-slider | 74.6 → 62.2 ms (−17%) | 21.50 → 16.54 ms (−23%) |

16 rounds per example, 30 for stable-fluid. Two identical builds measured the same way differ by ~1.5%. The generated WGSL is identical to `main`'s for all 22 shader modules.

- `typegpu` test suite passes (192 files, 2771 tests), along with typecheck, oxlint and oxfmt.
@iwoplaza
iwoplaza force-pushed the impr/faster-shader-generation branch from 8a2e4f5 to 2e2bb7d Compare October 21, 2026 12:54

@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

A re-review of the delta since the prior pullfrog review (43021ffd). The change is a mechanical extension of an already-approved optimization; no generated-WGSL impact, and the full packages/typegpu/tests suite passes (2955 tests).

  • Type-guard reorder extended — the cheap type/kind/resourceType field is now tested before isMarkedInternal in roughly two dozen more guards (isTgpuFn, isGenericFn, isDisarray, isUnstruct, isPackedData, isVecInstance, isVec2/3/4, isMatInstance, isMat2x2f/3x3f/4x4f, isInteger, isWgslArray, isWgslStruct, isAtomic, the attribute guards, isAbstractFloat/isAbstractInt, isVoid, isBool, isNumericSchema, isHalfPrecisionSchema), up from just isDecorated/isLooseDecorated/isPtr in the first commit.
  • Explanatory comments dropped — the three // Testing the cheap \type` field first for performance reasons` notes are gone now that the ordering is uniform.

Every reordered guard keeps its optional chaining, so null/undefined still short-circuit before the symbol read, and TypeGPU's proxies are all $internal-marked — behaviour is unchanged, only the common (marked) case is reached in a cheaper order. For marked values both operands were already evaluated, so this is neutral-to-faster on the value-proxy paths too.

Note: the PR description still says only "the hottest guards" test type first, which no longer matches the code — worth a one-line update, not blocking.

( ◦°^°◦) 🏴‍☠️ Avast! I scoured the fresh commit — every type-guard swap from stem to stern — checked the proxy waters fer hidden throwin' getters, an' ran the whole 2955-test armada. No leaks in the hull, so I'm signin' off clean.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants