Skip to content

impr: Clean up vectorOps - #2848

Open
aleksanderkatan wants to merge 41 commits into
mainfrom
impr/clean-up-vectorops
Open

impr: Clean up vectorOps#2848
aleksanderkatan wants to merge 41 commits into
mainfrom
impr/clean-up-vectorops

Conversation

@aleksanderkatan

Copy link
Copy Markdown
Member

No description provided.

@aleksanderkatan aleksanderkatan linked an issue Aug 13, 2026 that may be closed by this pull request
@github-actions

github-actions Bot commented Aug 13, 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.97, 1.88, 4.24, 7.00, 7.46, 12.57, 23.64, 23.44]
  line [0.99, 1.89, 4.61, 6.57, 7.27, 10.57, 23.44, 23.40]
  line [0.96, 1.94, 3.88, 6.03, 7.42, 12.95, 23.88, 25.90]
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.28, 0.49, 0.68, 0.77, 1.05, 1.11, 1.34, 1.52]
  line [0.29, 0.51, 0.70, 0.82, 1.08, 1.16, 1.37, 1.55]
  line [0.34, 0.54, 0.69, 0.81, 1.11, 1.19, 1.43, 1.54]
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.87, 2.24, 3.82, 7.45, 12.34, 26.53, 55.40, 111.60]
  line [0.86, 2.22, 4.00, 7.09, 13.09, 26.79, 53.88, 110.27]
  line [0.79, 2.20, 4.29, 7.34, 12.83, 26.92, 55.66, 111.11]
Loading

@github-actions

github-actions Bot commented Aug 13, 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@cf3e123016ac958d0c7bcab1f634686fcb77d5f8

benchmark
view benchmark

commit
view commit

@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown

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

🟢 Decreased (max -29.83%) ➖ Unchanged 🔴 Increased ❔ Unknown
115 209 0 0

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

