Skip to content

feat: Add radix to sort package and improve existing algos - #2756

Open
reczkok wants to merge 15 commits into
mainfrom
feat/radix-sort-v2
Open

feat: Add radix to sort package and improve existing algos#2756
reczkok wants to merge 15 commits into
mainfrom
feat/radix-sort-v2

Conversation

@reczkok

@reczkok reczkok commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@github-actions

github-actions Bot commented Jul 25, 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/eslint-plugin-typegpu@cd073500609dc8ae43a6dcf8917ff5700f043d5e
https://pkg.pr.new/software-mansion/TypeGPU/tgpu-gen@cd073500609dc8ae43a6dcf8917ff5700f043d5e
https://pkg.pr.new/software-mansion/TypeGPU/tinyest-for-wgsl@cd073500609dc8ae43a6dcf8917ff5700f043d5e
https://pkg.pr.new/software-mansion/TypeGPU/typegpu@cd073500609dc8ae43a6dcf8917ff5700f043d5e
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/cli@cd073500609dc8ae43a6dcf8917ff5700f043d5e
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/color@cd073500609dc8ae43a6dcf8917ff5700f043d5e
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/gl@cd073500609dc8ae43a6dcf8917ff5700f043d5e
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/noise@cd073500609dc8ae43a6dcf8917ff5700f043d5e
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/radiance-cascades@cd073500609dc8ae43a6dcf8917ff5700f043d5e
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/react@cd073500609dc8ae43a6dcf8917ff5700f043d5e
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/sdf@cd073500609dc8ae43a6dcf8917ff5700f043d5e
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/three@cd073500609dc8ae43a6dcf8917ff5700f043d5e
https://pkg.pr.new/software-mansion/TypeGPU/unplugin-typegpu@cd073500609dc8ae43a6dcf8917ff5700f043d5e

benchmark
view benchmark

commit
view commit

@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown

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

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

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

Test tsdown
tgpu_init.ts 267.87 kB ($${\color{green}-3.3\%}$$)
tgpu_initFromDevice.ts 267.34 kB ($${\color{green}-3.5\%}$$)
tgpu_resolve.ts 168.44 kB ($${\color{green}-39.2\%}$$)
tgpu_resolveWithContext.ts 168.38 kB ($${\color{green}-39.2\%}$$)
tgpu_bindGroupLayout.ts 73.79 kB ($${\color{green}-73.4\%}$$)
tgpu_mutableAccessor.ts 68.51 kB ($${\color{green}-75.3\%}$$)
tgpu_accessor.ts 68.51 kB ($${\color{green}-75.3\%}$$)
tgpu_privateVar.ts 67.20 kB ($${\color{green}-75.7\%}$$)
tgpu_workgroupVar.ts 67.20 kB ($${\color{green}-75.7\%}$$)
tgpu_const.ts 66.62 kB ($${\color{green}-75.9\%}$$)
tgpu_lazy.ts 66.42 kB ($${\color{green}-76.0\%}$$)
tgpu_fragmentFn.ts 38.92 kB ($${\color{green}-85.9\%}$$)
tgpu_fn.ts 38.86 kB ($${\color{green}-86.0\%}$$)
tgpu_vertexFn.ts 38.73 kB ($${\color{green}-86.0\%}$$)
tgpu_computeFn.ts 38.44 kB ($${\color{green}-86.1\%}$$)
tgpu_vertexLayout.ts 27.57 kB ($${\color{green}-90.0\%}$$)
tgpu_comptime.ts 15.17 kB ($${\color{green}-94.5\%}$$)
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.

@github-actions

github-actions Bot commented Jul 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.91, 1.78, 3.95, 6.02, 6.87, 11.50, 20.74, 24.37]
  line [0.95, 1.90, 3.96, 6.13, 7.11, 10.61, 21.72, 26.10]
  line [0.96, 1.84, 4.34, 6.69, 7.32, 11.34, 21.69, 24.56]
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.29, 0.47, 0.66, 0.78, 1.06, 1.13, 1.33, 1.48]
  line [0.30, 0.50, 0.66, 0.81, 1.28, 1.20, 1.33, 1.50]
  line [0.34, 0.54, 0.63, 0.89, 1.10, 1.16, 1.43, 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.85, 2.14, 4.18, 6.99, 12.70, 24.87, 53.02, 106.38]
  line [0.82, 1.90, 3.41, 6.64, 12.62, 25.59, 52.27, 106.72]
  line [0.95, 2.07, 4.58, 6.66, 13.19, 27.70, 55.10, 112.12]
