@graphty/webgpu-graph-algorithms 0.0.0 → 0.2.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 +344 -23
- package/dist/browser.d.ts +1 -0
- package/dist/browser.js +32 -0
- package/dist/browser.js.map +1 -0
- package/dist/chunks/context-E6iKaeuJ.js +3136 -0
- package/dist/chunks/context-E6iKaeuJ.js.map +1 -0
- package/dist/node.d.ts +1 -0
- package/dist/node.js +131 -0
- package/dist/node.js.map +1 -0
- package/dist/src/accelerator.d.ts +26 -0
- package/dist/src/accelerator.d.ts.map +1 -0
- package/dist/src/accelerator.js +101 -0
- package/dist/src/accelerator.js.map +1 -0
- package/dist/src/algorithms/degree.d.ts +35 -0
- package/dist/src/algorithms/degree.d.ts.map +1 -0
- package/dist/src/algorithms/degree.js +119 -0
- package/dist/src/algorithms/degree.js.map +1 -0
- package/dist/src/browser/index.d.ts +23 -0
- package/dist/src/browser/index.d.ts.map +1 -0
- package/dist/src/browser/index.js +48 -0
- package/dist/src/browser/index.js.map +1 -0
- package/dist/src/constants.d.ts +92 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +92 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/context.d.ts +84 -0
- package/dist/src/context.d.ts.map +1 -0
- package/dist/src/context.js +304 -0
- package/dist/src/context.js.map +1 -0
- package/dist/src/device/acquire.d.ts +57 -0
- package/dist/src/device/acquire.d.ts.map +1 -0
- package/dist/src/device/acquire.js +232 -0
- package/dist/src/device/acquire.js.map +1 -0
- package/dist/src/device/caps.d.ts +43 -0
- package/dist/src/device/caps.d.ts.map +1 -0
- package/dist/src/device/caps.js +104 -0
- package/dist/src/device/caps.js.map +1 -0
- package/dist/src/device/error-scope.d.ts +75 -0
- package/dist/src/device/error-scope.d.ts.map +1 -0
- package/dist/src/device/error-scope.js +152 -0
- package/dist/src/device/error-scope.js.map +1 -0
- package/dist/src/device/lost.d.ts +51 -0
- package/dist/src/device/lost.d.ts.map +1 -0
- package/dist/src/device/lost.js +130 -0
- package/dist/src/device/lost.js.map +1 -0
- package/dist/src/device/webgpu-constants.d.ts +31 -0
- package/dist/src/device/webgpu-constants.d.ts.map +1 -0
- package/dist/src/device/webgpu-constants.js +31 -0
- package/dist/src/device/webgpu-constants.js.map +1 -0
- package/dist/src/errors.d.ts +56 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +57 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/index.d.ts +29 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +27 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/kernel/batch.d.ts +116 -0
- package/dist/src/kernel/batch.d.ts.map +1 -0
- package/dist/src/kernel/batch.js +335 -0
- package/dist/src/kernel/batch.js.map +1 -0
- package/dist/src/kernel/dispatch.d.ts +59 -0
- package/dist/src/kernel/dispatch.d.ts.map +1 -0
- package/dist/src/kernel/dispatch.js +139 -0
- package/dist/src/kernel/dispatch.js.map +1 -0
- package/dist/src/kernel/kernel.d.ts +84 -0
- package/dist/src/kernel/kernel.d.ts.map +1 -0
- package/dist/src/kernel/kernel.js +239 -0
- package/dist/src/kernel/kernel.js.map +1 -0
- package/dist/src/kernel/pipeline-cache.d.ts +90 -0
- package/dist/src/kernel/pipeline-cache.d.ts.map +1 -0
- package/dist/src/kernel/pipeline-cache.js +251 -0
- package/dist/src/kernel/pipeline-cache.js.map +1 -0
- package/dist/src/kernel/prelude.d.ts +35 -0
- package/dist/src/kernel/prelude.d.ts.map +1 -0
- package/dist/src/kernel/prelude.js +211 -0
- package/dist/src/kernel/prelude.js.map +1 -0
- package/dist/src/kernel/profiler.d.ts +64 -0
- package/dist/src/kernel/profiler.d.ts.map +1 -0
- package/dist/src/kernel/profiler.js +120 -0
- package/dist/src/kernel/profiler.js.map +1 -0
- package/dist/src/kernel/struct-block.d.ts +122 -0
- package/dist/src/kernel/struct-block.d.ts.map +1 -0
- package/dist/src/kernel/struct-block.js +353 -0
- package/dist/src/kernel/struct-block.js.map +1 -0
- package/dist/src/kernel/uniform-ring.d.ts +70 -0
- package/dist/src/kernel/uniform-ring.d.ts.map +1 -0
- package/dist/src/kernel/uniform-ring.js +146 -0
- package/dist/src/kernel/uniform-ring.js.map +1 -0
- package/dist/src/kernel/wgsl.d.ts +88 -0
- package/dist/src/kernel/wgsl.d.ts.map +1 -0
- package/dist/src/kernel/wgsl.js +390 -0
- package/dist/src/kernel/wgsl.js.map +1 -0
- package/dist/src/kernels.d.ts +81 -0
- package/dist/src/kernels.d.ts.map +1 -0
- package/dist/src/kernels.js +417 -0
- package/dist/src/kernels.js.map +1 -0
- package/dist/src/layouts/force-simulation.d.ts +498 -0
- package/dist/src/layouts/force-simulation.d.ts.map +1 -0
- package/dist/src/layouts/force-simulation.js +1650 -0
- package/dist/src/layouts/force-simulation.js.map +1 -0
- package/dist/src/layouts/forceatlas2.d.ts +210 -0
- package/dist/src/layouts/forceatlas2.d.ts.map +1 -0
- package/dist/src/layouts/forceatlas2.js +759 -0
- package/dist/src/layouts/forceatlas2.js.map +1 -0
- package/dist/src/layouts/inputs.d.ts +40 -0
- package/dist/src/layouts/inputs.d.ts.map +1 -0
- package/dist/src/layouts/inputs.js +185 -0
- package/dist/src/layouts/inputs.js.map +1 -0
- package/dist/src/layouts/repulsion-exact.d.ts +85 -0
- package/dist/src/layouts/repulsion-exact.d.ts.map +1 -0
- package/dist/src/layouts/repulsion-exact.js +134 -0
- package/dist/src/layouts/repulsion-exact.js.map +1 -0
- package/dist/src/layouts/seed.d.ts +56 -0
- package/dist/src/layouts/seed.d.ts.map +1 -0
- package/dist/src/layouts/seed.js +173 -0
- package/dist/src/layouts/seed.js.map +1 -0
- package/dist/src/memory/buffer-pool.d.ts +73 -0
- package/dist/src/memory/buffer-pool.d.ts.map +1 -0
- package/dist/src/memory/buffer-pool.js +170 -0
- package/dist/src/memory/buffer-pool.js.map +1 -0
- package/dist/src/memory/lease.d.ts +53 -0
- package/dist/src/memory/lease.d.ts.map +1 -0
- package/dist/src/memory/lease.js +85 -0
- package/dist/src/memory/lease.js.map +1 -0
- package/dist/src/memory/readback.d.ts +143 -0
- package/dist/src/memory/readback.d.ts.map +1 -0
- package/dist/src/memory/readback.js +375 -0
- package/dist/src/memory/readback.js.map +1 -0
- package/dist/src/memory/residency.d.ts +83 -0
- package/dist/src/memory/residency.d.ts.map +1 -0
- package/dist/src/memory/residency.js +573 -0
- package/dist/src/memory/residency.js.map +1 -0
- package/dist/src/memory/upload-plan.d.ts +101 -0
- package/dist/src/memory/upload-plan.d.ts.map +1 -0
- package/dist/src/memory/upload-plan.js +265 -0
- package/dist/src/memory/upload-plan.js.map +1 -0
- package/dist/src/node/index.d.ts +64 -0
- package/dist/src/node/index.d.ts.map +1 -0
- package/dist/src/node/index.js +183 -0
- package/dist/src/node/index.js.map +1 -0
- package/dist/src/primitives/reduce.d.ts +57 -0
- package/dist/src/primitives/reduce.d.ts.map +1 -0
- package/dist/src/primitives/reduce.js +161 -0
- package/dist/src/primitives/reduce.js.map +1 -0
- package/dist/src/primitives/segmented-reduce.d.ts +38 -0
- package/dist/src/primitives/segmented-reduce.d.ts.map +1 -0
- package/dist/src/primitives/segmented-reduce.js +211 -0
- package/dist/src/primitives/segmented-reduce.js.map +1 -0
- package/dist/src/types/accelerator.d.ts +209 -0
- package/dist/src/types/accelerator.d.ts.map +1 -0
- package/dist/src/types/accelerator.js +8 -0
- package/dist/src/types/accelerator.js.map +1 -0
- package/dist/src/types/context.d.ts +114 -0
- package/dist/src/types/context.d.ts.map +1 -0
- package/dist/src/types/context.js +7 -0
- package/dist/src/types/context.js.map +1 -0
- package/dist/src/types/layout.d.ts +95 -0
- package/dist/src/types/layout.d.ts.map +1 -0
- package/dist/src/types/layout.js +6 -0
- package/dist/src/types/layout.js.map +1 -0
- package/dist/src/types/memory.d.ts +22 -0
- package/dist/src/types/memory.d.ts.map +1 -0
- package/dist/src/types/memory.js +7 -0
- package/dist/src/types/memory.js.map +1 -0
- package/dist/src/types/options.d.ts +78 -0
- package/dist/src/types/options.d.ts.map +1 -0
- package/dist/src/types/options.js +7 -0
- package/dist/src/types/options.js.map +1 -0
- package/dist/src/types/run.d.ts +13 -0
- package/dist/src/types/run.d.ts.map +1 -0
- package/dist/src/types/run.js +6 -0
- package/dist/src/types/run.js.map +1 -0
- package/dist/src/wgsl/degree.wgsl.d.ts +10 -0
- package/dist/src/wgsl/degree.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/degree.wgsl.js +25 -0
- package/dist/src/wgsl/degree.wgsl.js.map +1 -0
- package/dist/src/wgsl/fa2-attraction.wgsl.d.ts +12 -0
- package/dist/src/wgsl/fa2-attraction.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/fa2-attraction.wgsl.js +37 -0
- package/dist/src/wgsl/fa2-attraction.wgsl.js.map +1 -0
- package/dist/src/wgsl/fa2-integrate.wgsl.d.ts +13 -0
- package/dist/src/wgsl/fa2-integrate.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/fa2-integrate.wgsl.js +69 -0
- package/dist/src/wgsl/fa2-integrate.wgsl.js.map +1 -0
- package/dist/src/wgsl/fa2-repulsion-exact.wgsl.d.ts +12 -0
- package/dist/src/wgsl/fa2-repulsion-exact.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/fa2-repulsion-exact.wgsl.js +79 -0
- package/dist/src/wgsl/fa2-repulsion-exact.wgsl.js.map +1 -0
- package/dist/src/wgsl/fa2-speed-finalize.wgsl.d.ts +15 -0
- package/dist/src/wgsl/fa2-speed-finalize.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/fa2-speed-finalize.wgsl.js +54 -0
- package/dist/src/wgsl/fa2-speed-finalize.wgsl.js.map +1 -0
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts +14 -0
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.js +57 -0
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.js.map +1 -0
- package/dist/src/wgsl/fa2-to-scene.wgsl.d.ts +11 -0
- package/dist/src/wgsl/fa2-to-scene.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/fa2-to-scene.wgsl.js +19 -0
- package/dist/src/wgsl/fa2-to-scene.wgsl.js.map +1 -0
- package/dist/src/wgsl/fill.wgsl.d.ts +7 -0
- package/dist/src/wgsl/fill.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/fill.wgsl.js +14 -0
- package/dist/src/wgsl/fill.wgsl.js.map +1 -0
- package/dist/src/wgsl/reduce.wgsl.d.ts +10 -0
- package/dist/src/wgsl/reduce.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/reduce.wgsl.js +63 -0
- package/dist/src/wgsl/reduce.wgsl.js.map +1 -0
- package/dist/src/wgsl/segmented-reduce.wgsl.d.ts +13 -0
- package/dist/src/wgsl/segmented-reduce.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/segmented-reduce.wgsl.js +35 -0
- package/dist/src/wgsl/segmented-reduce.wgsl.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/dist/webgpu-graph-algorithms.d.ts +1 -0
- package/dist/webgpu-graph-algorithms.js +4454 -0
- package/dist/webgpu-graph-algorithms.js.map +1 -0
- package/package.json +108 -17
- package/src/accelerator.ts +117 -0
- package/src/algorithms/degree.ts +142 -0
- package/src/browser/index.ts +57 -0
- package/src/constants.ts +116 -0
- package/src/context.ts +399 -0
- package/src/device/acquire.ts +256 -0
- package/src/device/caps.ts +122 -0
- package/src/device/error-scope.ts +171 -0
- package/src/device/lost.ts +142 -0
- package/src/device/webgpu-constants.ts +44 -0
- package/src/errors.ts +94 -0
- package/src/index.ts +102 -0
- package/src/kernel/batch.ts +427 -0
- package/src/kernel/dispatch.ts +162 -0
- package/src/kernel/kernel.ts +311 -0
- package/src/kernel/pipeline-cache.ts +288 -0
- package/src/kernel/prelude.ts +229 -0
- package/src/kernel/profiler.ts +148 -0
- package/src/kernel/struct-block.ts +439 -0
- package/src/kernel/uniform-ring.ts +184 -0
- package/src/kernel/wgsl.ts +490 -0
- package/src/kernels.ts +511 -0
- package/src/layouts/force-simulation.ts +2111 -0
- package/src/layouts/forceatlas2.ts +942 -0
- package/src/layouts/inputs.ts +252 -0
- package/src/layouts/repulsion-exact.ts +183 -0
- package/src/layouts/seed.ts +198 -0
- package/src/memory/buffer-pool.ts +204 -0
- package/src/memory/lease.ts +93 -0
- package/src/memory/readback.ts +429 -0
- package/src/memory/residency.ts +753 -0
- package/src/memory/upload-plan.ts +350 -0
- package/src/node/index.ts +230 -0
- package/src/primitives/reduce.ts +233 -0
- package/src/primitives/segmented-reduce.ts +270 -0
- package/src/types/accelerator.ts +236 -0
- package/src/types/context.ts +135 -0
- package/src/types/layout.ts +103 -0
- package/src/types/memory.ts +23 -0
- package/src/types/options.ts +84 -0
- package/src/types/run.ts +13 -0
- package/src/wgsl/degree.wgsl.ts +24 -0
- package/src/wgsl/fa2-attraction.wgsl.ts +37 -0
- package/src/wgsl/fa2-integrate.wgsl.ts +69 -0
- package/src/wgsl/fa2-repulsion-exact.wgsl.ts +78 -0
- package/src/wgsl/fa2-speed-finalize.wgsl.ts +53 -0
- package/src/wgsl/fa2-stats-finalize.wgsl.ts +57 -0
- package/src/wgsl/fa2-to-scene.wgsl.ts +19 -0
- package/src/wgsl/fill.wgsl.ts +13 -0
- package/src/wgsl/reduce.wgsl.ts +62 -0
- package/src/wgsl/segmented-reduce.wgsl.ts +35 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PipelineCache (spec 5.1; contract 3.9): compile-once compute pipelines keyed by
|
|
3
|
+
* `(id, overrides, needs present on the device, snippets)` with EXPLICIT bind-group layouts derived from
|
|
4
|
+
* `spec.bindings` (never `layout: "auto"`, so one layout serves every dummy-binding variant and bind groups are
|
|
5
|
+
* reused across variants). A WGSL error is `E_SHADER_COMPILE { stage: "compile", messages, lines }` with
|
|
6
|
+
* body-relative line numbers; a pipeline error that is not a compilation message (an oversized workgroup, an
|
|
7
|
+
* invalid layout) is `E_VALIDATION`.
|
|
8
|
+
*/
|
|
9
|
+
import { formatCompilationInfo } from "../device/error-scope.js";
|
|
10
|
+
import { WebGpuGraphError } from "../errors.js";
|
|
11
|
+
import { Kernel } from "./kernel.js";
|
|
12
|
+
import { bindGroupLayoutDescriptors, composeWgsl } from "./wgsl.js";
|
|
13
|
+
/**
|
|
14
|
+
* JSON with the keys of every (nested) object sorted, so two override records with the same content give one key.
|
|
15
|
+
* @param value - a JSON-compatible value
|
|
16
|
+
* @returns the canonical text
|
|
17
|
+
*/
|
|
18
|
+
function stableJson(value) {
|
|
19
|
+
if (Array.isArray(value)) {
|
|
20
|
+
return `[${value.map((item) => stableJson(item)).join(",")}]`;
|
|
21
|
+
}
|
|
22
|
+
if (typeof value === "object" && value !== null) {
|
|
23
|
+
const record = value;
|
|
24
|
+
const keys = Object.keys(record).sort();
|
|
25
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`).join(",")}}`;
|
|
26
|
+
}
|
|
27
|
+
return JSON.stringify(value);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* FNV-1a (32-bit) of a text as 8 hex digits; "0" for an absent or empty snippet record. Hashes text, never an arc
|
|
31
|
+
* index or a byte offset, so the bitwise operators are within the house rule.
|
|
32
|
+
* @param snippets - the snippet record
|
|
33
|
+
* @returns the hash text
|
|
34
|
+
*/
|
|
35
|
+
function hashSnippets(snippets) {
|
|
36
|
+
if (snippets === undefined || Object.keys(snippets).length === 0) {
|
|
37
|
+
return "0";
|
|
38
|
+
}
|
|
39
|
+
const text = stableJson(snippets);
|
|
40
|
+
let hash = 0x811c9dc5;
|
|
41
|
+
for (let i = 0; i < text.length; i++) {
|
|
42
|
+
hash ^= text.charCodeAt(i);
|
|
43
|
+
hash = Math.imul(hash, 0x01000193) >>> 0;
|
|
44
|
+
}
|
|
45
|
+
return hash.toString(16).padStart(8, "0");
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The cache key of a spec on a device (pure; PLAN DECISION: exported so the override-matrix helper of P2-T2 can
|
|
49
|
+
* compute keys without a device): `id + "|" + stableJson(overrides) + "|" + needs.filter(present).join(",") + "|" + hash(snippets)`.
|
|
50
|
+
* @param spec - the module
|
|
51
|
+
* @param caps - the device capabilities (the feature set decides which needs are present)
|
|
52
|
+
* @returns the key
|
|
53
|
+
*/
|
|
54
|
+
export function pipelineKey(spec, caps) {
|
|
55
|
+
const present = spec.needs.filter((need) => caps.features.has(need)).join(",");
|
|
56
|
+
return `${spec.id}|${stableJson(spec.overrides)}|${present}|${hashSnippets(spec.snippets)}`;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The message of an unknown error value.
|
|
60
|
+
* @param err - the thrown value
|
|
61
|
+
* @returns its message text
|
|
62
|
+
*/
|
|
63
|
+
function messageOf(err) {
|
|
64
|
+
return err instanceof Error ? err.message : String(err);
|
|
65
|
+
}
|
|
66
|
+
/** Compile-once pipelines keyed by (id, overrides, needs present on the device, snippets) with explicit bind-group layouts (spec 5.1). */
|
|
67
|
+
export class PipelineCache {
|
|
68
|
+
/**
|
|
69
|
+
* Builds a cache over a device.
|
|
70
|
+
* @param device - the device pipelines are created on
|
|
71
|
+
* @param caps - its capabilities (WG, subgroup sizes, features)
|
|
72
|
+
*/
|
|
73
|
+
constructor(device, caps) {
|
|
74
|
+
/** In-flight and finished compiles by key (a promise, so two concurrent get() calls share one compile). */
|
|
75
|
+
this.pending = new Map();
|
|
76
|
+
/** Finished compiles by key. */
|
|
77
|
+
this.entries = new Map();
|
|
78
|
+
/** Keys in the order their compiles were started (a failed compile is removed again). */
|
|
79
|
+
this.order = [];
|
|
80
|
+
this.disposed = false;
|
|
81
|
+
this.device = device;
|
|
82
|
+
this.caps = caps;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The cache key of a spec on this device: `id + "|" + stableJson(overrides) + "|" + needs.filter(present).join(",") + "|" + hash(snippets)`.
|
|
86
|
+
* @param spec - the module
|
|
87
|
+
* @returns the key
|
|
88
|
+
*/
|
|
89
|
+
key(spec) {
|
|
90
|
+
return pipelineKey(spec, this.caps);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* createComputePipelineAsync inside a validation scope with the layouts derived from spec.bindings; compilation messages become E_SHADER_COMPILE { id, stage: "compile", messages } with body-relative lines.
|
|
94
|
+
* @param spec - the module
|
|
95
|
+
* @returns the pipeline (the same object for the same key)
|
|
96
|
+
*/
|
|
97
|
+
async get(spec) {
|
|
98
|
+
return (await this.entry(spec)).pipeline;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The Kernel (pipeline + layouts + binding names) for a spec; cached with the pipeline.
|
|
102
|
+
* @param spec - the module
|
|
103
|
+
* @returns the kernel (the same object for the same key)
|
|
104
|
+
*/
|
|
105
|
+
async kernel(spec) {
|
|
106
|
+
return (await this.entry(spec)).kernel;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Compiles every spec not yet cached (load() calls it so the first step() does not compile).
|
|
110
|
+
* @param specs - the modules
|
|
111
|
+
*/
|
|
112
|
+
async warm(specs) {
|
|
113
|
+
await Promise.all(specs.map((spec) => this.entry(spec)));
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Number of cached pipelines.
|
|
117
|
+
* @returns the count of finished compiles
|
|
118
|
+
*/
|
|
119
|
+
get size() {
|
|
120
|
+
return this.entries.size;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Every key created so far, in creation order (the override-matrix coverage test reads it).
|
|
124
|
+
* PLAN DECISION: keys are listed in compile-START order and only those whose compile finished (warm() compiles
|
|
125
|
+
* concurrently, so completion order is not deterministic; a failed compile is removed).
|
|
126
|
+
* @returns the keys
|
|
127
|
+
* @internal
|
|
128
|
+
*/
|
|
129
|
+
keys() {
|
|
130
|
+
return this.order.filter((key) => this.entries.has(key));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The bind-group layouts of a cached kernel by key.
|
|
134
|
+
* @param key - a key from keys()
|
|
135
|
+
* @returns the layouts, or null when the key is not cached
|
|
136
|
+
* @internal
|
|
137
|
+
*/
|
|
138
|
+
layoutsOf(key) {
|
|
139
|
+
return this.entries.get(key)?.layouts ?? null;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* PLAN DECISION: forgets every pipeline and refuses further compiles with E_DISPOSED (the context's dispose()
|
|
143
|
+
* may call it; a GPUComputePipeline has no destroy(), so the objects are simply released).
|
|
144
|
+
*/
|
|
145
|
+
dispose() {
|
|
146
|
+
this.disposed = true;
|
|
147
|
+
this.pending.clear();
|
|
148
|
+
this.entries.clear();
|
|
149
|
+
this.order.length = 0;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The cached entry of a spec, compiling it once per key.
|
|
153
|
+
* @param spec - the module
|
|
154
|
+
* @returns the entry
|
|
155
|
+
*/
|
|
156
|
+
entry(spec) {
|
|
157
|
+
if (this.disposed) {
|
|
158
|
+
return Promise.reject(new WebGpuGraphError("E_DISPOSED", "PipelineCache: disposed", { label: "PipelineCache" }));
|
|
159
|
+
}
|
|
160
|
+
const key = this.key(spec);
|
|
161
|
+
const inFlight = this.pending.get(key);
|
|
162
|
+
if (inFlight !== undefined) {
|
|
163
|
+
return inFlight;
|
|
164
|
+
}
|
|
165
|
+
this.order.push(key);
|
|
166
|
+
const compile = this.compile(spec, key).then((entry) => {
|
|
167
|
+
if (!this.disposed) {
|
|
168
|
+
this.entries.set(key, entry);
|
|
169
|
+
}
|
|
170
|
+
return entry;
|
|
171
|
+
}, (err) => {
|
|
172
|
+
this.pending.delete(key);
|
|
173
|
+
const at = this.order.indexOf(key);
|
|
174
|
+
if (at >= 0) {
|
|
175
|
+
this.order.splice(at, 1);
|
|
176
|
+
}
|
|
177
|
+
throw err;
|
|
178
|
+
});
|
|
179
|
+
this.pending.set(key, compile);
|
|
180
|
+
return compile;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* One compile: compose, create the shader module inside a synchronous push / pop validation pair (so concurrent
|
|
184
|
+
* compiles never interleave scopes), check the compilation info, create the layouts and the pipeline.
|
|
185
|
+
* PLAN DECISION: `E_SHADER_COMPILE` at stage "compile" also carries `lines` (the body-relative 1-based line of every
|
|
186
|
+
* error message, from `composed.bodyLine`) next to the contract's `messages`; a `createComputePipelineAsync`
|
|
187
|
+
* rejection (a GPUPipelineError, which pushes nothing into a validation scope on Dawn 0.4.0) is
|
|
188
|
+
* `E_VALIDATION { label: "<id>|pipeline", message }`; bool overrides travel as the pipeline constants 1 / 0
|
|
189
|
+
* because dawn.node's WebIDL layer rejects a boolean constant.
|
|
190
|
+
* @param spec - the module
|
|
191
|
+
* @param key - its key (the pipeline label)
|
|
192
|
+
* @returns the entry
|
|
193
|
+
*/
|
|
194
|
+
async compile(spec, key) {
|
|
195
|
+
const composed = composeWgsl(spec, this.caps);
|
|
196
|
+
const { device } = this;
|
|
197
|
+
const label = `${spec.id}|pipeline`;
|
|
198
|
+
device.pushErrorScope("validation");
|
|
199
|
+
const module = device.createShaderModule({ code: composed.code, label: spec.id });
|
|
200
|
+
const layouts = bindGroupLayoutDescriptors(spec).map((descriptor, group) => device.createBindGroupLayout({ ...descriptor, label: `${spec.id}/layout${group}` }));
|
|
201
|
+
const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: layouts, label: `${spec.id}|layout` });
|
|
202
|
+
const popped = device.popErrorScope();
|
|
203
|
+
const info = await module.getCompilationInfo();
|
|
204
|
+
const scopeError = await popped;
|
|
205
|
+
const errors = info.messages.filter((message) => message.type === "error");
|
|
206
|
+
if (errors.length > 0) {
|
|
207
|
+
const preludeLines = composed.bodyLine - 1;
|
|
208
|
+
throw new WebGpuGraphError("E_SHADER_COMPILE", `${spec.id}: WGSL compilation failed (${errors.length} error(s))`, {
|
|
209
|
+
id: spec.id,
|
|
210
|
+
stage: "compile",
|
|
211
|
+
messages: formatCompilationInfo(info, preludeLines),
|
|
212
|
+
lines: errors.map((message) => message.lineNum - preludeLines),
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
if (scopeError !== null) {
|
|
216
|
+
throw new WebGpuGraphError("E_VALIDATION", `${spec.id}: ${scopeError.message}`, {
|
|
217
|
+
label,
|
|
218
|
+
message: scopeError.message,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
// dawn.node's WebIDL layer rejects a boolean pipeline constant ("value is not a number"), so bools travel as 1 / 0;
|
|
222
|
+
// only the REFERENCED overrides are supplied (composed.constants): WebKit rejects a constant for an unread override
|
|
223
|
+
const constants = {};
|
|
224
|
+
for (const [name, value] of Object.entries(composed.constants)) {
|
|
225
|
+
if (typeof value === "boolean") {
|
|
226
|
+
constants[name] = value ? 1 : 0;
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
constants[name] = value;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
let pipeline;
|
|
233
|
+
try {
|
|
234
|
+
pipeline = await device.createComputePipelineAsync({
|
|
235
|
+
label: key,
|
|
236
|
+
layout: pipelineLayout,
|
|
237
|
+
compute: { module, entryPoint: composed.entryPoint, constants },
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
const message = messageOf(err);
|
|
242
|
+
throw new WebGpuGraphError("E_VALIDATION", `${spec.id}: pipeline creation failed: ${message}`, {
|
|
243
|
+
label,
|
|
244
|
+
message,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
const kernel = new Kernel(device, spec, composed, pipeline, layouts);
|
|
248
|
+
return { key, composed, pipeline, layouts, kernel };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=pipeline-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline-cache.js","sourceRoot":"","sources":["../../../src/kernel/pipeline-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The WGSL prelude every composed module receives first (contract 4.1), the two reduction-helper blocks the
|
|
3
|
+
* composer splices for a body whose spec lists `needs: ["subgroups"]` (contract 4.3, D16), and the WGSL
|
|
4
|
+
* reserved-word list the composer rejects identifiers from (contract 3.9).
|
|
5
|
+
*
|
|
6
|
+
* CONTRACT DECISION (3.9): this text lives in the kernel layer, not in `src/wgsl/`, because the composer must own
|
|
7
|
+
* what it splices and `src/wgsl/` then holds kernel BODIES only. Every numeric constant is interpolated from
|
|
8
|
+
* `constants.ts` and graph-format's `INVALID_INDEX`; the literal-grep test of spec 3.5 (`test/kernel/wgsl.test.ts`)
|
|
9
|
+
* covers this file's template, so no u32 sentinel or workgroup-size literal is ever typed here.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Formats a finite number as a WGSL f32 literal that always carries a decimal point or an exponent (`0.01`,
|
|
13
|
+
* `1e-8`, `2.0`), so an integral value never becomes an abstract-int literal (contract 4.1).
|
|
14
|
+
* @param value - the number to format
|
|
15
|
+
* @returns the literal text
|
|
16
|
+
*/
|
|
17
|
+
export declare function wgslF32Literal(value: number): string;
|
|
18
|
+
/** The prelude text with the constants interpolated from constants.ts and graph-format's INVALID_INDEX (4.1). */
|
|
19
|
+
export declare const PRELUDE_WGSL: string;
|
|
20
|
+
/** Line count of PRELUDE_WGSL (the compilation-info formatter subtracts it plus the emitted declarations). */
|
|
21
|
+
export declare const PRELUDE_LINES: number;
|
|
22
|
+
/** The workgroup-memory reduction helpers (the twin) (4.3). */
|
|
23
|
+
export declare const REDUCE_HELPERS_WORKGROUP_WGSL: string;
|
|
24
|
+
/** The subgroup reduction helpers, spliced only with `enable subgroups;` (4.3). */
|
|
25
|
+
export declare const REDUCE_HELPERS_SUBGROUP_WGSL: string;
|
|
26
|
+
/** The helper function names a body may call when its spec lists needs: ["subgroups"]. */
|
|
27
|
+
export declare const REDUCE_HELPER_NAMES: readonly ["wg_reduce_f32", "wg_reduce_u32", "wg_reduce_vec4"];
|
|
28
|
+
/**
|
|
29
|
+
* The WGSL reserved words of spec section 16.2 (the `_reserved` production), frozen; composeWgsl rejects a body or
|
|
30
|
+
* snippet that uses one (3.9). PLAN DECISION: the list is the 146 words of the W3C Candidate Recommendation Draft
|
|
31
|
+
* of 2026-08-31 (identical to the editor's draft of 2026-09-01); the contract's count of 147 predates the removal
|
|
32
|
+
* of `binding_array`. `free`, `valid`, `tile`, `slot`, `key` and `count` are NOT reserved.
|
|
33
|
+
*/
|
|
34
|
+
export declare const WGSL_RESERVED_WORDS: readonly string[];
|
|
35
|
+
//# sourceMappingURL=prelude.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prelude.d.ts","sourceRoot":"","sources":["../../../src/kernel/prelude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAeH;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUpD;AAED,iHAAiH;AACjH,eAAO,MAAM,YAAY,EAAE,MAwCzB,CAAC;AAEH,8GAA8G;AAC9G,eAAO,MAAM,aAAa,EAAE,MAAwC,CAAC;AAErE,+DAA+D;AAC/D,eAAO,MAAM,6BAA6B,EAAE,MAoCsE,CAAC;AAEnH,mFAAmF;AACnF,eAAO,MAAM,4BAA4B,EAAE,MA2EuE,CAAC;AAEnH,0FAA0F;AAC1F,eAAO,MAAM,mBAAmB,+DAAgE,CAAC;AAEjG;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAchD,CAAC"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The WGSL prelude every composed module receives first (contract 4.1), the two reduction-helper blocks the
|
|
3
|
+
* composer splices for a body whose spec lists `needs: ["subgroups"]` (contract 4.3, D16), and the WGSL
|
|
4
|
+
* reserved-word list the composer rejects identifiers from (contract 3.9).
|
|
5
|
+
*
|
|
6
|
+
* CONTRACT DECISION (3.9): this text lives in the kernel layer, not in `src/wgsl/`, because the composer must own
|
|
7
|
+
* what it splices and `src/wgsl/` then holds kernel BODIES only. Every numeric constant is interpolated from
|
|
8
|
+
* `constants.ts` and graph-format's `INVALID_INDEX`; the literal-grep test of spec 3.5 (`test/kernel/wgsl.test.ts`)
|
|
9
|
+
* covers this file's template, so no u32 sentinel or workgroup-size literal is ever typed here.
|
|
10
|
+
*/
|
|
11
|
+
import { INVALID_INDEX } from "@graphty/graph-format";
|
|
12
|
+
import { FA2_COINCIDENT_SQ, FA2_DISTANCE_FLOOR, FA2_DISTANCE_FLOOR_SQ, FA2_FLAG_FIRST, MAX_WORKGROUPS_PER_DIM, U32_MAX, WORKGROUP_SIZE, } from "../constants.js";
|
|
13
|
+
import { WebGpuGraphError } from "../errors.js";
|
|
14
|
+
/**
|
|
15
|
+
* Formats a finite number as a WGSL f32 literal that always carries a decimal point or an exponent (`0.01`,
|
|
16
|
+
* `1e-8`, `2.0`), so an integral value never becomes an abstract-int literal (contract 4.1).
|
|
17
|
+
* @param value - the number to format
|
|
18
|
+
* @returns the literal text
|
|
19
|
+
*/
|
|
20
|
+
export function wgslF32Literal(value) {
|
|
21
|
+
if (!Number.isFinite(value)) {
|
|
22
|
+
throw new WebGpuGraphError("E_INVALID_ARGUMENT", `wgslF32Literal: ${value} is not a finite number`, {
|
|
23
|
+
argument: "value",
|
|
24
|
+
value,
|
|
25
|
+
expected: "a finite number",
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const text = String(value);
|
|
29
|
+
return /[.e]/.test(text) ? text : `${text}.0`;
|
|
30
|
+
}
|
|
31
|
+
/** The prelude text with the constants interpolated from constants.ts and graph-format's INVALID_INDEX (4.1). */
|
|
32
|
+
export const PRELUDE_WGSL = /* wgsl */ `// ---- prelude: constants, standard overrides, helpers (every module receives this text first)
|
|
33
|
+
const INVALID_INDEX: u32 = ${INVALID_INDEX}u;
|
|
34
|
+
const U32_MAX: u32 = ${U32_MAX}u;
|
|
35
|
+
const MAX_WORKGROUPS_PER_DIM: u32 = ${MAX_WORKGROUPS_PER_DIM}u;
|
|
36
|
+
const FA2_DIST_FLOOR: f32 = ${wgslF32Literal(FA2_DISTANCE_FLOOR)};
|
|
37
|
+
const FA2_DIST_FLOOR_SQ: f32 = ${wgslF32Literal(FA2_DISTANCE_FLOOR_SQ)};
|
|
38
|
+
const FA2_COINCIDENT_SQ: f32 = ${wgslF32Literal(FA2_COINCIDENT_SQ)};
|
|
39
|
+
const FA2_FLAG_FIRST: u32 = ${FA2_FLAG_FIRST}u;
|
|
40
|
+
const F32_MAX: f32 = 0x1.fffffep+127;
|
|
41
|
+
override WG: u32 = ${WORKGROUP_SIZE}u;
|
|
42
|
+
override USE_PERM: bool = false;
|
|
43
|
+
override HAS_WEIGHTS: bool = false;
|
|
44
|
+
override SUBGROUP_MIN: u32 = 4u;
|
|
45
|
+
override SUBGROUP_MAX: u32 = 0u;
|
|
46
|
+
|
|
47
|
+
fn linear_id(wid: vec3<u32>, lid: u32) -> u32 { return (wid.x + wid.y * MAX_WORKGROUPS_PER_DIM) * WG + lid; }
|
|
48
|
+
fn group_id(wid: vec3<u32>) -> u32 { return wid.x + wid.y * MAX_WORKGROUPS_PER_DIM; }
|
|
49
|
+
fn lowbias32(x0: u32) -> u32 {
|
|
50
|
+
var x = x0;
|
|
51
|
+
x = x ^ (x >> 16u);
|
|
52
|
+
x = x * 0x7feb352du;
|
|
53
|
+
x = x ^ (x >> 15u);
|
|
54
|
+
x = x * 0x846ca68bu;
|
|
55
|
+
x = x ^ (x >> 16u);
|
|
56
|
+
return x;
|
|
57
|
+
}
|
|
58
|
+
fn mask_bit(w: u32, i: u32) -> bool { return ((w >> (i & 31u)) & 1u) == 1u; }
|
|
59
|
+
fn unpack_u8(w: u32, i: u32) -> u32 { return (w >> (8u * (i & 3u))) & 0xFFu; }
|
|
60
|
+
fn pair_hash(i: u32, j: u32) -> u32 { return lowbias32((min(i, j) * 0x9E3779B9u) ^ max(i, j)); }
|
|
61
|
+
fn hash_unit(h: u32) -> f32 { return f32(h >> 8u) * (1.0 / 16777216.0); }
|
|
62
|
+
fn hash_dir(h: u32, dim: u32) -> vec3f {
|
|
63
|
+
let phi = 6.283185307179586 * hash_unit(h);
|
|
64
|
+
if (dim == 2u) { return vec3f(cos(phi), sin(phi), 0.0); }
|
|
65
|
+
let z = 2.0 * hash_unit(lowbias32(h ^ 0x5bd1e995u)) - 1.0;
|
|
66
|
+
let r = sqrt(max(0.0, 1.0 - z * z));
|
|
67
|
+
return vec3f(r * cos(phi), r * sin(phi), z);
|
|
68
|
+
}
|
|
69
|
+
fn kick_dir(i: u32, j: u32, dim: u32) -> vec3f {
|
|
70
|
+
let d = hash_dir(pair_hash(i, j), dim);
|
|
71
|
+
return select(d, -d, i > j);
|
|
72
|
+
}`;
|
|
73
|
+
/** Line count of PRELUDE_WGSL (the compilation-info formatter subtracts it plus the emitted declarations). */
|
|
74
|
+
export const PRELUDE_LINES = PRELUDE_WGSL.split("\n").length;
|
|
75
|
+
/** The workgroup-memory reduction helpers (the twin) (4.3). */
|
|
76
|
+
export const REDUCE_HELPERS_WORKGROUP_WGSL = /* wgsl */ `var<workgroup> wg_scratch_v: array<vec4f, WG>;
|
|
77
|
+
var<workgroup> wg_scratch_u: array<u32, WG>;
|
|
78
|
+
fn combine_v(a: vec4f, b: vec4f, op: u32) -> vec4f {
|
|
79
|
+
if (op == 1u) { return min(a, b); }
|
|
80
|
+
if (op == 2u) { return max(a, b); }
|
|
81
|
+
return a + b;
|
|
82
|
+
}
|
|
83
|
+
fn combine_u(a: u32, b: u32, op: u32) -> u32 {
|
|
84
|
+
if (op == 1u) { return min(a, b); }
|
|
85
|
+
if (op == 2u) { return max(a, b); }
|
|
86
|
+
return a + b;
|
|
87
|
+
}
|
|
88
|
+
fn wg_reduce_vec4(v: vec4f, lid: u32, op: u32) -> vec4f {
|
|
89
|
+
workgroupBarrier();
|
|
90
|
+
wg_scratch_v[lid] = v;
|
|
91
|
+
workgroupBarrier();
|
|
92
|
+
for (var s = WG / 2u; s > 0u; s = s >> 1u) {
|
|
93
|
+
if (lid < s) { wg_scratch_v[lid] = combine_v(wg_scratch_v[lid], wg_scratch_v[lid + s], op); }
|
|
94
|
+
workgroupBarrier();
|
|
95
|
+
}
|
|
96
|
+
let total = wg_scratch_v[0];
|
|
97
|
+
workgroupBarrier();
|
|
98
|
+
return total;
|
|
99
|
+
}
|
|
100
|
+
fn wg_reduce_u32(v: u32, lid: u32, op: u32) -> u32 {
|
|
101
|
+
workgroupBarrier();
|
|
102
|
+
wg_scratch_u[lid] = v;
|
|
103
|
+
workgroupBarrier();
|
|
104
|
+
for (var s = WG / 2u; s > 0u; s = s >> 1u) {
|
|
105
|
+
if (lid < s) { wg_scratch_u[lid] = combine_u(wg_scratch_u[lid], wg_scratch_u[lid + s], op); }
|
|
106
|
+
workgroupBarrier();
|
|
107
|
+
}
|
|
108
|
+
let total = wg_scratch_u[0];
|
|
109
|
+
workgroupBarrier();
|
|
110
|
+
return total;
|
|
111
|
+
}
|
|
112
|
+
fn wg_reduce_f32(v: f32, lid: u32, op: u32) -> f32 { return wg_reduce_vec4(vec4f(v, 0.0, 0.0, 0.0), lid, op).x; }`;
|
|
113
|
+
/** The subgroup reduction helpers, spliced only with `enable subgroups;` (4.3). */
|
|
114
|
+
export const REDUCE_HELPERS_SUBGROUP_WGSL = /* wgsl */ `override SG_SLOTS: u32 = (WG + SUBGROUP_MIN - 1u) / SUBGROUP_MIN; // one slot per subgroup; the count is largest when the compiler picks the SMALLEST size
|
|
115
|
+
var<workgroup> sg_counter: atomic<u32>;
|
|
116
|
+
var<workgroup> sg_val_v: array<vec4f, SG_SLOTS>;
|
|
117
|
+
var<workgroup> sg_val_u: array<u32, SG_SLOTS>;
|
|
118
|
+
var<workgroup> sg_key: array<u32, SG_SLOTS>;
|
|
119
|
+
var<workgroup> sg_sorted_v: array<vec4f, SG_SLOTS>;
|
|
120
|
+
var<workgroup> sg_sorted_u: array<u32, SG_SLOTS>;
|
|
121
|
+
fn combine_v(a: vec4f, b: vec4f, op: u32) -> vec4f {
|
|
122
|
+
if (op == 1u) { return min(a, b); }
|
|
123
|
+
if (op == 2u) { return max(a, b); }
|
|
124
|
+
return a + b;
|
|
125
|
+
}
|
|
126
|
+
fn combine_u(a: u32, b: u32, op: u32) -> u32 {
|
|
127
|
+
if (op == 1u) { return min(a, b); }
|
|
128
|
+
if (op == 2u) { return max(a, b); }
|
|
129
|
+
return a + b;
|
|
130
|
+
}
|
|
131
|
+
fn wg_reduce_vec4(v: vec4f, lid: u32, op: u32) -> vec4f {
|
|
132
|
+
workgroupBarrier();
|
|
133
|
+
if (lid == 0u) { atomicStore(&sg_counter, 0u); }
|
|
134
|
+
workgroupBarrier();
|
|
135
|
+
let s_add = subgroupAdd(v);
|
|
136
|
+
let s_min = subgroupMin(v);
|
|
137
|
+
let s_max = subgroupMax(v);
|
|
138
|
+
var partial = s_add;
|
|
139
|
+
if (op == 1u) { partial = s_min; }
|
|
140
|
+
if (op == 2u) { partial = s_max; }
|
|
141
|
+
let key = subgroupMin(lid); // the smallest local id of this subgroup: a stable identity without @builtin(subgroup_id)
|
|
142
|
+
var slot = 0u;
|
|
143
|
+
if (subgroupElect()) { slot = atomicAdd(&sg_counter, 1u); } // D16: the elected lane takes a slot from the counter
|
|
144
|
+
slot = subgroupBroadcast(slot, 0u); // and broadcasts it (lane 0 is the elected lane in uniform control flow)
|
|
145
|
+
if (subgroupElect()) { sg_val_v[slot] = partial; sg_key[slot] = key; }
|
|
146
|
+
workgroupBarrier();
|
|
147
|
+
let count = atomicLoad(&sg_counter);
|
|
148
|
+
if (lid < count) { // rank the slots by key so the final sum has a fixed order (11.9 item 4)
|
|
149
|
+
let mine = sg_key[lid];
|
|
150
|
+
var rank = 0u;
|
|
151
|
+
for (var k = 0u; k < count; k = k + 1u) { if (sg_key[k] < mine) { rank = rank + 1u; } }
|
|
152
|
+
sg_sorted_v[rank] = sg_val_v[lid];
|
|
153
|
+
}
|
|
154
|
+
workgroupBarrier();
|
|
155
|
+
var total = sg_sorted_v[0];
|
|
156
|
+
for (var k = 1u; k < count; k = k + 1u) { total = combine_v(total, sg_sorted_v[k], op); }
|
|
157
|
+
workgroupBarrier();
|
|
158
|
+
return total;
|
|
159
|
+
}
|
|
160
|
+
fn wg_reduce_u32(v: u32, lid: u32, op: u32) -> u32 {
|
|
161
|
+
workgroupBarrier();
|
|
162
|
+
if (lid == 0u) { atomicStore(&sg_counter, 0u); }
|
|
163
|
+
workgroupBarrier();
|
|
164
|
+
let s_add = subgroupAdd(v);
|
|
165
|
+
let s_min = subgroupMin(v);
|
|
166
|
+
let s_max = subgroupMax(v);
|
|
167
|
+
var partial = s_add;
|
|
168
|
+
if (op == 1u) { partial = s_min; }
|
|
169
|
+
if (op == 2u) { partial = s_max; }
|
|
170
|
+
let key = subgroupMin(lid);
|
|
171
|
+
var slot = 0u;
|
|
172
|
+
if (subgroupElect()) { slot = atomicAdd(&sg_counter, 1u); }
|
|
173
|
+
slot = subgroupBroadcast(slot, 0u);
|
|
174
|
+
if (subgroupElect()) { sg_val_u[slot] = partial; sg_key[slot] = key; }
|
|
175
|
+
workgroupBarrier();
|
|
176
|
+
let count = atomicLoad(&sg_counter);
|
|
177
|
+
if (lid < count) {
|
|
178
|
+
let mine = sg_key[lid];
|
|
179
|
+
var rank = 0u;
|
|
180
|
+
for (var k = 0u; k < count; k = k + 1u) { if (sg_key[k] < mine) { rank = rank + 1u; } }
|
|
181
|
+
sg_sorted_u[rank] = sg_val_u[lid];
|
|
182
|
+
}
|
|
183
|
+
workgroupBarrier();
|
|
184
|
+
var total = sg_sorted_u[0];
|
|
185
|
+
for (var k = 1u; k < count; k = k + 1u) { total = combine_u(total, sg_sorted_u[k], op); }
|
|
186
|
+
workgroupBarrier();
|
|
187
|
+
return total;
|
|
188
|
+
}
|
|
189
|
+
fn wg_reduce_f32(v: f32, lid: u32, op: u32) -> f32 { return wg_reduce_vec4(vec4f(v, 0.0, 0.0, 0.0), lid, op).x; }`;
|
|
190
|
+
/** The helper function names a body may call when its spec lists needs: ["subgroups"]. */
|
|
191
|
+
export const REDUCE_HELPER_NAMES = ["wg_reduce_f32", "wg_reduce_u32", "wg_reduce_vec4"];
|
|
192
|
+
/**
|
|
193
|
+
* The WGSL reserved words of spec section 16.2 (the `_reserved` production), frozen; composeWgsl rejects a body or
|
|
194
|
+
* snippet that uses one (3.9). PLAN DECISION: the list is the 146 words of the W3C Candidate Recommendation Draft
|
|
195
|
+
* of 2026-08-31 (identical to the editor's draft of 2026-09-01); the contract's count of 147 predates the removal
|
|
196
|
+
* of `binding_array`. `free`, `valid`, `tile`, `slot`, `key` and `count` are NOT reserved.
|
|
197
|
+
*/
|
|
198
|
+
export const WGSL_RESERVED_WORDS = Object.freeze(`NULL Self abstract active alignas alignof as asm asm_fragment async attribute auto await become cast catch class
|
|
199
|
+
co_await co_return co_yield coherent column_major common compile compile_fragment concept const_cast consteval
|
|
200
|
+
constexpr constinit crate debugger decltype delete demote demote_to_helper do dynamic_cast enum explicit export
|
|
201
|
+
extends extern external fallthrough filter final finally friend from fxgroup get goto groupshared highp impl
|
|
202
|
+
implements import inline instanceof interface layout lowp macro macro_rules match mediump meta mod module move
|
|
203
|
+
mut mutable namespace new nil noexcept noinline nointerpolation non_coherent noncoherent noperspective null
|
|
204
|
+
nullptr of operator package packoffset partition pass patch pixelfragment precise precision premerge priv
|
|
205
|
+
protected pub public readonly ref regardless register reinterpret_cast require resource restrict self set shared
|
|
206
|
+
sizeof smooth snorm static static_assert static_cast std subroutine super target template this thread_local
|
|
207
|
+
throw trait try type typedef typeid typename typeof union unless unorm unsafe unsized use using varying virtual
|
|
208
|
+
volatile wgsl where with writeonly yield`
|
|
209
|
+
.trim()
|
|
210
|
+
.split(/\s+/));
|
|
211
|
+
//# sourceMappingURL=prelude.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prelude.js","sourceRoot":"","sources":["../../../src/kernel/prelude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EACH,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,cAAc,EACd,sBAAsB,EACtB,OAAO,EACP,cAAc,GACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IACxC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,gBAAgB,CAAC,oBAAoB,EAAE,mBAAmB,KAAK,yBAAyB,EAAE;YAChG,QAAQ,EAAE,OAAO;YACjB,KAAK;YACL,QAAQ,EAAE,iBAAiB;SAC9B,CAAC,CAAC;IACP,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3B,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC;AAClD,CAAC;AAED,iHAAiH;AACjH,MAAM,CAAC,MAAM,YAAY,GAAW,UAAU,CAAC;6BAClB,aAAa;uBACnB,OAAO;sCACQ,sBAAsB;8BAC9B,cAAc,CAAC,kBAAkB,CAAC;iCAC/B,cAAc,CAAC,qBAAqB,CAAC;iCACrC,cAAc,CAAC,iBAAiB,CAAC;8BACpC,cAAc;;qBAEvB,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+BjC,CAAC;AAEH,8GAA8G;AAC9G,MAAM,CAAC,MAAM,aAAa,GAAW,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;AAErE,+DAA+D;AAC/D,MAAM,CAAC,MAAM,6BAA6B,GAAW,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kHAoCkD,CAAC;AAEnH,mFAAmF;AACnF,MAAM,CAAC,MAAM,4BAA4B,GAAW,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kHA2EmD,CAAC;AAEnH,0FAA0F;AAC1F,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,eAAe,EAAE,eAAe,EAAE,gBAAgB,CAAU,CAAC;AAEjG;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAsB,MAAM,CAAC,MAAM,CAC/D;;;;;;;;;;6CAUyC;KACpC,IAAI,EAAE;KACN,KAAK,CAAC,KAAK,CAAC,CACpB,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timestamp profiling of spec 5.5: when "timestamp-query" was granted the context owns a Profiler
|
|
3
|
+
* whose query set (PROFILER_QUERY_SLOTS slots, two per pass) is written by the timestampWrites of
|
|
4
|
+
* every CommandBatch pass; `resolveInto(batch)` records the resolve plus a copy into the batch's
|
|
5
|
+
* staging slot and `timings(bytes, request)` decodes `{ label, ns }` per pass from the batch's
|
|
6
|
+
* readback bytes. Chromium quantises timestamps to 100 us (`quantised: true`); Dawn-node reports
|
|
7
|
+
* 1,024 ns ticks (spec 2.6). The profiler never throws: a full query set drops timings and a device
|
|
8
|
+
* without the feature disables it. Slot pairs are handed out in order and the cursor restarts at 0
|
|
9
|
+
* after every resolve: queue order guarantees the resolve of batch A reads its slots before batch B
|
|
10
|
+
* overwrites them, and one resolve buffer suffices for the same reason.
|
|
11
|
+
*/
|
|
12
|
+
import type { CommandBatch, ReadbackRequest } from "./batch.js";
|
|
13
|
+
/** One resolved pass timing. */
|
|
14
|
+
export interface PassTiming {
|
|
15
|
+
readonly label: string;
|
|
16
|
+
readonly ns: number;
|
|
17
|
+
}
|
|
18
|
+
/** Timestamp profiling when "timestamp-query" was granted (spec 5.5). */
|
|
19
|
+
export declare class Profiler {
|
|
20
|
+
/** True when the feature was granted and a query set exists. */
|
|
21
|
+
readonly enabled: boolean;
|
|
22
|
+
/** True in browsers (100 us quantisation), false under Dawn-node (spec 2.6). */
|
|
23
|
+
readonly quantised: boolean;
|
|
24
|
+
private readonly slots;
|
|
25
|
+
private readonly querySet;
|
|
26
|
+
private readonly resolveBuffer;
|
|
27
|
+
private readonly labels;
|
|
28
|
+
private pending;
|
|
29
|
+
private cursor;
|
|
30
|
+
private destroyed;
|
|
31
|
+
/**
|
|
32
|
+
* Creates the query set and the resolve buffer when usable; `slots` defaults to PROFILER_QUERY_SLOTS = 256
|
|
33
|
+
* (spec 5.5); 2 query slots per pass. `enabled` is also false when the device lacks "timestamp-query" or
|
|
34
|
+
* `slots < 2`, so no caller can make the profiler raise a validation error.
|
|
35
|
+
* @param device - the device the query set is created on
|
|
36
|
+
* @param enabled - whether the caller wants profiling (the context passes `features.has("timestamp-query")`)
|
|
37
|
+
* @param quantised - true in browsers
|
|
38
|
+
* @param slots - query slots, two per pass (default PROFILER_QUERY_SLOTS)
|
|
39
|
+
*/
|
|
40
|
+
constructor(device: GPUDevice, enabled: boolean, quantised: boolean, slots?: number);
|
|
41
|
+
/**
|
|
42
|
+
* The timestampWrites descriptor for a new pass, or undefined when disabled / out of slots.
|
|
43
|
+
* @param label - the pass name reported by `timings()`
|
|
44
|
+
* @returns the descriptor a CommandBatch puts on beginComputePass, or undefined
|
|
45
|
+
*/
|
|
46
|
+
beginPass(label: string): GPUComputePassTimestampWrites | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Records the resolve + copy into the batch's staging slot; returns the request's byte range, or null when
|
|
49
|
+
* nothing was written (disabled, destroyed, or no pass begun since the last resolve).
|
|
50
|
+
* @param batch - the batch whose passes were timed (it must still be open)
|
|
51
|
+
* @returns the readback request `timings()` decodes, or null
|
|
52
|
+
*/
|
|
53
|
+
resolveInto(batch: CommandBatch): ReadbackRequest | null;
|
|
54
|
+
/**
|
|
55
|
+
* Decodes the timings of a batch from its readback bytes.
|
|
56
|
+
* @param bytes - the resolved readback of the batch
|
|
57
|
+
* @param request - the request `resolveInto()` returned for it
|
|
58
|
+
* @returns one timing per pass in pass order (empty for a request this profiler did not create)
|
|
59
|
+
*/
|
|
60
|
+
timings(bytes: ArrayBuffer, request: ReadbackRequest): readonly PassTiming[];
|
|
61
|
+
/** Destroys the query set and the resolve buffer; idempotent. */
|
|
62
|
+
destroy(): void;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=profiler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiler.d.ts","sourceRoot":"","sources":["../../../src/kernel/profiler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEhE,gCAAgC;AAChC,MAAM,WAAW,UAAU;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACvB;AAQD,yEAAyE;AACzE,qBAAa,QAAQ;IACjB,gEAAgE;IAChE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAmB;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqD;IAC5E,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,SAAS,CAAS;IAE1B;;;;;;;;OAQG;gBACS,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM;IAmBnF;;;;OAIG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,6BAA6B,GAAG,SAAS;IAUnE;;;;;OAKG;IACH,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,eAAe,GAAG,IAAI;IAgBxD;;;;;OAKG;IACH,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,GAAG,SAAS,UAAU,EAAE;IAmB5E,iEAAiE;IACjE,OAAO,IAAI,IAAI;CAclB"}
|