@graphty/webgpu-graph-algorithms 0.0.0 → 0.2.1
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 +378 -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,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Size-class pool of GPUBuffers by usage (spec 4.4): a request rounds up to a class (powers of two from 4 KiB to
|
|
3
|
+
* 64 MiB, then 16 MiB steps), an idle buffer of that (class, usage) is reused, otherwise one is created through the
|
|
4
|
+
* context's AllocationTracker (so the OOM scope of spec 5.7 and the resident accounting are shared with the
|
|
5
|
+
* residency); `release` keeps at most maxIdlePerClass idle buffers per (class, usage); `trim` destroys the idle
|
|
6
|
+
* ones (ctx.release and layouts on dispose); `destroyAll` everything (ctx.dispose). The Lease scope object arrives
|
|
7
|
+
* with P2-T1 (`lease()`, PLAN DECISION 1). Classes above maxBufferSize are never created: a request that fits the
|
|
8
|
+
* device but whose class overshoots gets the capped class (PLAN DECISION 14).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
POOL_LINEAR_STEP_BYTES,
|
|
13
|
+
POOL_MAX_IDLE_PER_CLASS,
|
|
14
|
+
POOL_MAX_POW2_CLASS_BYTES,
|
|
15
|
+
POOL_MIN_CLASS_BYTES,
|
|
16
|
+
} from "../constants.js";
|
|
17
|
+
import { type AllocationTracker } from "../device/error-scope.js";
|
|
18
|
+
import { WebGpuGraphError } from "../errors.js";
|
|
19
|
+
import { Lease } from "./lease.js";
|
|
20
|
+
|
|
21
|
+
/** What the pool remembers about a live buffer. */
|
|
22
|
+
interface LiveEntry {
|
|
23
|
+
readonly sizeClass: number;
|
|
24
|
+
readonly usage: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The idle-list key of a (class, usage) pair.
|
|
29
|
+
* @param sizeClass - the class in bytes
|
|
30
|
+
* @param usage - the GPUBufferUsage flags
|
|
31
|
+
* @returns the map key
|
|
32
|
+
*/
|
|
33
|
+
function classKey(sizeClass: number, usage: number): string {
|
|
34
|
+
return `${sizeClass}:${usage}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Size-class pool of GPUBuffers by usage (spec 4.4). */
|
|
38
|
+
export class BufferPool {
|
|
39
|
+
private readonly allocator: AllocationTracker;
|
|
40
|
+
private readonly maxBufferSize: number;
|
|
41
|
+
private readonly maxIdlePerClass: number;
|
|
42
|
+
private readonly idle = new Map<string, GPUBuffer[]>();
|
|
43
|
+
private readonly live = new Map<GPUBuffer, LiveEntry>();
|
|
44
|
+
private liveBytesValue = 0;
|
|
45
|
+
private idleBytesValue = 0;
|
|
46
|
+
private disposed = false;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Creates an empty pool.
|
|
50
|
+
* @param _device - the device (buffers are created through the allocator, which owns it; the parameter keeps
|
|
51
|
+
* the contract's constructor shape)
|
|
52
|
+
* @param allocator - the context's OOM-scoped allocator
|
|
53
|
+
* @param maxBufferSize - the device's maxBufferSize (classes above it are never created)
|
|
54
|
+
* @param options - pool options
|
|
55
|
+
* @param options.maxIdlePerClass - idle buffers kept per (class, usage); default POOL_MAX_IDLE_PER_CLASS
|
|
56
|
+
*/
|
|
57
|
+
constructor(
|
|
58
|
+
_device: GPUDevice,
|
|
59
|
+
allocator: AllocationTracker,
|
|
60
|
+
maxBufferSize: number,
|
|
61
|
+
options?: { readonly maxIdlePerClass?: number | undefined },
|
|
62
|
+
) {
|
|
63
|
+
this.allocator = allocator;
|
|
64
|
+
this.maxBufferSize = maxBufferSize - (maxBufferSize % 4);
|
|
65
|
+
this.maxIdlePerClass = options?.maxIdlePerClass ?? POOL_MAX_IDLE_PER_CLASS;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The size class a byte length rounds up to: powers of two from 4 KiB to 64 MiB, then 16 MiB steps (pure).
|
|
70
|
+
* @param byteLength - a positive integer
|
|
71
|
+
* @returns the class in bytes
|
|
72
|
+
*/
|
|
73
|
+
static sizeClass(byteLength: number): number {
|
|
74
|
+
if (!Number.isInteger(byteLength) || byteLength <= 0) {
|
|
75
|
+
throw new WebGpuGraphError("E_INVALID_ARGUMENT", "byteLength: expected a positive integer", {
|
|
76
|
+
argument: "byteLength",
|
|
77
|
+
value: byteLength,
|
|
78
|
+
expected: "a positive integer",
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (byteLength > POOL_MAX_POW2_CLASS_BYTES) {
|
|
82
|
+
return Math.ceil(byteLength / POOL_LINEAR_STEP_BYTES) * POOL_LINEAR_STEP_BYTES;
|
|
83
|
+
}
|
|
84
|
+
let sizeClass = POOL_MIN_CLASS_BYTES;
|
|
85
|
+
while (sizeClass < byteLength) {
|
|
86
|
+
sizeClass *= 2;
|
|
87
|
+
}
|
|
88
|
+
return sizeClass;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Acquires a buffer of at least byteLength (an idle one of the class and usage, else a new one through the
|
|
93
|
+
* allocator); E_TOO_LARGE above maxBufferSize.
|
|
94
|
+
* @param byteLength - the bytes needed
|
|
95
|
+
* @param usage - the GPUBufferUsage flags
|
|
96
|
+
* @param label - the buffer label (a reused buffer is relabelled)
|
|
97
|
+
* @returns a buffer of the class size
|
|
98
|
+
*/
|
|
99
|
+
acquire(byteLength: number, usage: number, label: string): GPUBuffer {
|
|
100
|
+
if (this.disposed) {
|
|
101
|
+
throw new WebGpuGraphError("E_DISPOSED", "the buffer pool was disposed", { label: "pool" });
|
|
102
|
+
}
|
|
103
|
+
const rounded = BufferPool.sizeClass(byteLength);
|
|
104
|
+
if (byteLength > this.maxBufferSize) {
|
|
105
|
+
throw new WebGpuGraphError(
|
|
106
|
+
"E_TOO_LARGE",
|
|
107
|
+
`pool: ${byteLength} bytes exceed the device maxBufferSize of ${this.maxBufferSize} bytes`,
|
|
108
|
+
{ needed: byteLength, limit: this.maxBufferSize, path: "pool", algorithm: null },
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
const sizeClass = Math.min(rounded, this.maxBufferSize);
|
|
112
|
+
const key = classKey(sizeClass, usage);
|
|
113
|
+
const idle = this.idle.get(key);
|
|
114
|
+
let buffer = idle?.pop();
|
|
115
|
+
if (buffer === undefined) {
|
|
116
|
+
buffer = this.allocator.createBuffer({ label, size: sizeClass, usage });
|
|
117
|
+
} else {
|
|
118
|
+
this.idleBytesValue -= sizeClass;
|
|
119
|
+
buffer.label = label;
|
|
120
|
+
}
|
|
121
|
+
this.live.set(buffer, { sizeClass, usage });
|
|
122
|
+
this.liveBytesValue += sizeClass;
|
|
123
|
+
return buffer;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Returns a buffer to its class; destroys it when the class already holds maxIdlePerClass idle buffers. A no-op
|
|
128
|
+
* after destroyAll() (so a `finally` never masks the original error).
|
|
129
|
+
* @param buffer - a buffer acquired from this pool and not yet released
|
|
130
|
+
*/
|
|
131
|
+
release(buffer: GPUBuffer): void {
|
|
132
|
+
if (this.disposed) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const entry = this.live.get(buffer);
|
|
136
|
+
if (entry === undefined) {
|
|
137
|
+
throw new WebGpuGraphError("E_INVALID_ARGUMENT", "buffer: not a live buffer of this pool", {
|
|
138
|
+
argument: "buffer",
|
|
139
|
+
value: buffer.label,
|
|
140
|
+
expected: "a buffer acquired from this pool and not yet released",
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
this.live.delete(buffer);
|
|
144
|
+
this.liveBytesValue -= entry.sizeClass;
|
|
145
|
+
const key = classKey(entry.sizeClass, entry.usage);
|
|
146
|
+
let idle = this.idle.get(key);
|
|
147
|
+
if (idle === undefined) {
|
|
148
|
+
idle = [];
|
|
149
|
+
this.idle.set(key, idle);
|
|
150
|
+
}
|
|
151
|
+
if (idle.length >= this.maxIdlePerClass) {
|
|
152
|
+
this.allocator.destroy(buffer);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
idle.push(buffer);
|
|
156
|
+
this.idleBytesValue += entry.sizeClass;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Destroys every idle buffer. */
|
|
160
|
+
trim(): void {
|
|
161
|
+
for (const buffers of this.idle.values()) {
|
|
162
|
+
for (const buffer of buffers) {
|
|
163
|
+
this.allocator.destroy(buffer);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
this.idle.clear();
|
|
167
|
+
this.idleBytesValue = 0;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Destroys everything, idle and live (ctx.dispose()); idempotent. @internal */
|
|
171
|
+
destroyAll(): void {
|
|
172
|
+
this.trim();
|
|
173
|
+
for (const buffer of this.live.keys()) {
|
|
174
|
+
this.allocator.destroy(buffer);
|
|
175
|
+
}
|
|
176
|
+
this.live.clear();
|
|
177
|
+
this.liveBytesValue = 0;
|
|
178
|
+
this.disposed = true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* A scope object that releases everything acquired through it (spec 4.4, P2).
|
|
183
|
+
* @returns a fresh lease over this pool
|
|
184
|
+
*/
|
|
185
|
+
lease(): Lease {
|
|
186
|
+
return new Lease(this);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Bytes of buffers acquired and not yet released (class sizes).
|
|
191
|
+
* @returns the live byte count
|
|
192
|
+
*/
|
|
193
|
+
get liveBytes(): number {
|
|
194
|
+
return this.liveBytesValue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Bytes of idle buffers (class sizes).
|
|
199
|
+
* @returns the idle byte count
|
|
200
|
+
*/
|
|
201
|
+
get idleBytes(): number {
|
|
202
|
+
return this.idleBytesValue;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Lease scope object of spec 4.4: every buffer an algorithm acquires through it goes back to the
|
|
3
|
+
* BufferPool in ONE `release()` call placed in the algorithm's `finally` block, so scratch never
|
|
4
|
+
* outlives the call that took it (a `try / finally`, not `Symbol.dispose`, until the monorepo's
|
|
5
|
+
* TypeScript target supports `using`). Persistent buffers (layout state) are owned by the simulation
|
|
6
|
+
* object instead and freed by its `dispose()`.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { BufferUsage } from "../device/webgpu-constants.js";
|
|
10
|
+
import { hasErrorCode, WebGpuGraphError } from "../errors.js";
|
|
11
|
+
import type { BufferPool } from "./buffer-pool.js";
|
|
12
|
+
|
|
13
|
+
/** Scope object algorithms use: every buffer acquired through it is released by `release()` in a finally block (spec 4.4). */
|
|
14
|
+
export class Lease {
|
|
15
|
+
private readonly pool: BufferPool;
|
|
16
|
+
private readonly buffers = new Set<GPUBuffer>();
|
|
17
|
+
private released = false;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates a lease over a pool; `pool.lease()` is the usual way to get one.
|
|
21
|
+
* @param pool - the pool every acquisition of this lease goes through
|
|
22
|
+
*/
|
|
23
|
+
constructor(pool: BufferPool) {
|
|
24
|
+
this.pool = pool;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* pool.acquire(byteLength, STORAGE | COPY_SRC | COPY_DST, label).
|
|
29
|
+
* @param byteLength - bytes needed; the pool rounds up to its size class
|
|
30
|
+
* @param label - the buffer label
|
|
31
|
+
* @returns the buffer, returned to the pool by `release()`
|
|
32
|
+
*/
|
|
33
|
+
storage(byteLength: number, label: string): GPUBuffer {
|
|
34
|
+
return this.acquire(byteLength, BufferUsage.STORAGE | BufferUsage.COPY_SRC | BufferUsage.COPY_DST, label);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* pool.acquire(byteLength, UNIFORM | COPY_DST, label).
|
|
39
|
+
* @param byteLength - bytes needed; the pool rounds up to its size class
|
|
40
|
+
* @param label - the buffer label
|
|
41
|
+
* @returns the buffer, returned to the pool by `release()`
|
|
42
|
+
*/
|
|
43
|
+
uniform(byteLength: number, label: string): GPUBuffer {
|
|
44
|
+
return this.acquire(byteLength, BufferUsage.UNIFORM | BufferUsage.COPY_DST, label);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* pool.acquire with explicit usage.
|
|
49
|
+
* @param byteLength - bytes needed; the pool rounds up to its size class
|
|
50
|
+
* @param usage - the GPUBufferUsage bits
|
|
51
|
+
* @param label - the buffer label
|
|
52
|
+
* @returns the buffer, returned to the pool by `release()`
|
|
53
|
+
*/
|
|
54
|
+
acquire(byteLength: number, usage: number, label: string): GPUBuffer {
|
|
55
|
+
if (this.released) {
|
|
56
|
+
throw new WebGpuGraphError("E_DISPOSED", `lease already released: cannot acquire "${label}"`, { label });
|
|
57
|
+
}
|
|
58
|
+
const buffer = this.pool.acquire(byteLength, usage, label);
|
|
59
|
+
this.buffers.add(buffer);
|
|
60
|
+
return buffer;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Releases every buffer acquired through this lease; idempotent. A pool that `ctx.dispose()` already
|
|
65
|
+
* destroyed has nothing left to take back, so its E_DISPOSED is swallowed here: the lease sits in a
|
|
66
|
+
* `finally` block and must never mask the error that unwound the algorithm.
|
|
67
|
+
*/
|
|
68
|
+
release(): void {
|
|
69
|
+
if (this.released) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
this.released = true;
|
|
73
|
+
const owned = [...this.buffers];
|
|
74
|
+
this.buffers.clear();
|
|
75
|
+
for (const buffer of owned) {
|
|
76
|
+
try {
|
|
77
|
+
this.pool.release(buffer);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (!hasErrorCode(error, "E_DISPOSED")) {
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Live buffers of this lease.
|
|
88
|
+
* @returns the number of buffers acquired through the lease and not yet released
|
|
89
|
+
*/
|
|
90
|
+
get count(): number {
|
|
91
|
+
return this.buffers.size;
|
|
92
|
+
}
|
|
93
|
+
}
|