feat: Add missing std functions - #2925
Conversation
|
pkg.pr.new packages benchmark commit |
Bundle size comparison (
|
| 🟢 Decreased | ➖ Unchanged | 🔴 Increased (max 17.74%) | ❔ Unknown |
|---|---|---|---|
| 0 | 276 | 49 | 23 |
import * as ... in PR vs import * as ... in target (did bundle size increase?):
Click to reveal the results table (72 entries).
| Test | tsdown |
|---|---|
| std_textureBarrier.ts | 20.86 kB ( |
| std_atomicLoad.ts | 20.85 kB ( |
| std_atomicStore.ts | 20.85 kB ( |
| std_atomicAdd.ts | 20.86 kB ( |
| std_atomicAnd.ts | 20.86 kB ( |
| std_atomicMax.ts | 20.86 kB ( |
| std_atomicMin.ts | 20.86 kB ( |
| std_atomicOr.ts | 20.86 kB ( |
| std_atomicSub.ts | 20.86 kB ( |
| std_atomicXor.ts | 20.86 kB ( |
| std_storageBarrier.ts | 20.86 kB ( |
| std_workgroupBarrier.ts | 20.85 kB ( |
| std_pack2x16float.ts | 40.86 kB ( |
| std_pack4x8unorm.ts | 40.86 kB ( |
| std_unpack2x16float.ts | 40.86 kB ( |
| std_unpack4x8unorm.ts | 40.86 kB ( |
| STATIC_std.ts | 103.68 kB ( |
| std_textureDimensions.ts | 28.99 kB ( |
| std_textureGather.ts | 28.99 kB ( |
| std_textureLoad.ts | 28.99 kB ( |
| std_textureSample.ts | 28.99 kB ( |
| std_textureSampleBaseClampToEdge.ts | 28.99 kB ( |
| std_textureSampleBias.ts | 28.99 kB ( |
| std_textureSampleCompare.ts | 28.99 kB ( |
| std_textureSampleCompareLevel.ts | 28.99 kB ( |
| std_textureSampleGrad.ts | 28.99 kB ( |
| std_textureSampleLevel.ts | 28.99 kB ( |
| std_textureStore.ts | 28.99 kB ( |
| STATIC_allImports.ts | 305.05 kB ( |
| STATIC_tgpu.ts | 275.56 kB ( |
| tgpu_fn.ts | 275.56 kB ( |
| tgpu_init.ts | 275.56 kB ( |
| tgpu_lazy.ts | 275.56 kB ( |
| tgpu_slot.ts | 275.56 kB ( |
| tgpu_const.ts | 275.56 kB ( |
| tgpu_unroll.ts | 275.57 kB ( |
| tgpu_resolve.ts | 275.57 kB ( |
| tgpu_accessor.ts | 275.57 kB ( |
| tgpu_comptime.ts | 275.57 kB ( |
| tgpu_vertexFn.ts | 275.57 kB ( |
| tgpu_computeFn.ts | 275.57 kB ( |
| tgpu_fragmentFn.ts | 275.57 kB ( |
| tgpu_privateVar.ts | 275.57 kB ( |
| tgpu_vertexLayout.ts | 275.57 kB ( |
| tgpu_workgroupVar.ts | 275.57 kB ( |
| tgpu_initFromDevice.ts | 275.57 kB ( |
| tgpu_bindGroupLayout.ts | 275.57 kB ( |
| tgpu_mutableAccessor.ts | 275.57 kB ( |
| tgpu_resolveWithContext.ts | 275.58 kB ( |
| std_atomicCompareExchangeWeak.ts | 20.85 kB |
| std_atomicExchange.ts | 20.85 kB |
| std_pack2x16snorm.ts | 40.86 kB |
| std_pack2x16unorm.ts | 40.86 kB |
| std_pack4x8snorm.ts | 40.86 kB |
| std_pack4xI8.ts | 40.86 kB |
| std_pack4xI8Clamp.ts | 40.86 kB |
| std_pack4xU8.ts | 40.86 kB |
| std_pack4xU8Clamp.ts | 40.86 kB |
| std_quadBroadcast.ts | 16.56 kB |
| std_quadSwapDiagonal.ts | 16.57 kB |
| std_quadSwapX.ts | 16.57 kB |
| std_quadSwapY.ts | 16.57 kB |
| std_textureGatherCompare.ts | 28.99 kB |
| std_textureNumLayers.ts | 28.99 kB |
| std_textureNumLevels.ts | 28.99 kB |
| std_textureNumSamples.ts | 28.99 kB |
| std_unpack2x16snorm.ts | 40.86 kB |
| std_unpack2x16unorm.ts | 40.86 kB |
| std_unpack4x8snorm.ts | 40.86 kB |
| std_unpack4xI8.ts | 40.86 kB |
| std_unpack4xU8.ts | 40.86 kB |
| std_workgroupUniformLoad.ts | 20.85 kB |
import { ... } in PR vs import * as ... in PR (is the library tree-Shakeable?):
| Test | tsdown |
|---|---|
| tgpu_init.ts | 266.03 kB ( |
| tgpu_initFromDevice.ts | 265.49 kB ( |
| tgpu_resolve.ts | 161.75 kB ( |
| tgpu_resolveWithContext.ts | 161.68 kB ( |
| tgpu_bindGroupLayout.ts | 62.32 kB ( |
| tgpu_mutableAccessor.ts | 57.04 kB ( |
| tgpu_accessor.ts | 57.04 kB ( |
| tgpu_privateVar.ts | 55.73 kB ( |
| tgpu_workgroupVar.ts | 55.73 kB ( |
| tgpu_const.ts | 55.15 kB ( |
| tgpu_lazy.ts | 54.95 kB ( |
| tgpu_fragmentFn.ts | 39.68 kB ( |
| tgpu_fn.ts | 39.62 kB ( |
| tgpu_vertexFn.ts | 39.50 kB ( |
| tgpu_computeFn.ts | 39.20 kB ( |
| tgpu_vertexLayout.ts | 28.33 kB ( |
| tgpu_comptime.ts | 15.93 kB ( |
| tgpu_unroll.ts | 1.75 kB ( |
| tgpu_slot.ts | 1.70 kB ( |
If you wish to run a comparison for other, slower bundlers, run the 'Tree-shake test' from the GitHub Actions menu.
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.88, 1.77, 4.04, 5.27, 6.33, 11.13, 21.27, 21.82]
line [0.85, 1.79, 3.75, 5.59, 6.72, 11.76, 20.51, 21.20]
line [0.82, 1.65, 3.26, 5.94, 6.33, 9.31, 19.55, 22.05]
---
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.32, 0.51, 0.64, 0.72, 1.02, 1.06, 1.26, 1.45]
line [0.26, 0.47, 0.61, 0.76, 1.18, 1.04, 1.27, 1.42]
line [0.31, 0.46, 0.56, 0.71, 1.00, 1.02, 1.16, 1.34]
---
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.08, 3.49, 6.23, 10.92, 23.36, 49.02, 100.29]
line [0.78, 1.84, 3.16, 6.58, 10.52, 23.09, 48.96, 99.39]
line [0.82, 1.83, 2.98, 5.52, 10.46, 23.09, 48.13, 96.34]
|
There was a problem hiding this comment.
Pull request overview
This PR expands TypeGPU’s std surface area by adding several missing WGSL builtins (texture queries, gather-compare, quad ops, additional packing/unpacking, and atomic helpers) and introduces snapshot/unit tests to validate codegen and typing.
Changes:
- Add texture query builtins (
textureNumLayers/Levels/Samples) andtextureGatherCompare, with coverage tests. - Add quad subgroup builtins (
quadBroadcast,quadSwapDiagonal/X/Y) and atomic builtins (atomicExchange,atomicCompareExchangeWeak,workgroupUniformLoad) with tests. - Extend packing std with additional WGSL pack/unpack builtins and broaden packing test coverage.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/typegpu/tests/std/texture/textureQueries.test.ts | Adds snapshot/type tests for texture query and gather-compare builtins. |
| packages/typegpu/tests/std/quad.test.ts | Adds snapshot/type tests for quad subgroup builtins (requires subgroups). |
| packages/typegpu/tests/std/packing.test.ts | Adds tests for new pack/unpack functions and rounding/clamping expectations. |
| packages/typegpu/tests/std/atomic.test.ts | Adds snapshot/type tests for new atomic and workgroupUniformLoad builtins. |
| packages/typegpu/src/std/texture.ts | Implements missing texture query builtins and textureGatherCompare codegen/signatures. |
| packages/typegpu/src/std/quad.ts | Implements quad subgroup builtins via dualImpl. |
| packages/typegpu/src/std/packing.ts | Refactors packing internals and adds multiple missing WGSL pack/unpack builtins. |
| packages/typegpu/src/std/index.ts | Re-exports the newly added std functions. |
| packages/typegpu/src/std/atomic.ts | Adds atomicExchange, atomicCompareExchangeWeak, and workgroupUniformLoad. |
Suppressed comments (3)
packages/typegpu/src/std/packing.ts:89
pack2x16unormusesMath.floor(0.5 + x)rounding, which disagrees with the project’s WGSLround(ties-to-even) semantics (seecpuRoundinstd/numeric.ts). This can produce different packed results for values that land exactly on.5after scaling.
normalImpl: (e: v2f): number => {
const x = Math.floor(0.5 + 65535 * clampScalar(e.x, 0, 1));
const y = Math.floor(0.5 + 65535 * clampScalar(e.y, 0, 1));
return writePacked((writer) => {
writer.writeUint16(x);
packages/typegpu/src/std/packing.ts:231
pack4x8snormusesMath.floor(0.5 + x)rounding, which doesn’t implement WGSLround(ties-to-even) semantics used elsewhere in the codebase (cpuRoundinstd/numeric.ts). This is particularly visible for negative.5cases (e.g. -63.5), where the current logic rounds toward zero instead of to the even integer.
export const pack4x8snorm = dualImpl({
name: 'pack4x8snorm',
normalImpl: (e: v4f): number =>
writePacked((writer) => {
writer.writeInt8(Math.floor(0.5 + 127 * clampScalar(e.x, -1, 1)));
writer.writeInt8(Math.floor(0.5 + 127 * clampScalar(e.y, -1, 1)));
writer.writeInt8(Math.floor(0.5 + 127 * clampScalar(e.z, -1, 1)));
writer.writeInt8(Math.floor(0.5 + 127 * clampScalar(e.w, -1, 1)));
}),
packages/typegpu/src/std/packing.ts:212
pack4x8unormcurrently rounds withMath.floor(0.5 + x), which isn’t ties-to-even and can disagree with WGSLroundsemantics used elsewhere in the codebase (cpuRoundinstd/numeric.ts). This can yield different results for values that scale toN + 0.5whereNis even/odd (e.g. 2.5).
export const pack4x8unorm = dualImpl({
name: 'pack4x8unorm',
normalImpl: (e: v4f): number =>
writePacked((writer) => {
writer.writeUint8(Math.floor(0.5 + 255 * clampScalar(e.x, 0, 1)));
writer.writeUint8(Math.floor(0.5 + 255 * clampScalar(e.y, 0, 1)));
writer.writeUint8(Math.floor(0.5 + 255 * clampScalar(e.z, 0, 1)));
writer.writeUint8(Math.floor(0.5 + 255 * clampScalar(e.w, 0, 1)));
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| normalImpl: (e: v2f): number => { | ||
| const buffer = new ArrayBuffer(4); | ||
| const writer = new TB.BufferWriter(buffer); | ||
| writeFloat16(writer, e.x); | ||
| writeFloat16(writer, e.y); | ||
| const reader = new TB.BufferReader(buffer); | ||
| return u32(reader.readUint32()); | ||
| const x = Math.floor(0.5 + 32767 * clampScalar(e.x, -1, 1)); | ||
| const y = Math.floor(0.5 + 32767 * clampScalar(e.y, -1, 1)); | ||
| return writePacked((writer) => { | ||
| writer.writeInt16(x); |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Atomics (
std/atomic.ts) — addsworkgroupUniformLoad,atomicExchange, andatomicCompareExchangeWeak(returning anabstructresult withold_value/exchanged), all consistent with the existingatomicOpSignature/isAtomic/atomicNormalErrorhelpers. - Packing (
std/packing.ts) — replaces per-callArrayBufferchurn with sharedpackedBuffer/packedWriter/packedReaderhelpers (explicitlittleendianness) and addspack/unpack2x16(snorm|unorm),pack/unpack4x8(snorm|unorm),pack/unpack4xI8,pack/unpack4xU8, and the twoClampvariants. - Quad (
std/quad.ts, new) —quadBroadcast(2nd arg validated asi32/u32viaunify),quadSwapDiagonal,quadSwapX,quadSwapY. - Texture (
std/texture.ts) —textureNumLayers/textureNumLevels/textureNumSampleswith typed texture-category unions, andtextureGatherCompare(array layer index coerced to[u32, i32], mirroring the existingtextureGathersignature). - Exports (
std/index.ts) — all new builtins exported. - Tests — inline WGSL snapshots plus
if (false)expectTypeOfassertions for atomics, quad, packing, and texture queries; all 9 pass.
I hand-verified the pack arithmetic (little-endian byte order, snorm -1 → -max symmetric mapping via coefficient 32767/127, clamp semantics, and writeInt8/writeUint8 wraparound for the non-clamp variants) against the WGSL spec and the expected values in the tests — all consistent, and the GPU codegenImpl emits the corresponding WGSL builtins.
ℹ️ Piggy-back notes for the author (nothing blocking)
- Forcing
littleendianness in the shared helpers is actually a correctness fix: the pre-existingpack2x16float/unpack*usednew TB.BufferWriter(buffer)with the system default endianness, which diverges from WGSL's little-endian packing on a big-endian host. No-op on the (effectively universal) little-endian case. - The CPU
pack*rounding usesMath.round/floor(0.5 + x)(round-half-up), which can differ from WGSL's round-to-nearest-ties-to-even at exact.5boundary values — so CPU and GPU results can disagree on that narrow input set. This matches an existing repo-wide known convention (jsPolyfills.tsalready notes the JS/WGSLrounddivergence), so it's fine to leave as-is.
ℹ️ Nitpicks
std/quad.tsredeclaresIdentityNumOrVec, which already exists instd/subgroup.ts— harmless duplication, but worth hoisting to a shared location if more quad-mirroring subgroup APIs land later.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏


No description provided.