Loading

@reczkok
reczkok force-pushed the feat/radix-sort-v2 branch from d6b4ea3 to 2adb4ae Compare August 6, 2026 15:34
Comment thread packages/typegpu-sort/src/bitonic/bitonicSort.ts
Comment thread packages/typegpu-sort/src/radix/schemas.ts Outdated

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Missing correctness tests for the sort itself

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Well no good way to run the shaders without introducing a lot of testing scaffolding, the sort example is a correctness test

@reczkok
reczkok force-pushed the feat/radix-sort-v2 branch from 2adb4ae to dad0228 Compare August 10, 2026 23:21
@reczkok
reczkok marked this pull request as ready for review August 11, 2026 00:17
Copilot AI lite review requested due to automatic review settings August 11, 2026 00:17

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

Adds a new @typegpu/sort radix sorter and refactors the existing scan/bitonic implementations to better reuse GPU resources (pipelines/bind groups/scratch buffers) and to compose with externally managed command encoders / compute passes.

Changes:

  • Introduces stable LSD radix sort (with optional payload reordering) plus test coverage.
  • Refactors prefix scan into reusable cached “plans” and shared dispatch/run-pass utilities.
  • Updates docs/examples/tests to reflect the new APIs and expanded algorithm set.

Reviewed changes

Copilot reviewed 41 out of 45 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
pnpm-lock.yaml Adds typegpu-testing-utility workspace dependency for the repo toolchain.
packages/typegpu/tests/accessor.test.ts Adds regression test for accessor $ access during simulation mode.
packages/typegpu-testing-utility/src/extendedIt.ts Extends mocked device limits (workgroup storage size) to cover new kernels.
packages/typegpu-sort/vitest.config.mts Adds Vitest config for the new typegpu-sort package tests.
packages/typegpu-sort/tests/utils.ts Adds test helpers for extracting generated WGSL and conversion warnings.
packages/typegpu-sort/tests/scan.test.ts Adds scan tests for warnings, schemas, caching behavior, and validation errors.
packages/typegpu-sort/tests/radix.test.ts Adds radix sort tests for warnings, digit extraction behavior, and caching.
packages/typegpu-sort/tests/bitonic.test.ts Adds/updates bitonic tests incl. payload behavior and “no allocations/writes” expectations.
packages/typegpu-sort/src/types.ts Introduces shared Sorter + RunOptions abstraction for composing into encoders/passes.
packages/typegpu-sort/src/scan/types.ts Extends scan BinaryOp typing to carry optional element type (d.u32/d.i32/d.f32).
packages/typegpu-sort/src/scan/schemas.ts Reworks scan schemas to be element-type aware; defines constants and per-type layouts/slots.
packages/typegpu-sort/src/scan/prefixScan.ts Major refactor: prepares reusable plans, caches per-buffer plans, adds reduce, and supports RunOptions.
packages/typegpu-sort/src/scan/kernels.ts New scan/apply-sums kernels using shared dispatch helpers and unrolled per-thread processing.
packages/typegpu-sort/src/scan/index.ts Updates scan exports: adds reduce, plan types, buffer types, and element-type exports.
packages/typegpu-sort/src/scan/compute/shared.ts Removes old scan shared compute helpers (replaced by kernels.ts).
packages/typegpu-sort/src/scan/compute/scan.ts Removes old scan kernel implementation (replaced by kernels.ts).
packages/typegpu-sort/src/scan/compute/applySums.ts Removes old apply-sums kernel implementation (replaced by kernels.ts).
packages/typegpu-sort/src/runPass.ts Adds shared helpers to run work either standalone or recorded into an existing encoder/pass.
packages/typegpu-sort/src/radix/types.ts Adds radix sorter options (direction + optional payload buffer).
packages/typegpu-sort/src/radix/schemas.ts Adds radix constants, layouts, workgroup memory, and digit extraction logic per key type/direction.
packages/typegpu-sort/src/radix/scatter.ts Adds radix scatter kernel (tile-based, uses workgroup bitsets + ranks).
packages/typegpu-sort/src/radix/radixSort.ts Implements radix sorter orchestration, resource ownership, and multi-pass execution.
packages/typegpu-sort/src/radix/index.ts Exposes radix sorter public API.
packages/typegpu-sort/src/radix/count.ts Adds radix histogram count kernel.
packages/typegpu-sort/src/index.ts Updates package exports to include radix and new scan APIs/types.
packages/typegpu-sort/src/dispatch.ts Adds shared dispatch decomposition + flat workgroup indexing helper for kernels.
packages/typegpu-sort/src/bitonic/utils.ts Removes old bitonic dispatch utilities (replaced by shared dispatch.ts).
packages/typegpu-sort/src/bitonic/types.ts Aligns bitonic types with shared Sorter/RunOptions and adds optional payload support.
packages/typegpu-sort/src/bitonic/slots.ts Refactors comparator/defaults; introduces per-key-type default padding values.
packages/typegpu-sort/src/bitonic/index.ts Simplifies bitonic exports and aligns with new public API surface.
packages/typegpu-sort/src/bitonic/bitonicSort.ts Major refactor: pre-creates resources, adds local/shared-memory kernels, and supports payload buffers.
packages/typegpu-sort/README.md Updates docs: adds radix sort section, updates scan API (reduce), and documents encoder/pass composition.
packages/typegpu-sort/package.json Adds typegpu-testing-utility dependency for tests/config.
apps/typegpu-docs/tests/individual-example-tests/sort.test.ts Adds/updates docs example test snapshots for the new consolidated sort example.
apps/typegpu-docs/tests/individual-example-tests/bitonic-sort.test.ts Removes old dedicated bitonic sort example test.
apps/typegpu-docs/src/examples/tests/prefix-scan/index.ts Updates example test to use reduce instead of scan.
apps/typegpu-docs/src/examples/algorithms/sort/meta.json Renames example from “Bitonic Sort” to “Sort”.
apps/typegpu-docs/src/examples/algorithms/sort/index.ts Updates example to include radix option + benchmarking and revised timing integration.
apps/typegpu-docs/src/examples/algorithms/sort/index.html Adds overlay/spinner UI for sort/benchmark actions.
apps/typegpu-docs/src/examples/algorithms/concurrent-chart/meta.json Removes concurrent-chart example metadata.
apps/typegpu-docs/src/examples/algorithms/concurrent-chart/index.ts Removes concurrent-chart example implementation.
apps/typegpu-docs/src/examples/algorithms/concurrent-chart/index.html Removes concurrent-chart example markup/styles.
apps/typegpu-docs/src/examples/algorithms/concurrent-chart/calculator.ts Removes concurrent-chart example compute/timing helper.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

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

