@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.
Files changed (269) hide show
  1. package/README.md +378 -23
  2. package/dist/browser.d.ts +1 -0
  3. package/dist/browser.js +32 -0
  4. package/dist/browser.js.map +1 -0
  5. package/dist/chunks/context-E6iKaeuJ.js +3136 -0
  6. package/dist/chunks/context-E6iKaeuJ.js.map +1 -0
  7. package/dist/node.d.ts +1 -0
  8. package/dist/node.js +131 -0
  9. package/dist/node.js.map +1 -0
  10. package/dist/src/accelerator.d.ts +26 -0
  11. package/dist/src/accelerator.d.ts.map +1 -0
  12. package/dist/src/accelerator.js +101 -0
  13. package/dist/src/accelerator.js.map +1 -0
  14. package/dist/src/algorithms/degree.d.ts +35 -0
  15. package/dist/src/algorithms/degree.d.ts.map +1 -0
  16. package/dist/src/algorithms/degree.js +119 -0
  17. package/dist/src/algorithms/degree.js.map +1 -0
  18. package/dist/src/browser/index.d.ts +23 -0
  19. package/dist/src/browser/index.d.ts.map +1 -0
  20. package/dist/src/browser/index.js +48 -0
  21. package/dist/src/browser/index.js.map +1 -0
  22. package/dist/src/constants.d.ts +92 -0
  23. package/dist/src/constants.d.ts.map +1 -0
  24. package/dist/src/constants.js +92 -0
  25. package/dist/src/constants.js.map +1 -0
  26. package/dist/src/context.d.ts +84 -0
  27. package/dist/src/context.d.ts.map +1 -0
  28. package/dist/src/context.js +304 -0
  29. package/dist/src/context.js.map +1 -0
  30. package/dist/src/device/acquire.d.ts +57 -0
  31. package/dist/src/device/acquire.d.ts.map +1 -0
  32. package/dist/src/device/acquire.js +232 -0
  33. package/dist/src/device/acquire.js.map +1 -0
  34. package/dist/src/device/caps.d.ts +43 -0
  35. package/dist/src/device/caps.d.ts.map +1 -0
  36. package/dist/src/device/caps.js +104 -0
  37. package/dist/src/device/caps.js.map +1 -0
  38. package/dist/src/device/error-scope.d.ts +75 -0
  39. package/dist/src/device/error-scope.d.ts.map +1 -0
  40. package/dist/src/device/error-scope.js +152 -0
  41. package/dist/src/device/error-scope.js.map +1 -0
  42. package/dist/src/device/lost.d.ts +51 -0
  43. package/dist/src/device/lost.d.ts.map +1 -0
  44. package/dist/src/device/lost.js +130 -0
  45. package/dist/src/device/lost.js.map +1 -0
  46. package/dist/src/device/webgpu-constants.d.ts +31 -0
  47. package/dist/src/device/webgpu-constants.d.ts.map +1 -0
  48. package/dist/src/device/webgpu-constants.js +31 -0
  49. package/dist/src/device/webgpu-constants.js.map +1 -0
  50. package/dist/src/errors.d.ts +56 -0
  51. package/dist/src/errors.d.ts.map +1 -0
  52. package/dist/src/errors.js +57 -0
  53. package/dist/src/errors.js.map +1 -0
  54. package/dist/src/index.d.ts +29 -0
  55. package/dist/src/index.d.ts.map +1 -0
  56. package/dist/src/index.js +27 -0
  57. package/dist/src/index.js.map +1 -0
  58. package/dist/src/kernel/batch.d.ts +116 -0
  59. package/dist/src/kernel/batch.d.ts.map +1 -0
  60. package/dist/src/kernel/batch.js +335 -0
  61. package/dist/src/kernel/batch.js.map +1 -0
  62. package/dist/src/kernel/dispatch.d.ts +59 -0
  63. package/dist/src/kernel/dispatch.d.ts.map +1 -0
  64. package/dist/src/kernel/dispatch.js +139 -0
  65. package/dist/src/kernel/dispatch.js.map +1 -0
  66. package/dist/src/kernel/kernel.d.ts +84 -0
  67. package/dist/src/kernel/kernel.d.ts.map +1 -0
  68. package/dist/src/kernel/kernel.js +239 -0
  69. package/dist/src/kernel/kernel.js.map +1 -0
  70. package/dist/src/kernel/pipeline-cache.d.ts +90 -0
  71. package/dist/src/kernel/pipeline-cache.d.ts.map +1 -0
  72. package/dist/src/kernel/pipeline-cache.js +251 -0
  73. package/dist/src/kernel/pipeline-cache.js.map +1 -0
  74. package/dist/src/kernel/prelude.d.ts +35 -0
  75. package/dist/src/kernel/prelude.d.ts.map +1 -0
  76. package/dist/src/kernel/prelude.js +211 -0
  77. package/dist/src/kernel/prelude.js.map +1 -0
  78. package/dist/src/kernel/profiler.d.ts +64 -0
  79. package/dist/src/kernel/profiler.d.ts.map +1 -0
  80. package/dist/src/kernel/profiler.js +120 -0
  81. package/dist/src/kernel/profiler.js.map +1 -0
  82. package/dist/src/kernel/struct-block.d.ts +122 -0
  83. package/dist/src/kernel/struct-block.d.ts.map +1 -0
  84. package/dist/src/kernel/struct-block.js +353 -0
  85. package/dist/src/kernel/struct-block.js.map +1 -0
  86. package/dist/src/kernel/uniform-ring.d.ts +70 -0
  87. package/dist/src/kernel/uniform-ring.d.ts.map +1 -0
  88. package/dist/src/kernel/uniform-ring.js +146 -0
  89. package/dist/src/kernel/uniform-ring.js.map +1 -0
  90. package/dist/src/kernel/wgsl.d.ts +88 -0
  91. package/dist/src/kernel/wgsl.d.ts.map +1 -0
  92. package/dist/src/kernel/wgsl.js +390 -0
  93. package/dist/src/kernel/wgsl.js.map +1 -0
  94. package/dist/src/kernels.d.ts +81 -0
  95. package/dist/src/kernels.d.ts.map +1 -0
  96. package/dist/src/kernels.js +417 -0
  97. package/dist/src/kernels.js.map +1 -0
  98. package/dist/src/layouts/force-simulation.d.ts +498 -0
  99. package/dist/src/layouts/force-simulation.d.ts.map +1 -0
  100. package/dist/src/layouts/force-simulation.js +1650 -0
  101. package/dist/src/layouts/force-simulation.js.map +1 -0
  102. package/dist/src/layouts/forceatlas2.d.ts +210 -0
  103. package/dist/src/layouts/forceatlas2.d.ts.map +1 -0
  104. package/dist/src/layouts/forceatlas2.js +759 -0
  105. package/dist/src/layouts/forceatlas2.js.map +1 -0
  106. package/dist/src/layouts/inputs.d.ts +40 -0
  107. package/dist/src/layouts/inputs.d.ts.map +1 -0
  108. package/dist/src/layouts/inputs.js +185 -0
  109. package/dist/src/layouts/inputs.js.map +1 -0
  110. package/dist/src/layouts/repulsion-exact.d.ts +85 -0
  111. package/dist/src/layouts/repulsion-exact.d.ts.map +1 -0
  112. package/dist/src/layouts/repulsion-exact.js +134 -0
  113. package/dist/src/layouts/repulsion-exact.js.map +1 -0
  114. package/dist/src/layouts/seed.d.ts +56 -0
  115. package/dist/src/layouts/seed.d.ts.map +1 -0
  116. package/dist/src/layouts/seed.js +173 -0
  117. package/dist/src/layouts/seed.js.map +1 -0
  118. package/dist/src/memory/buffer-pool.d.ts +73 -0
  119. package/dist/src/memory/buffer-pool.d.ts.map +1 -0
  120. package/dist/src/memory/buffer-pool.js +170 -0
  121. package/dist/src/memory/buffer-pool.js.map +1 -0
  122. package/dist/src/memory/lease.d.ts +53 -0
  123. package/dist/src/memory/lease.d.ts.map +1 -0
  124. package/dist/src/memory/lease.js +85 -0
  125. package/dist/src/memory/lease.js.map +1 -0
  126. package/dist/src/memory/readback.d.ts +143 -0
  127. package/dist/src/memory/readback.d.ts.map +1 -0
  128. package/dist/src/memory/readback.js +375 -0
  129. package/dist/src/memory/readback.js.map +1 -0
  130. package/dist/src/memory/residency.d.ts +83 -0
  131. package/dist/src/memory/residency.d.ts.map +1 -0
  132. package/dist/src/memory/residency.js +573 -0
  133. package/dist/src/memory/residency.js.map +1 -0
  134. package/dist/src/memory/upload-plan.d.ts +101 -0
  135. package/dist/src/memory/upload-plan.d.ts.map +1 -0
  136. package/dist/src/memory/upload-plan.js +265 -0
  137. package/dist/src/memory/upload-plan.js.map +1 -0
  138. package/dist/src/node/index.d.ts +64 -0
  139. package/dist/src/node/index.d.ts.map +1 -0
  140. package/dist/src/node/index.js +183 -0
  141. package/dist/src/node/index.js.map +1 -0
  142. package/dist/src/primitives/reduce.d.ts +57 -0
  143. package/dist/src/primitives/reduce.d.ts.map +1 -0
  144. package/dist/src/primitives/reduce.js +161 -0
  145. package/dist/src/primitives/reduce.js.map +1 -0
  146. package/dist/src/primitives/segmented-reduce.d.ts +38 -0
  147. package/dist/src/primitives/segmented-reduce.d.ts.map +1 -0
  148. package/dist/src/primitives/segmented-reduce.js +211 -0
  149. package/dist/src/primitives/segmented-reduce.js.map +1 -0
  150. package/dist/src/types/accelerator.d.ts +209 -0
  151. package/dist/src/types/accelerator.d.ts.map +1 -0
  152. package/dist/src/types/accelerator.js +8 -0
  153. package/dist/src/types/accelerator.js.map +1 -0
  154. package/dist/src/types/context.d.ts +114 -0
  155. package/dist/src/types/context.d.ts.map +1 -0
  156. package/dist/src/types/context.js +7 -0
  157. package/dist/src/types/context.js.map +1 -0
  158. package/dist/src/types/layout.d.ts +95 -0
  159. package/dist/src/types/layout.d.ts.map +1 -0
  160. package/dist/src/types/layout.js +6 -0
  161. package/dist/src/types/layout.js.map +1 -0
  162. package/dist/src/types/memory.d.ts +22 -0
  163. package/dist/src/types/memory.d.ts.map +1 -0
  164. package/dist/src/types/memory.js +7 -0
  165. package/dist/src/types/memory.js.map +1 -0
  166. package/dist/src/types/options.d.ts +78 -0
  167. package/dist/src/types/options.d.ts.map +1 -0
  168. package/dist/src/types/options.js +7 -0
  169. package/dist/src/types/options.js.map +1 -0
  170. package/dist/src/types/run.d.ts +13 -0
  171. package/dist/src/types/run.d.ts.map +1 -0
  172. package/dist/src/types/run.js +6 -0
  173. package/dist/src/types/run.js.map +1 -0
  174. package/dist/src/wgsl/degree.wgsl.d.ts +10 -0
  175. package/dist/src/wgsl/degree.wgsl.d.ts.map +1 -0
  176. package/dist/src/wgsl/degree.wgsl.js +25 -0
  177. package/dist/src/wgsl/degree.wgsl.js.map +1 -0
  178. package/dist/src/wgsl/fa2-attraction.wgsl.d.ts +12 -0
  179. package/dist/src/wgsl/fa2-attraction.wgsl.d.ts.map +1 -0
  180. package/dist/src/wgsl/fa2-attraction.wgsl.js +37 -0
  181. package/dist/src/wgsl/fa2-attraction.wgsl.js.map +1 -0
  182. package/dist/src/wgsl/fa2-integrate.wgsl.d.ts +13 -0
  183. package/dist/src/wgsl/fa2-integrate.wgsl.d.ts.map +1 -0
  184. package/dist/src/wgsl/fa2-integrate.wgsl.js +69 -0
  185. package/dist/src/wgsl/fa2-integrate.wgsl.js.map +1 -0
  186. package/dist/src/wgsl/fa2-repulsion-exact.wgsl.d.ts +12 -0
  187. package/dist/src/wgsl/fa2-repulsion-exact.wgsl.d.ts.map +1 -0
  188. package/dist/src/wgsl/fa2-repulsion-exact.wgsl.js +79 -0
  189. package/dist/src/wgsl/fa2-repulsion-exact.wgsl.js.map +1 -0
  190. package/dist/src/wgsl/fa2-speed-finalize.wgsl.d.ts +15 -0
  191. package/dist/src/wgsl/fa2-speed-finalize.wgsl.d.ts.map +1 -0
  192. package/dist/src/wgsl/fa2-speed-finalize.wgsl.js +54 -0
  193. package/dist/src/wgsl/fa2-speed-finalize.wgsl.js.map +1 -0
  194. package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts +14 -0
  195. package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts.map +1 -0
  196. package/dist/src/wgsl/fa2-stats-finalize.wgsl.js +57 -0
  197. package/dist/src/wgsl/fa2-stats-finalize.wgsl.js.map +1 -0
  198. package/dist/src/wgsl/fa2-to-scene.wgsl.d.ts +11 -0
  199. package/dist/src/wgsl/fa2-to-scene.wgsl.d.ts.map +1 -0
  200. package/dist/src/wgsl/fa2-to-scene.wgsl.js +19 -0
  201. package/dist/src/wgsl/fa2-to-scene.wgsl.js.map +1 -0
  202. package/dist/src/wgsl/fill.wgsl.d.ts +7 -0
  203. package/dist/src/wgsl/fill.wgsl.d.ts.map +1 -0
  204. package/dist/src/wgsl/fill.wgsl.js +14 -0
  205. package/dist/src/wgsl/fill.wgsl.js.map +1 -0
  206. package/dist/src/wgsl/reduce.wgsl.d.ts +10 -0
  207. package/dist/src/wgsl/reduce.wgsl.d.ts.map +1 -0
  208. package/dist/src/wgsl/reduce.wgsl.js +63 -0
  209. package/dist/src/wgsl/reduce.wgsl.js.map +1 -0
  210. package/dist/src/wgsl/segmented-reduce.wgsl.d.ts +13 -0
  211. package/dist/src/wgsl/segmented-reduce.wgsl.d.ts.map +1 -0
  212. package/dist/src/wgsl/segmented-reduce.wgsl.js +35 -0
  213. package/dist/src/wgsl/segmented-reduce.wgsl.js.map +1 -0
  214. package/dist/tsconfig.build.tsbuildinfo +1 -0
  215. package/dist/webgpu-graph-algorithms.d.ts +1 -0
  216. package/dist/webgpu-graph-algorithms.js +4454 -0
  217. package/dist/webgpu-graph-algorithms.js.map +1 -0
  218. package/package.json +108 -17
  219. package/src/accelerator.ts +117 -0
  220. package/src/algorithms/degree.ts +142 -0
  221. package/src/browser/index.ts +57 -0
  222. package/src/constants.ts +116 -0
  223. package/src/context.ts +399 -0
  224. package/src/device/acquire.ts +256 -0
  225. package/src/device/caps.ts +122 -0
  226. package/src/device/error-scope.ts +171 -0
  227. package/src/device/lost.ts +142 -0
  228. package/src/device/webgpu-constants.ts +44 -0
  229. package/src/errors.ts +94 -0
  230. package/src/index.ts +102 -0
  231. package/src/kernel/batch.ts +427 -0
  232. package/src/kernel/dispatch.ts +162 -0
  233. package/src/kernel/kernel.ts +311 -0
  234. package/src/kernel/pipeline-cache.ts +288 -0
  235. package/src/kernel/prelude.ts +229 -0
  236. package/src/kernel/profiler.ts +148 -0
  237. package/src/kernel/struct-block.ts +439 -0
  238. package/src/kernel/uniform-ring.ts +184 -0
  239. package/src/kernel/wgsl.ts +490 -0
  240. package/src/kernels.ts +511 -0
  241. package/src/layouts/force-simulation.ts +2111 -0
  242. package/src/layouts/forceatlas2.ts +942 -0
  243. package/src/layouts/inputs.ts +252 -0
  244. package/src/layouts/repulsion-exact.ts +183 -0
  245. package/src/layouts/seed.ts +198 -0
  246. package/src/memory/buffer-pool.ts +204 -0
  247. package/src/memory/lease.ts +93 -0
  248. package/src/memory/readback.ts +429 -0
  249. package/src/memory/residency.ts +753 -0
  250. package/src/memory/upload-plan.ts +350 -0
  251. package/src/node/index.ts +230 -0
  252. package/src/primitives/reduce.ts +233 -0
  253. package/src/primitives/segmented-reduce.ts +270 -0
  254. package/src/types/accelerator.ts +236 -0
  255. package/src/types/context.ts +135 -0
  256. package/src/types/layout.ts +103 -0
  257. package/src/types/memory.ts +23 -0
  258. package/src/types/options.ts +84 -0
  259. package/src/types/run.ts +13 -0
  260. package/src/wgsl/degree.wgsl.ts +24 -0
  261. package/src/wgsl/fa2-attraction.wgsl.ts +37 -0
  262. package/src/wgsl/fa2-integrate.wgsl.ts +69 -0
  263. package/src/wgsl/fa2-repulsion-exact.wgsl.ts +78 -0
  264. package/src/wgsl/fa2-speed-finalize.wgsl.ts +53 -0
  265. package/src/wgsl/fa2-stats-finalize.wgsl.ts +57 -0
  266. package/src/wgsl/fa2-to-scene.wgsl.ts +19 -0
  267. package/src/wgsl/fill.wgsl.ts +13 -0
  268. package/src/wgsl/reduce.wgsl.ts +62 -0
  269. 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
+ }