@graphty/webgpu-graph-algorithms 0.5.0 → 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 +104 -52
- package/dist/browser.js +1 -1
- package/dist/chunks/{context-CRbw2Wyo.js → context-BXqgCifx.js} +225 -33
- package/dist/chunks/context-BXqgCifx.js.map +1 -0
- package/dist/node.js +1 -1
- package/dist/src/accelerator.d.ts +12 -10
- package/dist/src/accelerator.d.ts.map +1 -1
- package/dist/src/accelerator.js +32 -10
- package/dist/src/accelerator.js.map +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 +85 -8
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +85 -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 +10 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +14 -5
- 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 +9 -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 +18 -8
- package/dist/src/kernels.d.ts.map +1 -1
- package/dist/src/kernels.js +345 -22
- 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 +42 -5
- package/dist/src/layouts/force-simulation.d.ts.map +1 -1
- package/dist/src/layouts/force-simulation.js +84 -22
- package/dist/src/layouts/force-simulation.js.map +1 -1
- package/dist/src/layouts/forceatlas2.d.ts +107 -38
- package/dist/src/layouts/forceatlas2.d.ts.map +1 -1
- package/dist/src/layouts/forceatlas2.js +297 -290
- package/dist/src/layouts/forceatlas2.js.map +1 -1
- package/dist/src/layouts/fruchterman-reingold.d.ts +241 -0
- package/dist/src/layouts/fruchterman-reingold.d.ts.map +1 -0
- package/dist/src/layouts/fruchterman-reingold.js +739 -0
- package/dist/src/layouts/fruchterman-reingold.js.map +1 -0
- package/dist/src/layouts/model-common.d.ts +140 -0
- package/dist/src/layouts/model-common.d.ts.map +1 -0
- package/dist/src/layouts/model-common.js +269 -0
- package/dist/src/layouts/model-common.js.map +1 -0
- 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 +224 -0
- package/dist/src/layouts/spring-electrical.d.ts.map +1 -0
- package/dist/src/layouts/spring-electrical.js +665 -0
- package/dist/src/layouts/spring-electrical.js.map +1 -0
- 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/accelerator.d.ts +7 -3
- package/dist/src/types/accelerator.d.ts.map +1 -1
- 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 +52 -0
- package/dist/src/types/layout.d.ts.map +1 -1
- package/dist/src/types/options.d.ts +43 -1
- package/dist/src/types/options.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 -8
- package/dist/src/wgsl/fa2-attraction.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/fa2-attraction.wgsl.js +100 -17
- package/dist/src/wgsl/fa2-attraction.wgsl.js.map +1 -1
- package/dist/src/wgsl/fa2-integrate.wgsl.d.ts +7 -2
- package/dist/src/wgsl/fa2-integrate.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/fa2-integrate.wgsl.js +28 -2
- package/dist/src/wgsl/fa2-integrate.wgsl.js.map +1 -1
- package/dist/src/wgsl/fa2-repulsion-exact.wgsl.d.ts +4 -2
- package/dist/src/wgsl/fa2-repulsion-exact.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/fa2-repulsion-exact.wgsl.js +14 -5
- package/dist/src/wgsl/fa2-repulsion-exact.wgsl.js.map +1 -1
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts +12 -1
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.js +54 -0
- 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 +5016 -1130
- package/dist/webgpu-graph-algorithms.js.map +1 -1
- package/package.json +10 -7
- package/src/accelerator.ts +46 -12
- 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 +108 -8
- package/src/errors.ts +3 -1
- package/src/index.ts +25 -5
- package/src/kernel/dispatch.ts +18 -7
- package/src/kernel/kernel.ts +59 -5
- package/src/kernel/prelude.ts +15 -0
- package/src/kernel/profiler.ts +28 -4
- package/src/kernels.ts +378 -24
- package/src/layouts/calibrate.ts +187 -0
- package/src/layouts/force-simulation.ts +111 -26
- package/src/layouts/forceatlas2.ts +346 -324
- package/src/layouts/fruchterman-reingold.ts +918 -0
- package/src/layouts/model-common.ts +323 -0
- package/src/layouts/repulsion-grid.ts +451 -0
- package/src/layouts/spring-electrical.ts +845 -0
- 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/accelerator.ts +15 -3
- package/src/types/context.ts +56 -0
- package/src/types/layout.ts +58 -0
- package/src/types/options.ts +45 -1
- package/src/wgsl/counting-scatter.wgsl.ts +16 -0
- package/src/wgsl/fa2-attraction.wgsl.ts +100 -17
- package/src/wgsl/fa2-integrate.wgsl.ts +28 -2
- package/src/wgsl/fa2-repulsion-exact.wgsl.ts +14 -5
- package/src/wgsl/fa2-stats-finalize.wgsl.ts +54 -0
- 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-CRbw2Wyo.js.map +0 -1
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The grid-tier repulsion stage of the force models (spec 7.7, 7.4; P4-T10): G1-G3 through the T8 grid build, G4-G5
|
|
3
|
+
* through the T9 pyramid, then G6 (`grid-far-field`) and G7 (`grid-near-field`, K3's pair law and epilogue) over
|
|
4
|
+
* `plan1d(n)`, followed by K4 (`fa2-speed-finalize`) exactly as `RepulsionExact` records it. The named grid buffers
|
|
5
|
+
* are the model's `BufferSpec`s (`buffers()`, so `inspect(name)` reaches them); the anonymous scratch of the sort
|
|
6
|
+
* and the scan and the static params of the pyramid draw on ONE `Lease` of the context's pool taken at `create()`
|
|
7
|
+
* (the scan planner takes its block-sum levels at prepare time) and released by `dispose()` (PD-11, DEP-P4-D: a
|
|
8
|
+
* per-batch lease would rebuild the G2-G4 bind groups every batch; a model creates one stage per bind(), so a stage
|
|
9
|
+
* is bound once). The stage is the ONE class every model reaches the grid tier through (PD-22): the FR and
|
|
10
|
+
* spring-electrical models pass `LAW` 1 / 2 in their override set, which reaches G6's per-cell law and G7's pair law
|
|
11
|
+
* (P4-T13).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { GRID_HUB_CELL } from "../constants.js";
|
|
15
|
+
import { BufferUsage } from "../device/webgpu-constants.js";
|
|
16
|
+
import { WebGpuGraphError } from "../errors.js";
|
|
17
|
+
import { plan1d } from "../kernel/dispatch.js";
|
|
18
|
+
import { type BoundKernel, INDIRECT_ARGS_STRIDE, type Kernel } from "../kernel/kernel.js";
|
|
19
|
+
import { type PipelineCache } from "../kernel/pipeline-cache.js";
|
|
20
|
+
import { type UniformBlock, type UniformValues } from "../kernel/struct-block.js";
|
|
21
|
+
import { type WgslModuleSpec } from "../kernel/wgsl.js";
|
|
22
|
+
import { kernelSpec } from "../kernels.js";
|
|
23
|
+
import { type BufferPool } from "../memory/buffer-pool.js";
|
|
24
|
+
import { type Lease } from "../memory/lease.js";
|
|
25
|
+
import {
|
|
26
|
+
type GridBuildPlanner,
|
|
27
|
+
type GridBuildStage,
|
|
28
|
+
gridPyramidBytes,
|
|
29
|
+
type GridSpec,
|
|
30
|
+
prepareGridBuild,
|
|
31
|
+
} from "../primitives/grid.js";
|
|
32
|
+
import { type GridPyramidPlanner, preparePyramid } from "../primitives/grid-pyramid.js";
|
|
33
|
+
import { type ReduceScope } from "../primitives/reduce.js";
|
|
34
|
+
import { type PlanCaps } from "../types/context.js";
|
|
35
|
+
import { type Binding } from "../types/memory.js";
|
|
36
|
+
import { type BufferSpec } from "./force-simulation.js";
|
|
37
|
+
import { type RepulsionExactOverrides, type RepulsionExactResources } from "./repulsion-exact.js";
|
|
38
|
+
|
|
39
|
+
/** The stage names the grid tier records, in dispatch order (the `upTo` vocabulary of recordRepulsion). */
|
|
40
|
+
const GRID_STAGES = ["G1", "G2", "G3", "G4", "G5", "G6", "G7"] as const;
|
|
41
|
+
|
|
42
|
+
/** A grid stage name. */
|
|
43
|
+
export type GridStage = (typeof GRID_STAGES)[number];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The buffers the grid-tier repulsion stage binds: the exact tier's plus the named grid buffers of `buffers()`. The
|
|
47
|
+
* parameter type of RepulsionGrid.bind (knip: exported for the signature, not imported by name).
|
|
48
|
+
* @public
|
|
49
|
+
*/
|
|
50
|
+
export interface RepulsionGridResources extends RepulsionExactResources {
|
|
51
|
+
readonly cellKey: Binding;
|
|
52
|
+
readonly cellVal: Binding;
|
|
53
|
+
readonly sortedKey: Binding;
|
|
54
|
+
readonly sortedIdx: Binding;
|
|
55
|
+
readonly cellHist: Binding;
|
|
56
|
+
readonly cellStart: Binding;
|
|
57
|
+
readonly hubList: Binding;
|
|
58
|
+
readonly hubCounters: Binding;
|
|
59
|
+
readonly hubArgs: Binding;
|
|
60
|
+
readonly pyramid: Binding;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The override set G6 / G7 / K4 compile with: K3's three plus the repulsion law (0 FA2, 1 FR, 2 coulomb; P4-T13,
|
|
65
|
+
* PD-22).
|
|
66
|
+
* @public
|
|
67
|
+
*/
|
|
68
|
+
export interface RepulsionGridOverrides extends RepulsionExactOverrides {
|
|
69
|
+
readonly LAW: 0 | 1 | 2;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* What the stage needs of the context to build its scope: the pieces `ModelResources` carries. The parameter type
|
|
74
|
+
* of RepulsionGrid.create (knip: exported for the signature, not imported by name).
|
|
75
|
+
* @public
|
|
76
|
+
*/
|
|
77
|
+
export interface RepulsionGridScope {
|
|
78
|
+
readonly device: GPUDevice;
|
|
79
|
+
readonly caps: PlanCaps;
|
|
80
|
+
readonly pipelines: PipelineCache;
|
|
81
|
+
readonly pool: BufferPool;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The storage usage of every grid buffer. */
|
|
85
|
+
const STORAGE_RW = BufferUsage.STORAGE | BufferUsage.COPY_SRC | BufferUsage.COPY_DST;
|
|
86
|
+
|
|
87
|
+
/** The lease the scope's scratch() and params() draw on: taken at create(), null after dispose(). */
|
|
88
|
+
interface LeaseBox {
|
|
89
|
+
lease: Lease | null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The stage's lease, or E_DISPOSED after dispose().
|
|
94
|
+
* @param box - the stage's lease box
|
|
95
|
+
* @returns the lease
|
|
96
|
+
*/
|
|
97
|
+
function leaseOf(box: LeaseBox): Lease {
|
|
98
|
+
if (box.lease === null) {
|
|
99
|
+
throw new WebGpuGraphError("E_DISPOSED", "RepulsionGrid: the stage was disposed", { label: "RepulsionGrid" });
|
|
100
|
+
}
|
|
101
|
+
return box.lease;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* A uniform buffer of the lease holding one written record of `block` (the pyramid's static params, PD-11).
|
|
106
|
+
* @param device - the device
|
|
107
|
+
* @param lease - the stage's lease
|
|
108
|
+
* @param block - the uniform block
|
|
109
|
+
* @param values - the values to write
|
|
110
|
+
* @returns the whole-buffer binding and a zero dynamic offset
|
|
111
|
+
*/
|
|
112
|
+
function writeParams(
|
|
113
|
+
device: GPUDevice,
|
|
114
|
+
lease: Lease,
|
|
115
|
+
block: UniformBlock,
|
|
116
|
+
values: UniformValues,
|
|
117
|
+
): { readonly binding: Binding; readonly offset: number } {
|
|
118
|
+
const buffer = lease.uniform(block.byteLength, `grid/${block.name}`);
|
|
119
|
+
const bytes = new ArrayBuffer(block.byteLength);
|
|
120
|
+
block.write(new DataView(bytes), values);
|
|
121
|
+
device.queue.writeBuffer(buffer, 0, bytes);
|
|
122
|
+
return { binding: { buffer, offset: 0, size: block.byteLength, window: null }, offset: 0 };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The G6 spec of an override set (the law alone).
|
|
127
|
+
* @param overrides - the override set of the stage
|
|
128
|
+
* @returns the spec
|
|
129
|
+
*/
|
|
130
|
+
function farFieldSpec(overrides: RepulsionGridOverrides): WgslModuleSpec {
|
|
131
|
+
return kernelSpec("grid-far-field", { LAW: overrides.LAW });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The G7 spec of an override set (K3's four).
|
|
136
|
+
* @param overrides - the override set of the stage
|
|
137
|
+
* @returns the spec
|
|
138
|
+
*/
|
|
139
|
+
function nearFieldSpec(overrides: RepulsionGridOverrides): WgslModuleSpec {
|
|
140
|
+
return kernelSpec("grid-near-field", {
|
|
141
|
+
SWING_MODE: overrides.SWING_MODE,
|
|
142
|
+
STRONG_GRAVITY: overrides.STRONG_GRAVITY,
|
|
143
|
+
GRAVITY_CENTER: overrides.GRAVITY_CENTER,
|
|
144
|
+
LAW: overrides.LAW,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** The three kernels the stage dispatches itself (the planners hold the others). */
|
|
149
|
+
interface FieldKernels {
|
|
150
|
+
readonly far: Kernel;
|
|
151
|
+
readonly near: Kernel;
|
|
152
|
+
readonly speedFinalize: Kernel;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** What bind() produced. */
|
|
156
|
+
interface Bound {
|
|
157
|
+
readonly far: BoundKernel;
|
|
158
|
+
readonly near: BoundKernel;
|
|
159
|
+
readonly speedFinalize: BoundKernel;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** G1-G7 then K4 (spec 7.7, 7.4): the grid tier's repulsion stage. */
|
|
163
|
+
export class RepulsionGrid {
|
|
164
|
+
/** The overrides G6 / G7 / K4 were compiled with (a frozen copy of the argument of create()). */
|
|
165
|
+
readonly overrides: RepulsionGridOverrides;
|
|
166
|
+
/** The grid geometry the stage was prepared for. */
|
|
167
|
+
readonly spec: GridSpec;
|
|
168
|
+
|
|
169
|
+
private readonly caps: PlanCaps;
|
|
170
|
+
private readonly workgroupSize: number;
|
|
171
|
+
private readonly box: LeaseBox;
|
|
172
|
+
private readonly build: GridBuildPlanner;
|
|
173
|
+
private readonly pyramid: GridPyramidPlanner;
|
|
174
|
+
private readonly far: Kernel;
|
|
175
|
+
private readonly near: Kernel;
|
|
176
|
+
private readonly speedFinalize: Kernel;
|
|
177
|
+
private bound: Bound | null = null;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Holds the planners and kernels; create() is the only caller.
|
|
181
|
+
* @param scope - the context pieces
|
|
182
|
+
* @param workgroupSize - the device's workgroup size (every kernel compiles with it)
|
|
183
|
+
* @param overrides - the override set G6 / G7 / K4 were compiled with
|
|
184
|
+
* @param spec - the grid
|
|
185
|
+
* @param box - the lease box the scope's scratch() and params() read
|
|
186
|
+
* @param build - the T8 planner (G1-G3)
|
|
187
|
+
* @param pyramid - the T9 planner (G4-G5)
|
|
188
|
+
* @param kernels - G6 (`far`), G7 (`near`) and K4 (`speedFinalize`)
|
|
189
|
+
*/
|
|
190
|
+
private constructor(
|
|
191
|
+
scope: RepulsionGridScope,
|
|
192
|
+
workgroupSize: number,
|
|
193
|
+
overrides: RepulsionGridOverrides,
|
|
194
|
+
spec: GridSpec,
|
|
195
|
+
box: LeaseBox,
|
|
196
|
+
build: GridBuildPlanner,
|
|
197
|
+
pyramid: GridPyramidPlanner,
|
|
198
|
+
kernels: FieldKernels,
|
|
199
|
+
) {
|
|
200
|
+
this.caps = scope.caps;
|
|
201
|
+
this.box = box;
|
|
202
|
+
this.workgroupSize = workgroupSize;
|
|
203
|
+
this.overrides = Object.freeze({
|
|
204
|
+
SWING_MODE: overrides.SWING_MODE,
|
|
205
|
+
STRONG_GRAVITY: overrides.STRONG_GRAVITY,
|
|
206
|
+
GRAVITY_CENTER: overrides.GRAVITY_CENTER,
|
|
207
|
+
LAW: overrides.LAW,
|
|
208
|
+
});
|
|
209
|
+
this.spec = spec;
|
|
210
|
+
this.build = build;
|
|
211
|
+
this.pyramid = pyramid;
|
|
212
|
+
this.far = kernels.far;
|
|
213
|
+
this.near = kernels.near;
|
|
214
|
+
this.speedFinalize = kernels.speedFinalize;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* The model-owned buffers of the grid tier (spec 7.3; PD-11): `cellKey` / `cellVal` / `sortedKey` / `sortedIdx`
|
|
219
|
+
* 4n, `cellHist` / `cellStart` 4 (cells + 2) zeroed, `hubList` one word per possible hub cell, `hubArgs` one
|
|
220
|
+
* indirect slot, `pyramid` 16 B per pyramid cell zeroed. `hubCounters` (16 B, zeroed) is the MODEL's on every
|
|
221
|
+
* tier (PD-14: K1 binds it on the exact tier too). n = 0 reports one node's worth of bytes (spec 3.6).
|
|
222
|
+
* @param n - the node count
|
|
223
|
+
* @param spec - the grid
|
|
224
|
+
* @returns the specs
|
|
225
|
+
*/
|
|
226
|
+
static buffers(n: number, spec: GridSpec): readonly BufferSpec[] {
|
|
227
|
+
const words = 4 * Math.max(1, n);
|
|
228
|
+
return [
|
|
229
|
+
{ name: "cellKey", byteLength: words, usage: STORAGE_RW, zero: false },
|
|
230
|
+
{ name: "cellVal", byteLength: words, usage: STORAGE_RW, zero: false },
|
|
231
|
+
{ name: "sortedKey", byteLength: words, usage: STORAGE_RW, zero: false },
|
|
232
|
+
{ name: "sortedIdx", byteLength: words, usage: STORAGE_RW, zero: false },
|
|
233
|
+
{ name: "cellHist", byteLength: 4 * spec.histWords, usage: STORAGE_RW, zero: true },
|
|
234
|
+
{ name: "cellStart", byteLength: 4 * spec.histWords, usage: STORAGE_RW, zero: true },
|
|
235
|
+
{
|
|
236
|
+
name: "hubList",
|
|
237
|
+
byteLength: 4 * Math.max(1, Math.ceil(n / GRID_HUB_CELL)),
|
|
238
|
+
usage: STORAGE_RW,
|
|
239
|
+
zero: false,
|
|
240
|
+
},
|
|
241
|
+
{ name: "hubArgs", byteLength: INDIRECT_ARGS_STRIDE, usage: STORAGE_RW | BufferUsage.INDIRECT, zero: true },
|
|
242
|
+
{ name: "pyramid", byteLength: gridPyramidBytes(spec), usage: STORAGE_RW, zero: true },
|
|
243
|
+
];
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* The module specs of the grid tier under an override set (for warm() and the compile matrix): the build's
|
|
248
|
+
* (G1, the sort path `spec.deterministic` selects, the histogram, the scan, the fill), the pyramid's (G4, the
|
|
249
|
+
* finalize, G4b, G5), G6, G7 and K4.
|
|
250
|
+
* @param overrides - the override set of the stage
|
|
251
|
+
* @param spec - the grid (only its `deterministic` flag matters: the pipeline key carries no geometry)
|
|
252
|
+
* @returns the specs
|
|
253
|
+
*/
|
|
254
|
+
static specs(overrides: RepulsionGridOverrides, spec: GridSpec): readonly WgslModuleSpec[] {
|
|
255
|
+
const sort: WgslModuleSpec[] = spec.deterministic
|
|
256
|
+
? [kernelSpec("radix-hist"), kernelSpec("radix-scatter"), kernelSpec("scan-block"), kernelSpec("scan-add")]
|
|
257
|
+
: [kernelSpec("counting-scatter"), kernelSpec("scan-block"), kernelSpec("scan-add")];
|
|
258
|
+
return [
|
|
259
|
+
kernelSpec("grid-cell-key"),
|
|
260
|
+
...sort,
|
|
261
|
+
kernelSpec("histogram"),
|
|
262
|
+
kernelSpec("fill"),
|
|
263
|
+
kernelSpec("grid-centroid"),
|
|
264
|
+
kernelSpec("indirect-finalize"),
|
|
265
|
+
kernelSpec("grid-centroid-hub"),
|
|
266
|
+
kernelSpec("grid-downsample"),
|
|
267
|
+
farFieldSpec(overrides),
|
|
268
|
+
nearFieldSpec(overrides),
|
|
269
|
+
kernelSpec("fa2-speed-finalize", { SWING_MODE: overrides.SWING_MODE }),
|
|
270
|
+
];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Compiles every kernel of the tier through the cache (sequentially: PipelineCache.get compiles inside a
|
|
275
|
+
* validation scope, one stack per device) over a scope whose scratch and params draw on the stage's lease.
|
|
276
|
+
* @param scope - the context pieces (device, caps, pipelines, pool)
|
|
277
|
+
* @param workgroupSize - the device's workgroup size
|
|
278
|
+
* @param overrides - the SWING_MODE / STRONG_GRAVITY / GRAVITY_CENTER / LAW set of this stage
|
|
279
|
+
* @param spec - the grid
|
|
280
|
+
* @returns the stage, ready for bind()
|
|
281
|
+
*/
|
|
282
|
+
static async create(
|
|
283
|
+
scope: RepulsionGridScope,
|
|
284
|
+
workgroupSize: number,
|
|
285
|
+
overrides: RepulsionGridOverrides,
|
|
286
|
+
spec: GridSpec,
|
|
287
|
+
): Promise<RepulsionGrid> {
|
|
288
|
+
const box: LeaseBox = { lease: scope.pool.lease() };
|
|
289
|
+
const reduceScope: ReduceScope = {
|
|
290
|
+
device: scope.device,
|
|
291
|
+
caps: scope.caps,
|
|
292
|
+
pipelines: scope.pipelines,
|
|
293
|
+
pool: scope.pool,
|
|
294
|
+
workgroupSize,
|
|
295
|
+
scratch: (byteLength: number, label: string): GPUBuffer => leaseOf(box).storage(byteLength, label),
|
|
296
|
+
params: (
|
|
297
|
+
block: UniformBlock,
|
|
298
|
+
values: UniformValues,
|
|
299
|
+
): { readonly binding: Binding; readonly offset: number } =>
|
|
300
|
+
writeParams(scope.device, leaseOf(box), block, values),
|
|
301
|
+
};
|
|
302
|
+
try {
|
|
303
|
+
const build = await prepareGridBuild(reduceScope, spec);
|
|
304
|
+
const pyramid = await preparePyramid(reduceScope, spec);
|
|
305
|
+
const far = await scope.pipelines.kernel(farFieldSpec(overrides));
|
|
306
|
+
const near = await scope.pipelines.kernel(nearFieldSpec(overrides));
|
|
307
|
+
const speedFinalize = await scope.pipelines.kernel(
|
|
308
|
+
kernelSpec("fa2-speed-finalize", { SWING_MODE: overrides.SWING_MODE }),
|
|
309
|
+
);
|
|
310
|
+
return new RepulsionGrid(scope, workgroupSize, overrides, spec, box, build, pyramid, {
|
|
311
|
+
far,
|
|
312
|
+
near,
|
|
313
|
+
speedFinalize,
|
|
314
|
+
});
|
|
315
|
+
} catch (error) {
|
|
316
|
+
leaseOf(box).release();
|
|
317
|
+
throw error;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Builds every bind group once per load(): the build's and the pyramid's bind() (which take their scratch and
|
|
323
|
+
* write their static params through the lease), then G6, G7 and K4 against the named buffers (the 3.10.1
|
|
324
|
+
* binding names). A second bind() takes fresh scratch from the same lease and the first bind()'s scratch stays
|
|
325
|
+
* held until dispose(): the one lease also holds the prepare-time allocations (the scan's block-sum levels), so
|
|
326
|
+
* it cannot be released on a rebind (the model creates one stage per bind(), so a rebind never happens).
|
|
327
|
+
* @param resources - the buffers of spec 7.3 plus the grid's
|
|
328
|
+
*/
|
|
329
|
+
bind(resources: RepulsionGridResources): void {
|
|
330
|
+
leaseOf(this.box);
|
|
331
|
+
const r = resources;
|
|
332
|
+
this.build.bind({
|
|
333
|
+
pos: r.pos,
|
|
334
|
+
state: r.state,
|
|
335
|
+
params: r.params,
|
|
336
|
+
cellKey: r.cellKey,
|
|
337
|
+
cellVal: r.cellVal,
|
|
338
|
+
sortedKey: r.sortedKey,
|
|
339
|
+
sortedIdx: r.sortedIdx,
|
|
340
|
+
cellHist: r.cellHist,
|
|
341
|
+
cellStart: r.cellStart,
|
|
342
|
+
});
|
|
343
|
+
this.pyramid.bind({
|
|
344
|
+
pos: r.pos,
|
|
345
|
+
params: r.params,
|
|
346
|
+
sortedIdx: r.sortedIdx,
|
|
347
|
+
cellStart: r.cellStart,
|
|
348
|
+
pyramid: r.pyramid,
|
|
349
|
+
hubList: r.hubList,
|
|
350
|
+
hubCounters: r.hubCounters,
|
|
351
|
+
hubArgs: r.hubArgs,
|
|
352
|
+
});
|
|
353
|
+
this.bound = {
|
|
354
|
+
far: this.far.bind({
|
|
355
|
+
pos: r.pos,
|
|
356
|
+
sortedIdx: r.sortedIdx,
|
|
357
|
+
pyramid: r.pyramid,
|
|
358
|
+
S: r.state,
|
|
359
|
+
force: r.force,
|
|
360
|
+
P: r.params,
|
|
361
|
+
}),
|
|
362
|
+
near: this.near.bind({
|
|
363
|
+
pos: r.pos,
|
|
364
|
+
sortedIdx: r.sortedIdx,
|
|
365
|
+
cellStart: r.cellStart,
|
|
366
|
+
S: r.state,
|
|
367
|
+
force: r.force,
|
|
368
|
+
oldForce: r.oldForce,
|
|
369
|
+
fixedMask: r.fixedMask,
|
|
370
|
+
partials: r.partials,
|
|
371
|
+
P: r.params,
|
|
372
|
+
}),
|
|
373
|
+
speedFinalize: this.speedFinalize.bind({ partials: r.partials, S: r.state, T: r.trace, P: r.params }),
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Records G1..G7 for `n` nodes with the params slot's dynamic offset, stopping after `upTo` when given (spec 7.4
|
|
379
|
+
* grid sequence; the inspect() stage split, spec 11.9 item 2): G1-G3 through the build planner, G4-G5 through the
|
|
380
|
+
* pyramid planner, then G6 and G7 over plan1d(n).
|
|
381
|
+
* @param pass - the open compute pass of the batch
|
|
382
|
+
* @param n - the node count (in [1, the capacity of the grid buffers])
|
|
383
|
+
* @param paramsOffset - the dynamic offset of this iteration's Fa2Params slot in the uniform ring
|
|
384
|
+
* @param upTo - the last grid stage to record (default "G7")
|
|
385
|
+
*/
|
|
386
|
+
recordRepulsion(pass: GPUComputePassEncoder, n: number, paramsOffset: number, upTo?: GridStage): void {
|
|
387
|
+
const bound = this.requireBound("recordRepulsion");
|
|
388
|
+
const stop = GRID_STAGES.indexOf(upTo ?? "G7");
|
|
389
|
+
let buildStop: GridBuildStage = "G3";
|
|
390
|
+
if (stop === 0) {
|
|
391
|
+
buildStop = "G1";
|
|
392
|
+
} else if (stop === 1) {
|
|
393
|
+
buildStop = "G2";
|
|
394
|
+
}
|
|
395
|
+
this.build.record(pass, n, paramsOffset, buildStop);
|
|
396
|
+
if (stop < 3) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
this.pyramid.record(pass, paramsOffset, stop === 3 ? "G4" : "G5");
|
|
400
|
+
if (stop < 5) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const plan = plan1d(n, this.workgroupSize, this.caps);
|
|
404
|
+
this.far.dispatch(pass, bound.far, plan, [paramsOffset]);
|
|
405
|
+
if (stop < 6) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
this.near.dispatch(pass, bound.near, plan, [paramsOffset]);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Records K4 only (one workgroup).
|
|
413
|
+
* @param pass - the open compute pass
|
|
414
|
+
* @param paramsOffset - the dynamic offset of the Fa2Params slot
|
|
415
|
+
*/
|
|
416
|
+
recordSpeedFinalize(pass: GPUComputePassEncoder, paramsOffset: number): void {
|
|
417
|
+
const bound = this.requireBound("recordSpeedFinalize");
|
|
418
|
+
const one = plan1d(this.speedFinalize.workgroupSize, this.speedFinalize.workgroupSize, this.caps);
|
|
419
|
+
this.speedFinalize.dispatch(pass, bound.speedFinalize, one, [paramsOffset]);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/** Releases the lease (the sort and scan scratch, the static params) and drops the bind groups; idempotent. */
|
|
423
|
+
dispose(): void {
|
|
424
|
+
if (this.box.lease !== null) {
|
|
425
|
+
this.box.lease.release();
|
|
426
|
+
this.box.lease = null;
|
|
427
|
+
}
|
|
428
|
+
for (const kernel of [this.far, this.near, this.speedFinalize]) {
|
|
429
|
+
kernel.invalidate();
|
|
430
|
+
}
|
|
431
|
+
this.bound = null;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* The bound groups, or E_NOT_LOADED when bind() has not run.
|
|
436
|
+
* @param method - the caller's name for the message
|
|
437
|
+
* @returns the bound groups
|
|
438
|
+
*/
|
|
439
|
+
private requireBound(method: string): Bound {
|
|
440
|
+
if (this.bound === null) {
|
|
441
|
+
throw new WebGpuGraphError(
|
|
442
|
+
"E_NOT_LOADED",
|
|
443
|
+
`RepulsionGrid.${method}(): bind() has not been called for this stage`,
|
|
444
|
+
{
|
|
445
|
+
state: "unbound",
|
|
446
|
+
},
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
return this.bound;
|
|
450
|
+
}
|
|
451
|
+
}
|