Skip to content

test: Add real-GPU tests for std bit, packing, trunc and faceForward … - #3286

Open
mishablablabla wants to merge 2 commits into
mainfrom
test/gpu-tests-expansion
Open

mishablablabla wants to merge 2 commits into
mainfrom
test/gpu-tests-expansion

Conversation

@mishablablabla

Copy link
Copy Markdown
Collaborator

Summary

  • Add real-GPU tests for std builtins that have no CPU implementation (bit counting, extractBits/insertBits, dot4U8Packed, trunc, faceForward).
  • Add a test:gpu script and config to packages/typegpu.

Validation

  • 16/16 passed locally on Metal and on Lavapipe (Docker).
  • Mutation testing confirmed the tests catch broken builtins.

Copilot AI balanced review requested due to automatic review settings October 28, 2026 11:53
@github-actions

github-actions Bot commented Oct 28, 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/tgpu-gen@c1f3de05289ad280e9945b748b7a16bc6078cd9b
https://pkg.pr.new/software-mansion/TypeGPU/typegpu@c1f3de05289ad280e9945b748b7a16bc6078cd9b
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/cli@c1f3de05289ad280e9945b748b7a16bc6078cd9b
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/gl@c1f3de05289ad280e9945b748b7a16bc6078cd9b

benchmark
view benchmark

commit
view commit

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.

🟢 Approval recommended

No active review issues remain for the added GPU builtin tests and supporting test configuration.

No open issues 🎉

What changed in this PR

Adds real-GPU coverage for TypeGPU standard-library builtins without CPU implementations, using reference calculations to check device results.

Changes:

  • Adds 16 tests covering bit counting, bit extraction/insertion, packed dot products, trunc, and faceForward.
  • Introduces a standalone GPU test configuration, separate from unit tests.
  • Adds a package-level test:gpu command.
File Description
packages/​typegpu/​vitest.gpu.config.mts Configures GPU test discovery, TypeGPU transforms, aliases, and timeouts.
packages/​typegpu/​tests/​gpu/​numeric.gpu.ts Adds GPU execution helpers and numeric builtin tests with reference expectations.
packages/​typegpu/​package.json Adds the test:gpu script.

Review effort: Balanced

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

( ◦°^°◦) 🏴☠️ Ahoy! I read the whole diff end to end, ran the type-check, linter, formatter and test collection, and cross-checked every reference against the WGSL spec — all shipshape, so I gave her the green light.

Reviewed changes

  • Real-GPU suite for std numeric builtins — packages/typegpu/tests/gpu/numeric.gpu.ts adds 16 gpuIt tests: countLeadingZeros (u32/i32/vec3u), countTrailingZeros (u32/i32), firstLeadingBit (u32/i32), extractBits/insertBits (unsigned zero-extend and signed sign-extend/insert), dot4U8Packed, trunc (f32/vec3f) and faceForward (vec2f/vec3f). Each case compares GPU output against a reference transcribed from the WGSL spec, driven by the mapOnGpu helper (one guarded thread per element).
  • Standalone GPU vitest config — packages/typegpu/vitest.gpu.config.mts mirrors the @typegpu/algorithm config (tests/**/*.gpu.ts include, 60s timeouts, unplugin-typegpu plugin plus built aliases), keeping these files out of the default test:unit include.
  • test:gpu script — added to packages/typegpu/package.json, so root pnpm test:gpu (and the manual test-gpu.yml workflow) now runs this package too.

I verified tsc --noEmit on the package passes, vitest list collects all 16 tests, and oxfmt --check / oxlint --type-aware are clean. I also cross-checked every reference against WGSL §17.5 (firstLeadingBit unsigned 0xFFFFFFFF for zero and signed -1 for 0/-1; extractBits c == 0 → 0 with the right extension; insertBits c == 0 → e; dot4U8Packed; faceForward), and all match. The tests genuinely assert GPU output — the output buffer starts zeroed while the expected values are nonzero, so a no-op dispatch would fail.

ℹ️ These tests only run in the manual GPU workflow

The new suite is reachable through pnpm test:gpu, which currently runs only in the workflow_dispatch-only test-gpu.yml (Lavapipe), not in a required PR check — so it will not guard against std regressions in ordinary CI. That is the existing design for real-GPU tests, so no change is requested; noting it only so the coverage boundary is explicit.

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

@github-actions

Copy link
Copy Markdown

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

🟢 Decreased ➖ Unchanged 🔴 Increased ❔ Unknown
0 355 0 0

No notable changes.

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 28, 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 [1.01, 2.16, 4.37, 7.27, 8.62, 13.71, 26.02, 28.40]
  line [1.11, 2.26, 4.96, 6.81, 7.99, 12.29, 27.42, 27.42]
  line [1.02, 2.04, 4.59, 7.08, 9.35, 13.35, 28.35, 26.67]
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.32, 0.51, 0.70, 0.83, 1.12, 1.19, 1.49, 1.62]
  line [0.36, 0.59, 0.76, 0.87, 1.19, 1.30, 1.54, 1.72]
  line [0.33, 0.51, 0.69, 0.83, 1.11, 1.26, 1.50, 1.67]
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.45, 3.88, 7.67, 14.08, 28.20, 60.28, 122.02]
  line [0.83, 2.38, 3.92, 7.92, 13.61, 27.42, 60.16, 120.63]
  line [1.00, 2.39, 3.79, 7.71, 13.56, 29.21, 60.32, 122.23]
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 critical issues — one low-stakes note inline.

( ◦°^°◦) 🏴☠️ Ahoy! I re-scanned the new commits end to end, typechecked the package, collected the 60 GPU tests, and cross-checked every new reference against the WGSL spec — all sound, with one wee note on the frexp zero case.

Reviewed changes

Reviewed the delta since the prior Pullfrog review (d77dccd3), where this was a single-file suite of 16 tests.

  • Split the suite into focused files plus a shared helper — utils.ts now holds mapOnGpu / noNegativeZero / U32_MAX / I32_MIN; numeric.gpu.ts keeps the bit-count / extract / insert / packing / trunc / faceForward cases, and two new files group the added builtins.
  • Added bits.gpu.ts — countOneBits, firstTrailingBit, reverseBits (u32 / i32 / vec) and dot4I8Packed.
  • Added float.gpu.ts — determinant (mat2/3/4), refract, ldexp, frexp, modf, quantizeToF16.
  • Added dispatch.gpu.ts — asserts dispatchThreads runs every requested thread exactly once (and no extra) for 0D/1D/2D/3D sizes straddling the 256 / 16×16 / 8×8×4 workgroup sizes, plus a re-dispatch with a changing size.

I re-ran tsc --noEmit (passes) and vitest list (collects all 60 GPU tests) on the head commit, and an independent pass cross-checked every new reference against WGSL §17.5 — including firstTrailingBit/firstLeadingBit returning T(-1) for zero and the quantizeToF16 bracket being exactly the two spec-allowed neighbouring values.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

}),
);
// and it recombines to the input
result.forEach((r, i) => expect(r.fract * 2 ** r.exp).toBe(Math.fround(inputs[i] as number)));

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.

toBe uses Object.is, so a -0 fraction fails this check even though the assertion just above deliberately normalizes zero signs through noNegativeZero. WGSL does not pin the sign of frexp(0)'s fraction (nor its exponent), so a backend returning -0 would fail here while the other assertions pass — wrap the recombined value in noNegativeZero (already imported) to stay consistent.

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.

2 participants