Click to reveal the results table (115 entries).
Test tsdown
STATIC_allImports.ts 296.17 kB ($${\color{green}-4.4\%}$$)
tgpu_resolveWithContext.ts 270.50 kB ($${\color{green}-4.8\%}$$)
tgpu_bindGroupLayout.ts 270.50 kB ($${\color{green}-4.8\%}$$)
tgpu_mutableAccessor.ts 270.50 kB ($${\color{green}-4.8\%}$$)
tgpu_initFromDevice.ts 270.50 kB ($${\color{green}-4.8\%}$$)
tgpu_vertexLayout.ts 270.50 kB ($${\color{green}-4.8\%}$$)
tgpu_workgroupVar.ts 270.50 kB ($${\color{green}-4.8\%}$$)
tgpu_fragmentFn.ts 270.50 kB ($${\color{green}-4.8\%}$$)
tgpu_privateVar.ts 270.50 kB ($${\color{green}-4.8\%}$$)
tgpu_computeFn.ts 270.50 kB ($${\color{green}-4.8\%}$$)
tgpu_accessor.ts 270.49 kB ($${\color{green}-4.8\%}$$)
tgpu_comptime.ts 270.49 kB ($${\color{green}-4.8\%}$$)
tgpu_vertexFn.ts 270.49 kB ($${\color{green}-4.8\%}$$)
tgpu_resolve.ts 270.49 kB ($${\color{green}-4.8\%}$$)
tgpu_unroll.ts 270.49 kB ($${\color{green}-4.8\%}$$)
tgpu_const.ts 270.49 kB ($${\color{green}-4.8\%}$$)
tgpu_init.ts 270.49 kB ($${\color{green}-4.8\%}$$)
tgpu_lazy.ts 270.49 kB ($${\color{green}-4.8\%}$$)
tgpu_slot.ts 270.49 kB ($${\color{green}-4.8\%}$$)
tgpu_fn.ts 270.49 kB ($${\color{green}-4.8\%}$$)
STATIC_tgpu.ts 270.49 kB ($${\color{green}-4.8\%}$$)
STATIC_std.ts 96.96 kB ($${\color{green}-12.2\%}$$)
STATIC_d.ts 77.36 kB ($${\color{green}-13.6\%}$$)
std_abs.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_acos.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_acosh.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_asin.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_asinh.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_atan.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_atan2.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_atanh.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_ceil.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_clamp.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_cos.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_cosh.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_countLeadingZeros.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_countOneBits.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_countTrailingZeros.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_cross.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_degrees.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_determinant.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_dot4I8Packed.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_exp.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_exp2.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_extractBits.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_faceForward.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_firstLeadingBit.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_firstTrailingBit.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_floor.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_fma.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_insertBits.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_inverseSqrt.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_ldexp.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_log.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_log2.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_max.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_min.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_normalize.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_pow.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_quantizeToF16.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_radians.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_reflect.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_refract.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_reverseBits.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_round.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_saturate.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_sign.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_sin.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_sinh.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_smoothstep.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_sqrt.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_step.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_tan.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_tanh.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_transpose.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_trunc.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_distance.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_dot4U8Packed.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_fract.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_frexp.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_mix.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_modf.ts 55.86 kB ($${\color{green}-19.4\%}$$)
std_dot.ts 55.85 kB ($${\color{green}-19.4\%}$$)
std_length.ts 55.85 kB ($${\color{green}-19.4\%}$$)
std_rotateY4.ts 38.98 kB ($${\color{green}-23.7\%}$$)
std_rotateZ4.ts 38.98 kB ($${\color{green}-23.7\%}$$)
std_rotateX4.ts 38.97 kB ($${\color{green}-23.7\%}$$)
std_scale4.ts 38.97 kB ($${\color{green}-23.7\%}$$)
std_translate4.ts 38.97 kB ($${\color{green}-23.7\%}$$)
std_add.ts 38.14 kB ($${\color{green}-24.1\%}$$)
std_bitShiftLeft.ts 38.14 kB ($${\color{green}-24.1\%}$$)
std_bitShiftRight.ts 38.14 kB ($${\color{green}-24.1\%}$$)
std_div.ts 38.14 kB ($${\color{green}-24.1\%}$$)
std_mod.ts 38.14 kB ($${\color{green}-24.1\%}$$)
std_mul.ts 38.14 kB ($${\color{green}-24.1\%}$$)
std_sub.ts 38.14 kB ($${\color{green}-24.1\%}$$)
std_neg.ts 38.14 kB ($${\color{green}-24.1\%}$$)
std_bitcast.ts 35.90 kB ($${\color{green}-25.9\%}$$)
std_bitcastU32toF32.ts 35.89 kB ($${\color{green}-25.9\%}$$)
std_bitcastU32toI32.ts 35.89 kB ($${\color{green}-25.9\%}$$)
std_bitcastF32toU32.ts 35.89 kB ($${\color{green}-25.9\%}$$)
std_ge.ts 37.84 kB ($${\color{green}-29.8\%}$$)
std_gt.ts 37.84 kB ($${\color{green}-29.8\%}$$)
std_isCloseTo.ts 37.84 kB ($${\color{green}-29.8\%}$$)
std_le.ts 37.84 kB ($${\color{green}-29.8\%}$$)
std_allEq.ts 37.84 kB ($${\color{green}-29.8\%}$$)
std_eq.ts 37.84 kB ($${\color{green}-29.8\%}$$)
std_lt.ts 37.84 kB ($${\color{green}-29.8\%}$$)
std_ne.ts 37.84 kB ($${\color{green}-29.8\%}$$)
std_not.ts 37.84 kB ($${\color{green}-29.8\%}$$)
std_select.ts 37.84 kB ($${\color{green}-29.8\%}$$)
std_and.ts 37.83 kB ($${\color{green}-29.8\%}$$)
std_or.ts 37.83 kB ($${\color{green}-29.8\%}$$)
std_all.ts 37.83 kB ($${\color{green}-29.8\%}$$)
std_any.ts 37.84 kB ($${\color{green}-29.8\%}$$)

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

