@graphty/webgpu-graph-algorithms 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -52
- package/dist/browser.js +1 -1
- package/dist/chunks/{context-BR7fx3vR.js → context-BXqgCifx.js} +190 -40
- package/dist/chunks/context-BXqgCifx.js.map +1 -0
- package/dist/node.js +1 -1
- package/dist/src/algorithms/components.d.ts.map +1 -1
- package/dist/src/algorithms/components.js +12 -13
- package/dist/src/algorithms/components.js.map +1 -1
- package/dist/src/algorithms/degree.d.ts +6 -8
- package/dist/src/algorithms/degree.d.ts.map +1 -1
- package/dist/src/algorithms/degree.js +58 -35
- package/dist/src/algorithms/degree.js.map +1 -1
- package/dist/src/algorithms/pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/pagerank.js +16 -14
- package/dist/src/algorithms/pagerank.js.map +1 -1
- package/dist/src/algorithms/power-iteration.d.ts +2 -2
- package/dist/src/algorithms/power-iteration.d.ts.map +1 -1
- package/dist/src/algorithms/power-iteration.js +17 -14
- package/dist/src/algorithms/power-iteration.js.map +1 -1
- package/dist/src/constants.d.ts +38 -8
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +38 -8
- package/dist/src/constants.js.map +1 -1
- package/dist/src/errors.d.ts +3 -2
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +2 -1
- package/dist/src/errors.js.map +1 -1
- package/dist/src/index.d.ts +6 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +8 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/kernel/dispatch.d.ts +8 -3
- package/dist/src/kernel/dispatch.d.ts.map +1 -1
- package/dist/src/kernel/dispatch.js +18 -7
- package/dist/src/kernel/dispatch.js.map +1 -1
- package/dist/src/kernel/kernel.d.ts +30 -1
- package/dist/src/kernel/kernel.d.ts.map +1 -1
- package/dist/src/kernel/kernel.js +49 -5
- package/dist/src/kernel/kernel.js.map +1 -1
- package/dist/src/kernel/prelude.d.ts.map +1 -1
- package/dist/src/kernel/prelude.js +6 -1
- package/dist/src/kernel/prelude.js.map +1 -1
- package/dist/src/kernel/profiler.d.ts +15 -3
- package/dist/src/kernel/profiler.d.ts.map +1 -1
- package/dist/src/kernel/profiler.js +27 -4
- package/dist/src/kernel/profiler.js.map +1 -1
- package/dist/src/kernels.d.ts +17 -7
- package/dist/src/kernels.d.ts.map +1 -1
- package/dist/src/kernels.js +323 -16
- package/dist/src/kernels.js.map +1 -1
- package/dist/src/layouts/calibrate.d.ts +51 -0
- package/dist/src/layouts/calibrate.d.ts.map +1 -0
- package/dist/src/layouts/calibrate.js +172 -0
- package/dist/src/layouts/calibrate.js.map +1 -0
- package/dist/src/layouts/force-simulation.d.ts +39 -4
- package/dist/src/layouts/force-simulation.d.ts.map +1 -1
- package/dist/src/layouts/force-simulation.js +71 -19
- package/dist/src/layouts/force-simulation.js.map +1 -1
- package/dist/src/layouts/forceatlas2.d.ts +107 -36
- package/dist/src/layouts/forceatlas2.d.ts.map +1 -1
- package/dist/src/layouts/forceatlas2.js +296 -100
- package/dist/src/layouts/forceatlas2.js.map +1 -1
- package/dist/src/layouts/fruchterman-reingold.d.ts +73 -27
- package/dist/src/layouts/fruchterman-reingold.d.ts.map +1 -1
- package/dist/src/layouts/fruchterman-reingold.js +230 -70
- package/dist/src/layouts/fruchterman-reingold.js.map +1 -1
- package/dist/src/layouts/model-common.d.ts +41 -3
- package/dist/src/layouts/model-common.d.ts.map +1 -1
- package/dist/src/layouts/model-common.js +74 -3
- package/dist/src/layouts/model-common.js.map +1 -1
- package/dist/src/layouts/repulsion-grid.d.ts +152 -0
- package/dist/src/layouts/repulsion-grid.d.ts.map +1 -0
- package/dist/src/layouts/repulsion-grid.js +318 -0
- package/dist/src/layouts/repulsion-grid.js.map +1 -0
- package/dist/src/layouts/spring-electrical.d.ts +75 -30
- package/dist/src/layouts/spring-electrical.d.ts.map +1 -1
- package/dist/src/layouts/spring-electrical.js +231 -74
- package/dist/src/layouts/spring-electrical.js.map +1 -1
- package/dist/src/memory/residency.d.ts +6 -2
- package/dist/src/memory/residency.d.ts.map +1 -1
- package/dist/src/memory/residency.js +84 -14
- package/dist/src/memory/residency.js.map +1 -1
- package/dist/src/primitives/core-shape.d.ts +38 -2
- package/dist/src/primitives/core-shape.d.ts.map +1 -1
- package/dist/src/primitives/core-shape.js +71 -3
- package/dist/src/primitives/core-shape.js.map +1 -1
- package/dist/src/primitives/grid-pyramid.d.ts +71 -0
- package/dist/src/primitives/grid-pyramid.d.ts.map +1 -0
- package/dist/src/primitives/grid-pyramid.js +143 -0
- package/dist/src/primitives/grid-pyramid.js.map +1 -0
- package/dist/src/primitives/grid.d.ts +118 -0
- package/dist/src/primitives/grid.d.ts.map +1 -0
- package/dist/src/primitives/grid.js +225 -0
- package/dist/src/primitives/grid.js.map +1 -0
- package/dist/src/primitives/histogram.d.ts +67 -0
- package/dist/src/primitives/histogram.d.ts.map +1 -0
- package/dist/src/primitives/histogram.js +190 -0
- package/dist/src/primitives/histogram.js.map +1 -0
- package/dist/src/primitives/radix-sort.d.ts +75 -0
- package/dist/src/primitives/radix-sort.d.ts.map +1 -0
- package/dist/src/primitives/radix-sort.js +168 -0
- package/dist/src/primitives/radix-sort.js.map +1 -0
- package/dist/src/primitives/scan.d.ts +44 -0
- package/dist/src/primitives/scan.d.ts.map +1 -0
- package/dist/src/primitives/scan.js +151 -0
- package/dist/src/primitives/scan.js.map +1 -0
- package/dist/src/primitives/segmented-reduce.d.ts +25 -17
- package/dist/src/primitives/segmented-reduce.d.ts.map +1 -1
- package/dist/src/primitives/segmented-reduce.js +166 -47
- package/dist/src/primitives/segmented-reduce.js.map +1 -1
- package/dist/src/primitives/spmv.d.ts +18 -14
- package/dist/src/primitives/spmv.d.ts.map +1 -1
- package/dist/src/primitives/spmv.js +94 -58
- package/dist/src/primitives/spmv.js.map +1 -1
- package/dist/src/primitives/verify.d.ts +49 -0
- package/dist/src/primitives/verify.d.ts.map +1 -0
- package/dist/src/primitives/verify.js +229 -0
- package/dist/src/primitives/verify.js.map +1 -0
- package/dist/src/types/context.d.ts +53 -0
- package/dist/src/types/context.d.ts.map +1 -1
- package/dist/src/types/layout.d.ts +20 -0
- package/dist/src/types/layout.d.ts.map +1 -1
- package/dist/src/wgsl/counting-scatter.wgsl.d.ts +8 -0
- package/dist/src/wgsl/counting-scatter.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/counting-scatter.wgsl.js +17 -0
- package/dist/src/wgsl/counting-scatter.wgsl.js.map +1 -0
- package/dist/src/wgsl/fa2-attraction.wgsl.d.ts +23 -11
- package/dist/src/wgsl/fa2-attraction.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/fa2-attraction.wgsl.js +98 -20
- package/dist/src/wgsl/fa2-attraction.wgsl.js.map +1 -1
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts +6 -2
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.js +22 -1
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.js.map +1 -1
- package/dist/src/wgsl/grid-cell-key.wgsl.d.ts +8 -0
- package/dist/src/wgsl/grid-cell-key.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-cell-key.wgsl.js +30 -0
- package/dist/src/wgsl/grid-cell-key.wgsl.js.map +1 -0
- package/dist/src/wgsl/grid-centroid-hub.wgsl.d.ts +8 -0
- package/dist/src/wgsl/grid-centroid-hub.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-centroid-hub.wgsl.js +29 -0
- package/dist/src/wgsl/grid-centroid-hub.wgsl.js.map +1 -0
- package/dist/src/wgsl/grid-centroid.wgsl.d.ts +8 -0
- package/dist/src/wgsl/grid-centroid.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-centroid.wgsl.js +29 -0
- package/dist/src/wgsl/grid-centroid.wgsl.js.map +1 -0
- package/dist/src/wgsl/grid-downsample.wgsl.d.ts +7 -0
- package/dist/src/wgsl/grid-downsample.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-downsample.wgsl.js +28 -0
- package/dist/src/wgsl/grid-downsample.wgsl.js.map +1 -0
- package/dist/src/wgsl/grid-far-field.wgsl.d.ts +13 -0
- package/dist/src/wgsl/grid-far-field.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-far-field.wgsl.js +98 -0
- package/dist/src/wgsl/grid-far-field.wgsl.js.map +1 -0
- package/dist/src/wgsl/grid-near-field.wgsl.d.ts +19 -0
- package/dist/src/wgsl/grid-near-field.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-near-field.wgsl.js +129 -0
- package/dist/src/wgsl/grid-near-field.wgsl.js.map +1 -0
- package/dist/src/wgsl/histogram.wgsl.d.ts +7 -0
- package/dist/src/wgsl/histogram.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/histogram.wgsl.js +15 -0
- package/dist/src/wgsl/histogram.wgsl.js.map +1 -0
- package/dist/src/wgsl/indirect-finalize.wgsl.d.ts +8 -0
- package/dist/src/wgsl/indirect-finalize.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/indirect-finalize.wgsl.js +26 -0
- package/dist/src/wgsl/indirect-finalize.wgsl.js.map +1 -0
- package/dist/src/wgsl/radix-hist.wgsl.d.ts +9 -0
- package/dist/src/wgsl/radix-hist.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/radix-hist.wgsl.js +31 -0
- package/dist/src/wgsl/radix-hist.wgsl.js.map +1 -0
- package/dist/src/wgsl/radix-scatter.wgsl.d.ts +9 -0
- package/dist/src/wgsl/radix-scatter.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/radix-scatter.wgsl.js +40 -0
- package/dist/src/wgsl/radix-scatter.wgsl.js.map +1 -0
- package/dist/src/wgsl/scan-add.wgsl.d.ts +6 -0
- package/dist/src/wgsl/scan-add.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/scan-add.wgsl.js +14 -0
- package/dist/src/wgsl/scan-add.wgsl.js.map +1 -0
- package/dist/src/wgsl/scan-block.wgsl.d.ts +8 -0
- package/dist/src/wgsl/scan-block.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/scan-block.wgsl.js +30 -0
- package/dist/src/wgsl/scan-block.wgsl.js.map +1 -0
- package/dist/src/wgsl/segmented-reduce.wgsl.d.ts +22 -8
- package/dist/src/wgsl/segmented-reduce.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/segmented-reduce.wgsl.js +84 -15
- package/dist/src/wgsl/segmented-reduce.wgsl.js.map +1 -1
- package/dist/src/wgsl/spmv-pull.wgsl.d.ts +22 -11
- package/dist/src/wgsl/spmv-pull.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/spmv-pull.wgsl.js +110 -36
- package/dist/src/wgsl/spmv-pull.wgsl.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/webgpu-graph-algorithms.js +3815 -1003
- package/dist/webgpu-graph-algorithms.js.map +1 -1
- package/package.json +5 -4
- package/src/algorithms/components.ts +12 -16
- package/src/algorithms/degree.ts +58 -43
- package/src/algorithms/pagerank.ts +20 -18
- package/src/algorithms/power-iteration.ts +19 -18
- package/src/constants.ts +38 -8
- package/src/errors.ts +3 -1
- package/src/index.ts +14 -4
- package/src/kernel/dispatch.ts +18 -7
- package/src/kernel/kernel.ts +59 -5
- package/src/kernel/prelude.ts +9 -0
- package/src/kernel/profiler.ts +28 -4
- package/src/kernels.ts +356 -18
- package/src/layouts/calibrate.ts +187 -0
- package/src/layouts/force-simulation.ts +91 -23
- package/src/layouts/forceatlas2.ts +331 -106
- package/src/layouts/fruchterman-reingold.ts +255 -74
- package/src/layouts/model-common.ts +98 -3
- package/src/layouts/repulsion-grid.ts +451 -0
- package/src/layouts/spring-electrical.ts +257 -78
- package/src/memory/residency.ts +126 -20
- package/src/primitives/core-shape.ts +91 -4
- package/src/primitives/grid-pyramid.ts +221 -0
- package/src/primitives/grid.ts +349 -0
- package/src/primitives/histogram.ts +273 -0
- package/src/primitives/radix-sort.ts +246 -0
- package/src/primitives/scan.ts +197 -0
- package/src/primitives/segmented-reduce.ts +214 -56
- package/src/primitives/spmv.ts +125 -65
- package/src/primitives/verify.ts +249 -0
- package/src/types/context.ts +56 -0
- package/src/types/layout.ts +22 -0
- package/src/wgsl/counting-scatter.wgsl.ts +16 -0
- package/src/wgsl/fa2-attraction.wgsl.ts +98 -20
- package/src/wgsl/fa2-stats-finalize.wgsl.ts +22 -1
- package/src/wgsl/grid-cell-key.wgsl.ts +29 -0
- package/src/wgsl/grid-centroid-hub.wgsl.ts +28 -0
- package/src/wgsl/grid-centroid.wgsl.ts +28 -0
- package/src/wgsl/grid-downsample.wgsl.ts +27 -0
- package/src/wgsl/grid-far-field.wgsl.ts +97 -0
- package/src/wgsl/grid-near-field.wgsl.ts +128 -0
- package/src/wgsl/histogram.wgsl.ts +14 -0
- package/src/wgsl/indirect-finalize.wgsl.ts +25 -0
- package/src/wgsl/radix-hist.wgsl.ts +30 -0
- package/src/wgsl/radix-scatter.wgsl.ts +39 -0
- package/src/wgsl/scan-add.wgsl.ts +13 -0
- package/src/wgsl/scan-block.wgsl.ts +29 -0
- package/src/wgsl/segmented-reduce.wgsl.ts +84 -15
- package/src/wgsl/spmv-pull.wgsl.ts +110 -36
- package/dist/chunks/context-BR7fx3vR.js.map +0 -1
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The grid pyramid (spec 6 row 12, 7.7 G4-G5; P4-T9): the planner that records, into the caller's pass, the finest
|
|
3
|
+
* centroids (G4, `grid-centroid`: thread per cell over `cells + 1`, the pseudo-cell included), the hub-cell
|
|
4
|
+
* completion (G4a: the T1 `indirect-finalize` over `hubCounters[0]` into `hubArgs` with `wg = 1`, so the finalize's
|
|
5
|
+
* `ceil(count / wg)` is ONE workgroup per hub cell; G4b: `grid-centroid-hub`, one workgroup per hub cell, dispatched
|
|
6
|
+
* indirectly; PD-13, DEP-P4-I) and one `grid-downsample` dispatch per coarser
|
|
7
|
+
* level (G5). Level 0 holds `[sum m x, sum m y, sum m z, sum m]` per cell; every parent is the sum of its 2^dim
|
|
8
|
+
* children; the pseudo-cell (index `cells` of level 0) is never a child. No atomics touch the sums (design 6 row 12:
|
|
9
|
+
* bitwise reproducible); the only atomics are the hub append and the occupancy max.
|
|
10
|
+
*
|
|
11
|
+
* The named grid buffers (`pyramid`, `hubList`, `hubCounters`, `hubArgs`) are the caller's (the model's
|
|
12
|
+
* `BufferSpec`s, so `inspect(name)` reaches them); the static params of the finalize and of every level are written
|
|
13
|
+
* ONCE at bind() through the scope's params writer (PD-11), so record() writes no uniform. `src/primitives/**` never
|
|
14
|
+
* imports `src/context.ts`.
|
|
15
|
+
*/
|
|
16
|
+
import { WebGpuGraphError } from "../errors.js";
|
|
17
|
+
import { plan1d } from "../kernel/dispatch.js";
|
|
18
|
+
import { GRID_LEVEL_PARAMS, INDIRECT_PARAMS, kernelSpec } from "../kernels.js";
|
|
19
|
+
/**
|
|
20
|
+
* Prepares the pyramid's pipelines over a scope (G4, the finalize, G4b and G5; compiles once) so bind() and record()
|
|
21
|
+
* are synchronous. The planner lives exactly as long as the scope.
|
|
22
|
+
* @param scope - the caller's scope (device, caps, cache, scratch, params)
|
|
23
|
+
* @param spec - the grid
|
|
24
|
+
* @returns the planner
|
|
25
|
+
*/
|
|
26
|
+
export async function preparePyramid(scope, spec) {
|
|
27
|
+
const centroid = await scope.pipelines.kernel(kernelSpec("grid-centroid"));
|
|
28
|
+
const finalize = await scope.pipelines.kernel(kernelSpec("indirect-finalize"));
|
|
29
|
+
const hub = await scope.pipelines.kernel(kernelSpec("grid-centroid-hub"));
|
|
30
|
+
const downsample = await scope.pipelines.kernel(kernelSpec("grid-downsample"));
|
|
31
|
+
return new GridPyramidPlannerImpl(scope, spec, { centroid, finalize, hub, downsample });
|
|
32
|
+
}
|
|
33
|
+
/** The planner: G4, G4a, G4b and G5 over one scope. */
|
|
34
|
+
class GridPyramidPlannerImpl {
|
|
35
|
+
/**
|
|
36
|
+
* Wraps the resolved kernels; use preparePyramid().
|
|
37
|
+
* @param scope - the caller's scope
|
|
38
|
+
* @param spec - the grid
|
|
39
|
+
* @param kernels - the four kernels
|
|
40
|
+
*/
|
|
41
|
+
constructor(scope, spec, kernels) {
|
|
42
|
+
this.bound = null;
|
|
43
|
+
this.dispatches = 0;
|
|
44
|
+
this.scope = scope;
|
|
45
|
+
this.spec = spec;
|
|
46
|
+
this.kernels = kernels;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Dispatches the last record() issued.
|
|
50
|
+
* @returns the count
|
|
51
|
+
*/
|
|
52
|
+
get lastDispatches() {
|
|
53
|
+
return this.dispatches;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Binds the buffers and writes the static params (see the interface).
|
|
57
|
+
* @param bindings - the buffers
|
|
58
|
+
*/
|
|
59
|
+
bind(bindings) {
|
|
60
|
+
const { centroid, finalize, hub, downsample } = this.kernels;
|
|
61
|
+
const { spec, scope } = this;
|
|
62
|
+
const b = bindings;
|
|
63
|
+
const finalizeParams = scope.params(INDIRECT_PARAMS, {
|
|
64
|
+
countIndex: 0,
|
|
65
|
+
wg: 1, // the finalize plans ceil(count / wg) workgroups over ITEMS; G4b's item is a hub cell, one workgroup each
|
|
66
|
+
slot: 0,
|
|
67
|
+
pad0: 0,
|
|
68
|
+
});
|
|
69
|
+
const levels = [];
|
|
70
|
+
let parentSide = spec.g;
|
|
71
|
+
for (let level = 0; level + 1 < spec.levels; level++) {
|
|
72
|
+
parentSide /= 2;
|
|
73
|
+
const parentCells = parentSide ** spec.dim;
|
|
74
|
+
const params = scope.params(GRID_LEVEL_PARAMS, {
|
|
75
|
+
childBase: spec.levelOffsets[level],
|
|
76
|
+
parentBase: spec.levelOffsets[level + 1],
|
|
77
|
+
parentSide,
|
|
78
|
+
parentCells,
|
|
79
|
+
depth: spec.dim === 3 ? 2 : 1,
|
|
80
|
+
pad0: 0,
|
|
81
|
+
pad1: 0,
|
|
82
|
+
pad2: 0,
|
|
83
|
+
});
|
|
84
|
+
levels.push({
|
|
85
|
+
bound: downsample.bind({ pyramid: b.pyramid, P: params.binding }),
|
|
86
|
+
offset: params.offset,
|
|
87
|
+
parentCells,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
this.bound = {
|
|
91
|
+
hubArgs: b.hubArgs,
|
|
92
|
+
centroid: centroid.bind({
|
|
93
|
+
sortedIdx: b.sortedIdx,
|
|
94
|
+
cellStart: b.cellStart,
|
|
95
|
+
pos: b.pos,
|
|
96
|
+
pyramid: b.pyramid,
|
|
97
|
+
hubList: b.hubList,
|
|
98
|
+
hubCounters: b.hubCounters,
|
|
99
|
+
P: b.params,
|
|
100
|
+
}),
|
|
101
|
+
finalize: finalize.bind({ counters: b.hubCounters, args: b.hubArgs, P: finalizeParams.binding }),
|
|
102
|
+
finalizeOffset: finalizeParams.offset,
|
|
103
|
+
hub: hub.bind({
|
|
104
|
+
sortedIdx: b.sortedIdx,
|
|
105
|
+
cellStart: b.cellStart,
|
|
106
|
+
pos: b.pos,
|
|
107
|
+
pyramid: b.pyramid,
|
|
108
|
+
hubList: b.hubList,
|
|
109
|
+
hubCount: b.hubCounters,
|
|
110
|
+
P: b.params,
|
|
111
|
+
}),
|
|
112
|
+
levels,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Records the stages (see the interface).
|
|
117
|
+
* @param pass - the compute pass
|
|
118
|
+
* @param paramsOffset - the `Fa2Params` dynamic offset
|
|
119
|
+
* @param upTo - the last stage
|
|
120
|
+
*/
|
|
121
|
+
record(pass, paramsOffset, upTo) {
|
|
122
|
+
const { bound, scope, spec } = this;
|
|
123
|
+
if (bound === null) {
|
|
124
|
+
throw new WebGpuGraphError("E_NOT_LOADED", "gridPyramid: record() before bind()", { argument: "bind" });
|
|
125
|
+
}
|
|
126
|
+
const { centroid, finalize, hub, downsample } = this.kernels;
|
|
127
|
+
const one = { x: 1, y: 1, z: 1, items: 1, stride: null };
|
|
128
|
+
centroid.dispatch(pass, bound.centroid, plan1d(spec.cells + 1, scope.workgroupSize, scope.caps), [paramsOffset]);
|
|
129
|
+
finalize.dispatch(pass, bound.finalize, one, [bound.finalizeOffset]);
|
|
130
|
+
hub.dispatchIndirect(pass, bound.hub, bound.hubArgs, 0, [paramsOffset]);
|
|
131
|
+
this.dispatches = 3;
|
|
132
|
+
if ((upTo ?? "G5") === "G4") {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
for (const level of bound.levels) {
|
|
136
|
+
downsample.dispatch(pass, level.bound, plan1d(level.parentCells, scope.workgroupSize, scope.caps), [
|
|
137
|
+
level.offset,
|
|
138
|
+
]);
|
|
139
|
+
this.dispatches += 1;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=grid-pyramid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grid-pyramid.js","sourceRoot":"","sources":["../../../src/primitives/grid-pyramid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAqB,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAElE,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAoD/E;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAkB,EAAE,IAAc;IACnE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC;IAC3E,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC/E,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC1E,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAC/E,OAAO,IAAI,sBAAsB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC;AAC5F,CAAC;AAoBD,uDAAuD;AACvD,MAAM,sBAAsB;IAOxB;;;;;OAKG;IACH,YAAY,KAAkB,EAAE,IAAc,EAAE,OAAgB;QATxD,UAAK,GAAiB,IAAI,CAAC;QAC3B,eAAU,GAAG,CAAC,CAAC;QASnB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACH,IAAI,cAAc;QACd,OAAO,IAAI,CAAC,UAAU,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACH,IAAI,CAAC,QAA6B;QAC9B,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QAC7D,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;QAC7B,MAAM,CAAC,GAAG,QAAQ,CAAC;QACnB,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC,eAAe,EAAE;YACjD,UAAU,EAAE,CAAC;YACb,EAAE,EAAE,CAAC,EAAE,0GAA0G;YACjH,IAAI,EAAE,CAAC;YACP,IAAI,EAAE,CAAC;SACV,CAAC,CAAC;QACH,MAAM,MAAM,GAA6F,EAAE,CAAC;QAC5G,IAAI,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC;QACxB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;YACnD,UAAU,IAAI,CAAC,CAAC;YAChB,MAAM,WAAW,GAAG,UAAU,IAAI,IAAI,CAAC,GAAG,CAAC;YAC3C,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,iBAAiB,EAAE;gBAC3C,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;gBACnC,UAAU,EAAE,IAAI,CAAC,YAAY,CAAC,KAAK,GAAG,CAAC,CAAC;gBACxC,UAAU;gBACV,WAAW;gBACX,KAAK,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC7B,IAAI,EAAE,CAAC;gBACP,IAAI,EAAE,CAAC;gBACP,IAAI,EAAE,CAAC;aACV,CAAC,CAAC;YACH,MAAM,CAAC,IAAI,CAAC;gBACR,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;gBACjE,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,WAAW;aACd,CAAC,CAAC;QACP,CAAC;QACD,IAAI,CAAC,KAAK,GAAG;YACT,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC;gBACpB,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,GAAG,EAAE,CAAC,CAAC,GAAG;gBACV,OAAO,EAAE,CAAC,CAAC,OAAO;gBAClB,OAAO,EAAE,CAAC,CAAC,OAAO;gBAClB,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,CAAC,EAAE,CAAC,CAAC,MAAM;aACd,CAAC;YACF,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,cAAc,CAAC,OAAO,EAAE,CAAC;YAChG,cAAc,EAAE,cAAc,CAAC,MAAM;YACrC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC;gBACV,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,GAAG,EAAE,CAAC,CAAC,GAAG;gBACV,OAAO,EAAE,CAAC,CAAC,OAAO;gBAClB,OAAO,EAAE,CAAC,CAAC,OAAO;gBAClB,QAAQ,EAAE,CAAC,CAAC,WAAW;gBACvB,CAAC,EAAE,CAAC,CAAC,MAAM;aACd,CAAC;YACF,MAAM;SACT,CAAC;IACN,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,IAA2B,EAAE,YAAoB,EAAE,IAAuB;QAC7E,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;QACpC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,gBAAgB,CAAC,cAAc,EAAE,qCAAqC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QAC5G,CAAC;QACD,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QAC7D,MAAM,GAAG,GAAiB,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QACvE,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,KAAK,CAAC,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;QACjH,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;QACrE,GAAG,CAAC,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;QACxE,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YAC1B,OAAO;QACX,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YAC/B,UAAU,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE;gBAC/F,KAAK,CAAC,MAAM;aACf,CAAC,CAAC;YACH,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;QACzB,CAAC;IACL,CAAC;CACJ"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The grid build (spec 6 row 12, 7.7 G1-G3; P4-T8): `GridSpec` on the host (PD-9) and the planner that records, into
|
|
3
|
+
* the caller's pass, the cell keys (G1, `grid-cell-key`), the stable sort by key (G2: `radixSort` at GRID_SORT_BITS,
|
|
4
|
+
* or `countingSortByKey` when the caller asks for the set-deterministic path) and the per-cell histogram with its
|
|
5
|
+
* exclusive scan (G3: the `histogram` kernel over `cellKey` and the `scan` of it; DEP-P4-I names no grid-specific
|
|
6
|
+
* id). `cellHist` and `cellStart` hold `cells + 2` words: every real cell, the outside pseudo-cell at index `cells`
|
|
7
|
+
* and one more so `cellStart[cells + 1] === n` closes the last range. Every zeroing is a `fill` dispatch inside the
|
|
8
|
+
* pass (PD-12), never an encoder clear.
|
|
9
|
+
*
|
|
10
|
+
* The named grid buffers (`cellKey`, `cellVal`, `sortedKey`, `sortedIdx`, `cellHist`, `cellStart`) are the caller's
|
|
11
|
+
* (the model's `BufferSpec`s, so `inspect(name)` reaches them); the anonymous scratch of the sort (the digit-major
|
|
12
|
+
* table and its scan, the counting-sort cursor) is taken from the scope ONCE at bind() so the sub-kernels bind the
|
|
13
|
+
* same buffers every iteration and `Kernel.bind`'s identity cache holds (PD-11). `src/primitives/**` never imports
|
|
14
|
+
* `src/context.ts`.
|
|
15
|
+
*/
|
|
16
|
+
import { type ResolvedLayoutTuning } from "../types/layout.js";
|
|
17
|
+
import { type Binding } from "../types/memory.js";
|
|
18
|
+
import { type ReduceScope } from "./reduce.js";
|
|
19
|
+
/** The grid's geometry (spec 7.7 geometry table; PD-9), computed on the host once per load. */
|
|
20
|
+
export interface GridSpec {
|
|
21
|
+
/** 2 or 3. */
|
|
22
|
+
readonly dim: 2 | 3;
|
|
23
|
+
/** The finest side `G` per axis: a power of two in [GRID_MIN_SIDE, floorPow2(gridMax)]. */
|
|
24
|
+
readonly g: number;
|
|
25
|
+
/** `log2(G / GRID_COARSEST_SIDE) + 1`. */
|
|
26
|
+
readonly levels: number;
|
|
27
|
+
/** `G^dim` finest cells; the outside pseudo-cell is index `cells`. */
|
|
28
|
+
readonly cells: number;
|
|
29
|
+
/** `cells + 2`: the length of `cellHist` / `cellStart`. */
|
|
30
|
+
readonly histWords: number;
|
|
31
|
+
/** The first cell of every level inside the pyramid: `levelOffsets[0] = 0`, level 0 holds `cells + 1` (the pseudo-cell), level L `(G / 2^L)^dim`. */
|
|
32
|
+
readonly levelOffsets: readonly number[];
|
|
33
|
+
/** Every level's cells together: `levelOffsets[levels - 1] + GRID_COARSEST_SIDE^dim`. */
|
|
34
|
+
readonly pyramidCells: number;
|
|
35
|
+
/** Whether G2 is the stable radix sort (true) or the set-deterministic counting sort (false). */
|
|
36
|
+
readonly deterministic: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The grid of `n` nodes in `dim` dimensions under the tuning (spec 7.7 geometry table; PD-9): `G = clamp(nextPow2(2 *
|
|
40
|
+
* ceil(n^(1 / dim))), GRID_MIN_SIDE, floorPow2(gridMax))` where `gridMax` is `gridMax2D` or `gridMax3D`, rounded DOWN
|
|
41
|
+
* to a power of two so every level's side is an integer (512 and 128 stay; 100 becomes 64); `levels = log2(G /
|
|
42
|
+
* GRID_COARSEST_SIDE) + 1`. At the caps: 349,521 pyramid cells in 2D, 2,396,737 in 3D (the design's counts plus the
|
|
43
|
+
* pseudo-cell).
|
|
44
|
+
* @param n - the node count (>= 0)
|
|
45
|
+
* @param dim - 2 or 3
|
|
46
|
+
* @param tuning - the resolved layout tuning (`gridMax2D`, `gridMax3D`, `deterministic`)
|
|
47
|
+
* @returns the spec
|
|
48
|
+
*/
|
|
49
|
+
export declare function gridSpecFor(n: number, dim: 2 | 3, tuning: Pick<ResolvedLayoutTuning, "gridMax2D" | "gridMax3D" | "deterministic">): GridSpec;
|
|
50
|
+
/**
|
|
51
|
+
* The bytes of the pyramid (spec 7.7: 16 B per cell, every level, the pseudo-cell included): 38,347,792 at the 3D cap.
|
|
52
|
+
* @param spec - the grid
|
|
53
|
+
* @returns the byte length
|
|
54
|
+
*/
|
|
55
|
+
export declare function gridPyramidBytes(spec: GridSpec): number;
|
|
56
|
+
/**
|
|
57
|
+
* The buffers the build reads and writes (the model's named buffers; `state` and `params` are the blocks G1 reads).
|
|
58
|
+
* The parameter type of GridBuildPlanner.bind (knip: exported for the signature, not imported by name).
|
|
59
|
+
* @public
|
|
60
|
+
*/
|
|
61
|
+
export interface GridBuildBindings {
|
|
62
|
+
/** `array<vec4f>` positions (xyz, mass). */
|
|
63
|
+
readonly pos: Binding;
|
|
64
|
+
/** The `Fa2State` block (`gridMin`, `invCellSize`), read-only here. */
|
|
65
|
+
readonly state: Binding;
|
|
66
|
+
/** The `Fa2Params` uniform ring (`n`, `dim`, `gridMax`); `record()` takes the iteration's dynamic offset. */
|
|
67
|
+
readonly params: Binding;
|
|
68
|
+
/**
|
|
69
|
+
* `n` words: G1's keys, node-indexed until G2. On the deterministic path the radix sort ping-pongs through this
|
|
70
|
+
* pair (PD-5: three passes, the even one writes the input pair), so after G2 `cellKey` / `cellVal` hold the
|
|
71
|
+
* sort's last even-pass intermediate -- a permutation of the keys, which is why the histogram recorded after it
|
|
72
|
+
* counts the same multiset; the node-indexed keys of an iteration are read through `upTo: "G1"`. The counting
|
|
73
|
+
* path never writes them.
|
|
74
|
+
*/
|
|
75
|
+
readonly cellKey: Binding;
|
|
76
|
+
/** `n` words: G1's values (`i`); the sort's working pair with `cellKey`. */
|
|
77
|
+
readonly cellVal: Binding;
|
|
78
|
+
/** `n` words: the sorted keys (the radix path's result pair; unused by the counting path). */
|
|
79
|
+
readonly sortedKey: Binding;
|
|
80
|
+
/** `n` words: the sorted node indices. */
|
|
81
|
+
readonly sortedIdx: Binding;
|
|
82
|
+
/** `cells + 2` words: the per-cell counts. */
|
|
83
|
+
readonly cellHist: Binding;
|
|
84
|
+
/** `cells + 2` words: the exclusive scan of `cellHist`. */
|
|
85
|
+
readonly cellStart: Binding;
|
|
86
|
+
}
|
|
87
|
+
/** Where `record()` stops: after the keys (G1), after the sort (G2) or after the histogram and its scan (G3, the default). */
|
|
88
|
+
export type GridBuildStage = "G1" | "G2" | "G3";
|
|
89
|
+
/** A prepared grid build (spec 7.7 G1-G3): binds once per load, records the stages of one iteration into a pass. */
|
|
90
|
+
export interface GridBuildPlanner {
|
|
91
|
+
/**
|
|
92
|
+
* Binds the named buffers and takes the sort's scratch from the scope, sized by `cellKey` (its word count is the
|
|
93
|
+
* node capacity; PD-11); called once per load (a second call rebinds and takes fresh scratch, so it belongs to a
|
|
94
|
+
* reload, never to an iteration).
|
|
95
|
+
* @param bindings - the buffers
|
|
96
|
+
*/
|
|
97
|
+
bind(bindings: GridBuildBindings): void;
|
|
98
|
+
/**
|
|
99
|
+
* Records G1, G2 and G3 for `n` nodes at the `Fa2Params` slot `paramsOffset`; `upTo` stops after the named
|
|
100
|
+
* stage (the counting path has no separate G2 stop: its sort and histogram are one sequence, so "G2" runs it all).
|
|
101
|
+
* @param pass - the compute pass
|
|
102
|
+
* @param n - the node count (in [1, the capacity]; the caller never records a grid iteration for an empty graph)
|
|
103
|
+
* @param paramsOffset - the dynamic offset of this iteration's `Fa2Params`
|
|
104
|
+
* @param upTo - the last stage to record (default "G3")
|
|
105
|
+
*/
|
|
106
|
+
record(pass: GPUComputePassEncoder, n: number, paramsOffset: number, upTo?: GridBuildStage): void;
|
|
107
|
+
/** Dispatches the last record() issued. */
|
|
108
|
+
readonly lastDispatches: number;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Prepares the grid build's pipelines over a scope (G1 and the sort / histogram / scan planners it composes; compiles
|
|
112
|
+
* once) so bind() and record() are synchronous. The planner lives exactly as long as the scope.
|
|
113
|
+
* @param scope - the caller's scope (device, caps, cache, scratch, params)
|
|
114
|
+
* @param spec - the grid
|
|
115
|
+
* @returns the planner
|
|
116
|
+
*/
|
|
117
|
+
export declare function prepareGridBuild(scope: ReduceScope, spec: GridSpec): Promise<GridBuildPlanner>;
|
|
118
|
+
//# sourceMappingURL=grid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../../../src/primitives/grid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAQlD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,+FAA+F;AAC/F,MAAM,WAAW,QAAQ;IACrB,cAAc;IACd,QAAQ,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IACpB,2FAA2F;IAC3F,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,qJAAqJ;IACrJ,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,yFAAyF;IACzF,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,iGAAiG;IACjG,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACnC;AA4BD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CACvB,CAAC,EAAE,MAAM,EACT,GAAG,EAAE,CAAC,GAAG,CAAC,EACV,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,WAAW,GAAG,WAAW,GAAG,eAAe,CAAC,GAChF,QAAQ,CA0BV;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAEvD;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAC9B,4CAA4C;IAC5C,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,6GAA6G;IAC7G,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,8FAA8F;IAC9F,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC/B;AAED,8HAA8H;AAC9H,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhD,oHAAoH;AACpH,MAAM,WAAW,gBAAgB;IAC7B;;;;;OAKG;IACH,IAAI,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACxC;;;;;;;OAOG;IACH,MAAM,CAAC,IAAI,EAAE,qBAAqB,EAAE,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAClG,2CAA2C;IAC3C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACnC;AAYD;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAWpG"}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The grid build (spec 6 row 12, 7.7 G1-G3; P4-T8): `GridSpec` on the host (PD-9) and the planner that records, into
|
|
3
|
+
* the caller's pass, the cell keys (G1, `grid-cell-key`), the stable sort by key (G2: `radixSort` at GRID_SORT_BITS,
|
|
4
|
+
* or `countingSortByKey` when the caller asks for the set-deterministic path) and the per-cell histogram with its
|
|
5
|
+
* exclusive scan (G3: the `histogram` kernel over `cellKey` and the `scan` of it; DEP-P4-I names no grid-specific
|
|
6
|
+
* id). `cellHist` and `cellStart` hold `cells + 2` words: every real cell, the outside pseudo-cell at index `cells`
|
|
7
|
+
* and one more so `cellStart[cells + 1] === n` closes the last range. Every zeroing is a `fill` dispatch inside the
|
|
8
|
+
* pass (PD-12), never an encoder clear.
|
|
9
|
+
*
|
|
10
|
+
* The named grid buffers (`cellKey`, `cellVal`, `sortedKey`, `sortedIdx`, `cellHist`, `cellStart`) are the caller's
|
|
11
|
+
* (the model's `BufferSpec`s, so `inspect(name)` reaches them); the anonymous scratch of the sort (the digit-major
|
|
12
|
+
* table and its scan, the counting-sort cursor) is taken from the scope ONCE at bind() so the sub-kernels bind the
|
|
13
|
+
* same buffers every iteration and `Kernel.bind`'s identity cache holds (PD-11). `src/primitives/**` never imports
|
|
14
|
+
* `src/context.ts`.
|
|
15
|
+
*/
|
|
16
|
+
import { GRID_COARSEST_SIDE, GRID_MIN_SIDE, GRID_SORT_BITS } from "../constants.js";
|
|
17
|
+
import { WebGpuGraphError } from "../errors.js";
|
|
18
|
+
import { plan1d } from "../kernel/dispatch.js";
|
|
19
|
+
import { kernelSpec } from "../kernels.js";
|
|
20
|
+
import { prepareCountingSort, prepareHistogram, } from "./histogram.js";
|
|
21
|
+
import { prepareRadixSort, radixHistBytes } from "./radix-sort.js";
|
|
22
|
+
import { prepareScan } from "./scan.js";
|
|
23
|
+
/**
|
|
24
|
+
* The smallest power of two >= x (by doubling; 1 for x <= 1).
|
|
25
|
+
* @param x - a non-negative number
|
|
26
|
+
* @returns the power of two
|
|
27
|
+
*/
|
|
28
|
+
function nextPow2(x) {
|
|
29
|
+
let p = 1;
|
|
30
|
+
while (p < x) {
|
|
31
|
+
p *= 2;
|
|
32
|
+
}
|
|
33
|
+
return p;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The largest power of two <= x (by doubling; 1 for x < 2).
|
|
37
|
+
* @param x - a number >= 1
|
|
38
|
+
* @returns the power of two
|
|
39
|
+
*/
|
|
40
|
+
function floorPow2(x) {
|
|
41
|
+
let p = 1;
|
|
42
|
+
while (p * 2 <= x) {
|
|
43
|
+
p *= 2;
|
|
44
|
+
}
|
|
45
|
+
return p;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The grid of `n` nodes in `dim` dimensions under the tuning (spec 7.7 geometry table; PD-9): `G = clamp(nextPow2(2 *
|
|
49
|
+
* ceil(n^(1 / dim))), GRID_MIN_SIDE, floorPow2(gridMax))` where `gridMax` is `gridMax2D` or `gridMax3D`, rounded DOWN
|
|
50
|
+
* to a power of two so every level's side is an integer (512 and 128 stay; 100 becomes 64); `levels = log2(G /
|
|
51
|
+
* GRID_COARSEST_SIDE) + 1`. At the caps: 349,521 pyramid cells in 2D, 2,396,737 in 3D (the design's counts plus the
|
|
52
|
+
* pseudo-cell).
|
|
53
|
+
* @param n - the node count (>= 0)
|
|
54
|
+
* @param dim - 2 or 3
|
|
55
|
+
* @param tuning - the resolved layout tuning (`gridMax2D`, `gridMax3D`, `deterministic`)
|
|
56
|
+
* @returns the spec
|
|
57
|
+
*/
|
|
58
|
+
export function gridSpecFor(n, dim, tuning) {
|
|
59
|
+
const gridMax = dim === 3 ? tuning.gridMax3D : tuning.gridMax2D;
|
|
60
|
+
const side = dim === 3 ? Math.cbrt(n) : Math.sqrt(n);
|
|
61
|
+
const cap = Math.max(GRID_MIN_SIDE, floorPow2(Math.max(1, gridMax)));
|
|
62
|
+
const g = Math.min(cap, Math.max(GRID_MIN_SIDE, nextPow2(2 * Math.ceil(side))));
|
|
63
|
+
let levels = 1;
|
|
64
|
+
for (let s = g; s > GRID_COARSEST_SIDE; s /= 2) {
|
|
65
|
+
levels++;
|
|
66
|
+
}
|
|
67
|
+
const cells = g ** dim;
|
|
68
|
+
const levelOffsets = [0];
|
|
69
|
+
let s = g;
|
|
70
|
+
for (let level = 0; level + 1 < levels; level++) {
|
|
71
|
+
levelOffsets.push(levelOffsets[level] + s ** dim + (level === 0 ? 1 : 0));
|
|
72
|
+
s /= 2;
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
dim,
|
|
76
|
+
g,
|
|
77
|
+
levels,
|
|
78
|
+
cells,
|
|
79
|
+
histWords: cells + 2,
|
|
80
|
+
levelOffsets: Object.freeze(levelOffsets),
|
|
81
|
+
pyramidCells: levelOffsets[levels - 1] + GRID_COARSEST_SIDE ** dim,
|
|
82
|
+
deterministic: tuning.deterministic,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The bytes of the pyramid (spec 7.7: 16 B per cell, every level, the pseudo-cell included): 38,347,792 at the 3D cap.
|
|
87
|
+
* @param spec - the grid
|
|
88
|
+
* @returns the byte length
|
|
89
|
+
*/
|
|
90
|
+
export function gridPyramidBytes(spec) {
|
|
91
|
+
return 16 * spec.pyramidCells;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Prepares the grid build's pipelines over a scope (G1 and the sort / histogram / scan planners it composes; compiles
|
|
95
|
+
* once) so bind() and record() are synchronous. The planner lives exactly as long as the scope.
|
|
96
|
+
* @param scope - the caller's scope (device, caps, cache, scratch, params)
|
|
97
|
+
* @param spec - the grid
|
|
98
|
+
* @returns the planner
|
|
99
|
+
*/
|
|
100
|
+
export async function prepareGridBuild(scope, spec) {
|
|
101
|
+
const cellKey = await scope.pipelines.kernel(kernelSpec("grid-cell-key"));
|
|
102
|
+
const path = spec.deterministic
|
|
103
|
+
? {
|
|
104
|
+
kind: "radix",
|
|
105
|
+
radix: await prepareRadixSort(scope),
|
|
106
|
+
histogram: await prepareHistogram(scope),
|
|
107
|
+
scan: await prepareScan(scope),
|
|
108
|
+
}
|
|
109
|
+
: { kind: "counting", counting: await prepareCountingSort(scope) };
|
|
110
|
+
return new GridBuildPlannerImpl(scope, spec, cellKey, path);
|
|
111
|
+
}
|
|
112
|
+
/** The planner: G1 and the composed sort / histogram / scan over one scope. */
|
|
113
|
+
class GridBuildPlannerImpl {
|
|
114
|
+
/**
|
|
115
|
+
* Wraps the resolved kernel and planners; use prepareGridBuild().
|
|
116
|
+
* @param scope - the caller's scope
|
|
117
|
+
* @param spec - the grid
|
|
118
|
+
* @param cellKey - the `grid-cell-key` kernel
|
|
119
|
+
* @param path - the sort path
|
|
120
|
+
*/
|
|
121
|
+
constructor(scope, spec, cellKey, path) {
|
|
122
|
+
this.bound = null;
|
|
123
|
+
this.dispatches = 0;
|
|
124
|
+
this.scope = scope;
|
|
125
|
+
this.spec = spec;
|
|
126
|
+
this.cellKey = cellKey;
|
|
127
|
+
this.path = path;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Dispatches the last record() issued.
|
|
131
|
+
* @returns the count
|
|
132
|
+
*/
|
|
133
|
+
get lastDispatches() {
|
|
134
|
+
return this.dispatches;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Binds the buffers and takes the sort's scratch (see the interface).
|
|
138
|
+
* @param bindings - the buffers
|
|
139
|
+
*/
|
|
140
|
+
bind(bindings) {
|
|
141
|
+
const capacity = Math.floor(bindings.cellKey.size / 4);
|
|
142
|
+
if (capacity < 1) {
|
|
143
|
+
throw new WebGpuGraphError("E_INVALID_ARGUMENT", "gridBuild: cellKey must hold at least one word", {
|
|
144
|
+
argument: "cellKey",
|
|
145
|
+
value: bindings.cellKey.size,
|
|
146
|
+
expected: 4,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const words = this.path.kind === "radix" ? radixHistBytes(capacity, this.scope.workgroupSize) / 4 : this.spec.histWords;
|
|
150
|
+
this.bound = {
|
|
151
|
+
bindings,
|
|
152
|
+
capacity,
|
|
153
|
+
scratchA: this.scratch(words, "grid/sort-scratch-a"),
|
|
154
|
+
scratchB: this.scratch(words, "grid/sort-scratch-b"),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Records the stages (see the interface).
|
|
159
|
+
* @param pass - the compute pass
|
|
160
|
+
* @param n - the node count
|
|
161
|
+
* @param paramsOffset - the `Fa2Params` dynamic offset
|
|
162
|
+
* @param upTo - the last stage
|
|
163
|
+
*/
|
|
164
|
+
record(pass, n, paramsOffset, upTo) {
|
|
165
|
+
const { bound } = this;
|
|
166
|
+
if (bound === null) {
|
|
167
|
+
throw new WebGpuGraphError("E_NOT_LOADED", "gridBuild: record() before bind()", { argument: "bind" });
|
|
168
|
+
}
|
|
169
|
+
if (!Number.isSafeInteger(n) || n < 1 || n > bound.capacity) {
|
|
170
|
+
throw new WebGpuGraphError("E_INVALID_ARGUMENT", "gridBuild: n must be an integer in [1, the capacity]", {
|
|
171
|
+
argument: "n",
|
|
172
|
+
value: n,
|
|
173
|
+
expected: bound.capacity,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
const stop = upTo ?? "G3";
|
|
177
|
+
const b = bound.bindings;
|
|
178
|
+
const keyBound = this.cellKey.bind({
|
|
179
|
+
pos: b.pos,
|
|
180
|
+
S: b.state,
|
|
181
|
+
cellKey: b.cellKey,
|
|
182
|
+
cellVal: b.cellVal,
|
|
183
|
+
P: b.params,
|
|
184
|
+
});
|
|
185
|
+
this.cellKey.dispatch(pass, keyBound, plan1d(n, this.scope.workgroupSize, this.scope.caps), [paramsOffset]);
|
|
186
|
+
this.dispatches = 1;
|
|
187
|
+
if (stop === "G1") {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const { histWords: bins } = this.spec;
|
|
191
|
+
if (this.path.kind === "counting") {
|
|
192
|
+
const { counting } = this.path;
|
|
193
|
+
const scratch = { hist: b.cellHist, cursor: bound.scratchA };
|
|
194
|
+
counting.record(pass, b.cellKey, n, bins, scratch, b.sortedIdx, b.cellStart);
|
|
195
|
+
this.dispatches += counting.lastDispatches;
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const { radix, histogram, scan } = this.path;
|
|
199
|
+
radix.record(pass, b.cellKey, b.cellVal, n, GRID_SORT_BITS, {
|
|
200
|
+
keys: b.sortedKey,
|
|
201
|
+
vals: b.sortedIdx,
|
|
202
|
+
hist: bound.scratchA,
|
|
203
|
+
offsets: bound.scratchB,
|
|
204
|
+
});
|
|
205
|
+
this.dispatches += radix.lastDispatches;
|
|
206
|
+
if (stop === "G2") {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
histogram.record(pass, b.cellKey, n, bins, b.cellHist);
|
|
210
|
+
scan.record(pass, b.cellHist, bins, b.cellStart);
|
|
211
|
+
this.dispatches += histogram.lastDispatches + scan.lastDispatches;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* A scratch of `words` u32 from the scope, bound whole.
|
|
215
|
+
* @param words - the word count (>= 1)
|
|
216
|
+
* @param label - the scratch label
|
|
217
|
+
* @returns the binding
|
|
218
|
+
*/
|
|
219
|
+
scratch(words, label) {
|
|
220
|
+
const size = 4 * Math.max(1, words);
|
|
221
|
+
const buffer = this.scope.scratch(size, label);
|
|
222
|
+
return { buffer, offset: 0, size, window: null };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=grid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grid.js","sourceRoot":"","sources":["../../../src/primitives/grid.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `histogram` and `countingSortByKey` primitive drivers (spec 6 row 5; P4-T3, PD-4). `histogram` zeroes `hist`
|
|
3
|
+
* with a `fill` dispatch and then adds one per key in global memory: order-independent, so bitwise deterministic on
|
|
4
|
+
* every adapter. `countingSortByKey` is the histogram, an exclusive scan of it into `outStart`, a zeroed per-bin
|
|
5
|
+
* `cursor`, and the scatter `outIndex[outStart[k] + atomicAdd(&cursor[k], 1)] = i`: the SET of indices inside a bin
|
|
6
|
+
* is fixed, their order follows the schedule (set-deterministic, design 6). Every zeroing is a dispatch inside the
|
|
7
|
+
* caller's pass (PD-12), never an encoder clear.
|
|
8
|
+
*
|
|
9
|
+
* The drivers own no device objects: the caller supplies a ReduceScope (the same record `reduce` takes) and the
|
|
10
|
+
* compute pass to record into. `src/primitives/**` never imports `src/context.ts`.
|
|
11
|
+
*/
|
|
12
|
+
import { type Binding } from "../types/memory.js";
|
|
13
|
+
import { type ReduceScope } from "./reduce.js";
|
|
14
|
+
/** A prepared histogram (spec 6 row 5): records the fill and the one counting dispatch into a pass. */
|
|
15
|
+
export interface HistogramPlanner {
|
|
16
|
+
/**
|
|
17
|
+
* Records the histogram of `count` u32 keys of `keys` over `bins` bins into `hist` (zeroed first by a `fill`
|
|
18
|
+
* dispatch); a key >= bins is not counted. For count 0 only the fill is recorded, so `hist` is all zero.
|
|
19
|
+
* @param pass - the compute pass
|
|
20
|
+
* @param keys - the keys (at least 4 x count bytes)
|
|
21
|
+
* @param count - the key count (a non-negative integer below 2^32)
|
|
22
|
+
* @param bins - the bin count (an integer in [1, 2^32))
|
|
23
|
+
* @param hist - the counts (at least 4 x bins bytes)
|
|
24
|
+
*/
|
|
25
|
+
record(pass: GPUComputePassEncoder, keys: Binding, count: number, bins: number, hist: Binding): void;
|
|
26
|
+
/** Dispatches the last record() issued: 1 (the fill) for count 0, else 2. */
|
|
27
|
+
readonly lastDispatches: number;
|
|
28
|
+
}
|
|
29
|
+
/** The two per-bin scratch arrays of a counting sort: the histogram and the scatter cursor, each at least 4 x bins bytes. */
|
|
30
|
+
interface CountingSortScratch {
|
|
31
|
+
readonly hist: Binding;
|
|
32
|
+
readonly cursor: Binding;
|
|
33
|
+
}
|
|
34
|
+
/** A prepared counting sort by key (spec 6 row 5): records the histogram, the scan, the cursor fill and the scatter into a pass. */
|
|
35
|
+
export interface CountingSortPlanner {
|
|
36
|
+
/**
|
|
37
|
+
* Records the counting sort of `count` keys of `keys` (every key < bins) into `outIndex` (the indices in key
|
|
38
|
+
* order; the order inside a bin follows the schedule) and `outStart` (the exclusive scan of the histogram, so bin
|
|
39
|
+
* k holds `outIndex[outStart[k] .. outStart[k + 1])` when outStart has bins + 1 words, as the grid's does).
|
|
40
|
+
* For count 0 the two fills and the scan run and `outStart` is all zero.
|
|
41
|
+
* @param pass - the compute pass
|
|
42
|
+
* @param keys - the keys (at least 4 x count bytes)
|
|
43
|
+
* @param count - the key count (a non-negative integer below 2^32)
|
|
44
|
+
* @param bins - the bin count (an integer in [1, 2^32))
|
|
45
|
+
* @param scratch - the per-bin histogram and cursor (each at least 4 x bins bytes)
|
|
46
|
+
* @param outIndex - the sorted indices (at least 4 x count bytes)
|
|
47
|
+
* @param outStart - the bin starts (at least 4 x bins bytes)
|
|
48
|
+
*/
|
|
49
|
+
record(pass: GPUComputePassEncoder, keys: Binding, count: number, bins: number, scratch: CountingSortScratch, outIndex: Binding, outStart: Binding): void;
|
|
50
|
+
/** Dispatches the last record() issued: the histogram's + the scan's + 1 (the cursor fill) + 1 (the scatter, count > 0 only). */
|
|
51
|
+
readonly lastDispatches: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Prepares the histogram pipelines of a scope (compiles `histogram` and `fill` once) so record() is synchronous.
|
|
55
|
+
* @param scope - the caller's scope (device, caps, cache, scratch, params)
|
|
56
|
+
* @returns the planner
|
|
57
|
+
*/
|
|
58
|
+
export declare function prepareHistogram(scope: ReduceScope): Promise<HistogramPlanner>;
|
|
59
|
+
/**
|
|
60
|
+
* Prepares the counting-sort pipelines of a scope (the histogram's, the scan's and `counting-scatter`) so record()
|
|
61
|
+
* is synchronous. The planner lives exactly as long as the scope (the scan keeps a scratch word of it).
|
|
62
|
+
* @param scope - the caller's scope
|
|
63
|
+
* @returns the planner
|
|
64
|
+
*/
|
|
65
|
+
export declare function prepareCountingSort(scope: ReduceScope): Promise<CountingSortPlanner>;
|
|
66
|
+
export {};
|
|
67
|
+
//# sourceMappingURL=histogram.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"histogram.d.ts","sourceRoot":"","sources":["../../../src/primitives/histogram.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,uGAAuG;AACvG,MAAM,WAAW,gBAAgB;IAC7B;;;;;;;;OAQG;IACH,MAAM,CAAC,IAAI,EAAE,qBAAqB,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IACrG,6EAA6E;IAC7E,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACnC;AAED,6HAA6H;AAC7H,UAAU,mBAAmB;IACzB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC5B;AAED,oIAAoI;AACpI,MAAM,WAAW,mBAAmB;IAChC;;;;;;;;;;;;OAYG;IACH,MAAM,CACF,IAAI,EAAE,qBAAqB,EAC3B,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,OAAO,EACjB,QAAQ,EAAE,OAAO,GAClB,IAAI,CAAC;IACR,iIAAiI;IACjI,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACnC;AAKD;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAEpF;AAaD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAK1F"}
|