Comment on lines +101 to +105
({ lid, wid, numWorkgroups }) => {
const workgroupId = flatWorkgroupIndex(wid, numWorkgroups);
const baseIdx = (workgroupId * WORKGROUP_SIZE + lid.x) * ELEMENTS_PER_THREAD;
const blockSum = applySumsLayout.$.sums[workgroupId];

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

(Note from @reczkok)

Mr. Frog pulled the alarm sirens but there is no cause for panic, the Radix sorter is tested via the sort example - we can add some automated testing via browser or deno in the future. Also you can't break an API that isn't public so yeah, false alarm

Important

The headline feature — the new radix sorter — plus the reworked bitonic sorter and prefix scan ship with no automated correctness test. Every new test asserts resolved-WGSL snapshots, the absence of conversion warnings, or GPU-resource reuse counts; none runs a sort/scan on real data and checks the output. A regression in the scatter rank/prefix logic, the digit transforms, or the scan's exclusive-prefix semantics would pass CI silently. Please add at least one data-correctness test (see the section below) before merging.

Reviewed changes

  • New stable LSD radix sorter (createRadixSorter) for u32/i32/f32 keys with optional payload reordering and ascending/descending directions, plus its count / scatter kernels and schema layer.
  • Bitonic sorter reworked: typed key support, a values payload path, block-local workgroup-shared kernels with a shared-memory-size-gated global fallback, and padding moved to per-type defaults.
  • Prefix scan rewritten around a reusable createPrefixScanComputer + prepare() plan API, integer element types, an encoder/pass composition surface, and a reduce helper replacing the old scan.
  • Shared dispatch.ts/runPass.ts infrastructure and new RunOptions/Sorter/PrefixScanPlan types.
  • Docs: sort example rewritten to showcase both sorters (with a benchmark harness), new sort.test.ts, and deletion of the old bitonic-sort.test.ts and concurrent-chart example.
  • New package tests, vitest.config.mts, and a typegpu-testing-utility mock-limit addition.

I traced the radix algorithm end-to-end (count → flat exclusive scan over the digit-major hist layout → bitset-ranked scatter) and it is correct, including the partial-final-tile bounds handling, i32/f32 digit transforms, stable within-tile placement, and descending direction.

⚠️ No data-correctness test for the radix sorter (and the rewritten bitonic/scan)

All tests in packages/typegpu-sort/tests/* and the docs sort.test.ts are codegen snapshots plus device-mock resource-count assertions. Nothing verifies that sorter.run() leaves the buffer actually sorted, or that the scan leaves the buffer actually exclusive-prefix-scanned. radix.test.ts in particular (the only genuinely new algorithm) covers digit functions and resource reuse but never checks a real ordering result — a bug in the scatter rank/prefix or an accidental switch of the flat scan from exclusive to inclusive would go completely unnoticed by CI.

Technical details
# Add a radix data-correctness test

## Affected sites
- packages/typegpu-sort/src/radix/{count,scatter,radixSort}.ts — the new algorithm, entirely untested on data
- packages/typegpu-sort/tests/radix.test.ts — today only WGSL snapshots and resource-reuse counts
- packages/typegpu-sort/tests/{bitonic,scan}.test.ts — same codegen-only pattern

## Required outcome
- A test that computes a reference result on CPU (e.g. `[...arr].sort(...)` for keys, and an index payload that must land exactly on its key's sorted position), runs the sorter, reads the GPU buffer back, and asserts equality — covering at least: u32 ascending and descending, a non-power-of-2 length (partial final tile + bounds checks), and a payload buffer reordered alongside the keys.
- Analogous check (even a single fixed small input) that `prefixScan` yields the exact exclusive prefix vs a JS reference, since the entire radix scheme depends on that flat scan being exclusive.

## Notes
- The mock `device` from `typegpu-testing-utility` is shader/codegen-oriented, so a real correctness test likely needs a run-and-compare harness (like the existing `apps/typegpu-docs/src/examples/tests/prefix-scan/`) or a test on a real device — worth aligning with however the repo validates numeric results elsewhere.

ℹ️ Breaking public-API changes to @typegpu/sort

This PR removes the scan export (renamed reduce) and drops compareSlot, defaultCompare, decomposeWorkgroups, and BitonicSorterRunOptions from the package's public index (compareSlot/defaultCompare are now internal to the bitonic module). Monorepo callers were updated and the now-obsolete examples deleted, so nothing internal breaks — but this is a breaking release for external consumers. Worth confirming the removal of compareSlot/defaultCompare from the public surface is intended (custom comparators still work via BitonicSorterOptions.compare).

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

}

export function bindPass(pipeline: TgpuComputePipeline, pass: RunPass): TgpuComputePipeline {
if ('resourceType' in pass) {

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.

Both branches of this if ('resourceType' in pass) are identical (return pipeline.with(pass)), so the guard is dead code and suggests two paths are handled differently when they aren't. Since TgpuComputePipeline.with() already accepts both TgpuComputePass and a raw GPUComputePassEncoder, this can collapse to a single return pipeline.with(pass);.

@reczkok
reczkok force-pushed the feat/radix-sort-v2 branch from dad0228 to cd07350 Compare August 11, 2026 12:47
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