Test tsdown
tgpu_init.ts 260.96 kB ($${\color{green}-3.5\%}$$)
tgpu_initFromDevice.ts 260.42 kB ($${\color{green}-3.7\%}$$)
tgpu_resolve.ts 161.30 kB ($${\color{green}-40.4\%}$$)
tgpu_resolveWithContext.ts 161.23 kB ($${\color{green}-40.4\%}$$)
tgpu_bindGroupLayout.ts 62.30 kB ($${\color{green}-77.0\%}$$)
tgpu_mutableAccessor.ts 57.02 kB ($${\color{green}-78.9\%}$$)
tgpu_accessor.ts 57.02 kB ($${\color{green}-78.9\%}$$)
tgpu_privateVar.ts 55.71 kB ($${\color{green}-79.4\%}$$)
tgpu_workgroupVar.ts 55.71 kB ($${\color{green}-79.4\%}$$)
tgpu_const.ts 55.13 kB ($${\color{green}-79.6\%}$$)
tgpu_lazy.ts 54.93 kB ($${\color{green}-79.7\%}$$)
tgpu_fragmentFn.ts 39.65 kB ($${\color{green}-85.3\%}$$)
tgpu_fn.ts 39.60 kB ($${\color{green}-85.4\%}$$)
tgpu_vertexFn.ts 39.47 kB ($${\color{green}-85.4\%}$$)
tgpu_computeFn.ts 39.17 kB ($${\color{green}-85.5\%}$$)
tgpu_vertexLayout.ts 28.30 kB ($${\color{green}-89.5\%}$$)
tgpu_comptime.ts 15.91 kB ($${\color{green}-94.1\%}$$)
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.

@aleksanderkatan aleksanderkatan changed the title Impr/clean up vectorops impr: Clean up vectorOps Aug 13, 2026
}
throw new Error('Div called with invalid arguments.');
verifyKind([lhs, rhs], numericKind);
return generalizeFn((a, b) => a / b, upCast([lhs, rhs]));

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Previously integer vectors used a separate function for integer division. This works because vector constructors cast their elements.

@aleksanderkatan

Copy link
Copy Markdown
Member Author

@pullfrog review

@pullfrog

pullfrog Bot commented Aug 14, 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) | 𝕏

@aleksanderkatan
aleksanderkatan marked this pull request as ready for review August 14, 2026 12:46
Copilot AI lite review requested due to automatic review settings August 14, 2026 12:46

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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

Important

This cleanup introduces two CPU regressions in the refactored paths: the deprecated vector bitcasts now return the wrong type and corrupted values, and isCloseTo crashes on mixed scalar/vector operands where the codegen path returns false. Both are pinned inline with confirmation from running the CPU implementations directly.

Reviewed changes — large cleanup that removes most hand-written per-kind overloads in data/vectorOps.ts in favor of a new data/generalizeFn.ts helper (generalizeFn / generalizeBoolFn / upCast), which applies an n-ary scalar function component-wise over vectors/matrices and rebuilds the result via a kind→constructor map:

  • New generalizeFn.ts with the applyArgs engine, kind/constructor maps, upCast, and the overloaded generalizeFn/generalizeBoolFn exports.
  • std/numeric.ts cpu* helpers (abs, acos, atan2, clamp, exp2, fract, log, max, min, mix, pow, sign, smoothstep, sqrt, trig, …) rewritten through generalizeFn.
  • std/operators.ts add/mul/div/mod/neg rewritten through generalizeFn (div/mod via upCast).
  • std/boolean.ts eq/lt/or/isCloseTo/select rewritten via generalizeBoolFn/generalizeFn.
  • Deprecated bitcastU32toF32/bitcastU32toI32/bitcastF32toU32 rewritten via generalizeFn.
  • VectorOps reduced to the non-generalizable set (all, length, dot, cross, matrix multiply, bit shifts); clamp renamed to clampScalar in numberOps.ts; tests updated for div-by-zero and distance.

ℹ️ Behavior change worth confirming

std.div (and std.mod) on integer vectors previously returned the operand on division by zero (divInteger returned lhs when rhs === 0); the generalized version now throws a Finite-Math-Assumption error via the u32/i32 cast. The rewritten test (forbids division by 0) intentionally encodes this, so it appears deliberate, but it is a silent behavioral change to the public CPU path. Worth confirming nothing downstream relies on the old lenient result.

ℹ️ Nitpicks

  • data/numberOps.ts still exports divInteger, which is now referenced nowhere after this refactor — it should be removed with the other deleted helpers (bitcast* impls were removed in the same file).

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

Comment thread packages/typegpu/src/std/bitcast.ts Outdated
Comment thread packages/typegpu/src/std/boolean.ts

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

Important

The isCloseTo crash fix (upcasting via upCast) resolves the CPU crash from the prior review but leaves a CPU/GPU divergence on mixed scalar/vector operands that the new test now locks in: the CPU computes a real component-wise result while the GPU codegen still returns the literal false.

Reviewed changes — incremental delta since the prior pullfrog review (9b5e1ebcf3e123):

  • Reverted the deprecated bitcastU32toF32 / bitcastU32toI32 / bitcastF32toU32 back to their original VectorOps.* per-kind CPU impls, fixing the wrong-type/corrupted-value regression.
  • Removed the now-unreferenced divInteger helper from data/numberOps.ts (also addresses the prior nitpick).
  • Fixed the isCloseTo mixed scalar/vector CPU crash by upcasting operands via upCast before generalizeBoolFn, and expanded tests/std/boolean/isCloseTo.test.ts with close/distant/precision/mixed cases.

⚠️ CPU/GPU divergence on mixed isCloseTo

The new works for mixed types test asserts CPU results for mixed scalar/vector operands that a compiled shader cannot reproduce — the GPU codegen returns false for every mixed case while the CPU now computes the real comparison. Details inline.

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

expect(isCloseTo(vec2h(0, 0), vec2h(0, 9), 10)).toBe(true);
expect(isCloseTo(vec2h(0, 0), vec2h(0, 11), 10)).toBe(false);
it('works for mixed types', () => {
expect(isCloseTo(d.vec2f(0, 0) as d.v2f | number, 0)).toBe(true);

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 works for mixed types block asserts CPU results for mixed scalar/vector operands — isCloseTo(d.vec2f(0, 0), 0)true — but the GPU codegen in std/boolean.ts still returns the literal false for mixed operands (the else branch when exactly one side is a scalar). A compiled shader evaluating this therefore yields false where the CPU returns true: a silent CPU/GPU divergence that the crash-fix did not resolve.

Technical details
# Mixed scalar/vector isCloseTo diverges CPU vs GPU

## Affected sites
- packages/typegpu/tests/std/boolean/isCloseTo.test.ts:59-62 — new test asserts CPU-side mixed results (e.g. `isCloseTo(d.vec2f(0,0), 0)` -> `true`)
- packages/typegpu/src/std/boolean.ts:338-348 — codegen `else` branch returns literal `'false'` whenever exactly one operand is a scalar snippet

## Required outcome
CPU and GPU must agree on mixed scalar/vector `isCloseTo`.

## Suggested approach
Either (a) make the GPU codegen broadcast the scalar to a matching vec and emit the real comparison (WGSL mixed scalar/vector arithmetic is valid — the codegen's own comment cites the spec), or (b) scope mixed operands out and have the CPU path (and this test) return `false` to match the codegen. Option (a) matches the upcast semantics now implemented on the CPU.

## Open questions for the human
Is mixed scalar/vector `isCloseTo` an intended, supported semantic (fix the codegen), or out of scope (the CPU should match the GPU's `false`)?

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.

impr: Clean up VectorOps

2 participants