@graphty/webgpu-graph-algorithms 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/README.md +98 -52
  2. package/dist/browser.js +1 -1
  3. package/dist/chunks/{context-BR7fx3vR.js → context-BXqgCifx.js} +190 -40
  4. package/dist/chunks/context-BXqgCifx.js.map +1 -0
  5. package/dist/node.js +1 -1
  6. package/dist/src/algorithms/components.d.ts.map +1 -1
  7. package/dist/src/algorithms/components.js +12 -13
  8. package/dist/src/algorithms/components.js.map +1 -1
  9. package/dist/src/algorithms/degree.d.ts +6 -8
  10. package/dist/src/algorithms/degree.d.ts.map +1 -1
  11. package/dist/src/algorithms/degree.js +58 -35
  12. package/dist/src/algorithms/degree.js.map +1 -1
  13. package/dist/src/algorithms/pagerank.d.ts.map +1 -1
  14. package/dist/src/algorithms/pagerank.js +16 -14
  15. package/dist/src/algorithms/pagerank.js.map +1 -1
  16. package/dist/src/algorithms/power-iteration.d.ts +2 -2
  17. package/dist/src/algorithms/power-iteration.d.ts.map +1 -1
  18. package/dist/src/algorithms/power-iteration.js +17 -14
  19. package/dist/src/algorithms/power-iteration.js.map +1 -1
  20. package/dist/src/constants.d.ts +38 -8
  21. package/dist/src/constants.d.ts.map +1 -1
  22. package/dist/src/constants.js +38 -8
  23. package/dist/src/constants.js.map +1 -1
  24. package/dist/src/errors.d.ts +3 -2
  25. package/dist/src/errors.d.ts.map +1 -1
  26. package/dist/src/errors.js +2 -1
  27. package/dist/src/errors.js.map +1 -1
  28. package/dist/src/index.d.ts +6 -4
  29. package/dist/src/index.d.ts.map +1 -1
  30. package/dist/src/index.js +8 -3
  31. package/dist/src/index.js.map +1 -1
  32. package/dist/src/kernel/dispatch.d.ts +8 -3
  33. package/dist/src/kernel/dispatch.d.ts.map +1 -1
  34. package/dist/src/kernel/dispatch.js +18 -7
  35. package/dist/src/kernel/dispatch.js.map +1 -1
  36. package/dist/src/kernel/kernel.d.ts +30 -1
  37. package/dist/src/kernel/kernel.d.ts.map +1 -1
  38. package/dist/src/kernel/kernel.js +49 -5
  39. package/dist/src/kernel/kernel.js.map +1 -1
  40. package/dist/src/kernel/prelude.d.ts.map +1 -1
  41. package/dist/src/kernel/prelude.js +6 -1
  42. package/dist/src/kernel/prelude.js.map +1 -1
  43. package/dist/src/kernel/profiler.d.ts +15 -3
  44. package/dist/src/kernel/profiler.d.ts.map +1 -1
  45. package/dist/src/kernel/profiler.js +27 -4
  46. package/dist/src/kernel/profiler.js.map +1 -1
  47. package/dist/src/kernels.d.ts +17 -7
  48. package/dist/src/kernels.d.ts.map +1 -1
  49. package/dist/src/kernels.js +323 -16
  50. package/dist/src/kernels.js.map +1 -1
  51. package/dist/src/layouts/calibrate.d.ts +51 -0
  52. package/dist/src/layouts/calibrate.d.ts.map +1 -0
  53. package/dist/src/layouts/calibrate.js +172 -0
  54. package/dist/src/layouts/calibrate.js.map +1 -0
  55. package/dist/src/layouts/force-simulation.d.ts +39 -4
  56. package/dist/src/layouts/force-simulation.d.ts.map +1 -1
  57. package/dist/src/layouts/force-simulation.js +71 -19
  58. package/dist/src/layouts/force-simulation.js.map +1 -1
  59. package/dist/src/layouts/forceatlas2.d.ts +107 -36
  60. package/dist/src/layouts/forceatlas2.d.ts.map +1 -1
  61. package/dist/src/layouts/forceatlas2.js +296 -100
  62. package/dist/src/layouts/forceatlas2.js.map +1 -1
  63. package/dist/src/layouts/fruchterman-reingold.d.ts +73 -27
  64. package/dist/src/layouts/fruchterman-reingold.d.ts.map +1 -1
  65. package/dist/src/layouts/fruchterman-reingold.js +230 -70
  66. package/dist/src/layouts/fruchterman-reingold.js.map +1 -1
  67. package/dist/src/layouts/model-common.d.ts +41 -3
  68. package/dist/src/layouts/model-common.d.ts.map +1 -1
  69. package/dist/src/layouts/model-common.js +74 -3
  70. package/dist/src/layouts/model-common.js.map +1 -1
  71. package/dist/src/layouts/repulsion-grid.d.ts +152 -0
  72. package/dist/src/layouts/repulsion-grid.d.ts.map +1 -0
  73. package/dist/src/layouts/repulsion-grid.js +318 -0
  74. package/dist/src/layouts/repulsion-grid.js.map +1 -0
  75. package/dist/src/layouts/spring-electrical.d.ts +75 -30
  76. package/dist/src/layouts/spring-electrical.d.ts.map +1 -1
  77. package/dist/src/layouts/spring-electrical.js +231 -74
  78. package/dist/src/layouts/spring-electrical.js.map +1 -1
  79. package/dist/src/memory/residency.d.ts +6 -2
  80. package/dist/src/memory/residency.d.ts.map +1 -1
  81. package/dist/src/memory/residency.js +84 -14
  82. package/dist/src/memory/residency.js.map +1 -1
  83. package/dist/src/primitives/core-shape.d.ts +38 -2
  84. package/dist/src/primitives/core-shape.d.ts.map +1 -1
  85. package/dist/src/primitives/core-shape.js +71 -3
  86. package/dist/src/primitives/core-shape.js.map +1 -1
  87. package/dist/src/primitives/grid-pyramid.d.ts +71 -0
  88. package/dist/src/primitives/grid-pyramid.d.ts.map +1 -0
  89. package/dist/src/primitives/grid-pyramid.js +143 -0
  90. package/dist/src/primitives/grid-pyramid.js.map +1 -0
  91. package/dist/src/primitives/grid.d.ts +118 -0
  92. package/dist/src/primitives/grid.d.ts.map +1 -0
  93. package/dist/src/primitives/grid.js +225 -0
  94. package/dist/src/primitives/grid.js.map +1 -0
  95. package/dist/src/primitives/histogram.d.ts +67 -0
  96. package/dist/src/primitives/histogram.d.ts.map +1 -0
  97. package/dist/src/primitives/histogram.js +190 -0
  98. package/dist/src/primitives/histogram.js.map +1 -0
  99. package/dist/src/primitives/radix-sort.d.ts +75 -0
  100. package/dist/src/primitives/radix-sort.d.ts.map +1 -0
  101. package/dist/src/primitives/radix-sort.js +168 -0
  102. package/dist/src/primitives/radix-sort.js.map +1 -0
  103. package/dist/src/primitives/scan.d.ts +44 -0
  104. package/dist/src/primitives/scan.d.ts.map +1 -0
  105. package/dist/src/primitives/scan.js +151 -0
  106. package/dist/src/primitives/scan.js.map +1 -0
  107. package/dist/src/primitives/segmented-reduce.d.ts +25 -17
  108. package/dist/src/primitives/segmented-reduce.d.ts.map +1 -1
  109. package/dist/src/primitives/segmented-reduce.js +166 -47
  110. package/dist/src/primitives/segmented-reduce.js.map +1 -1
  111. package/dist/src/primitives/spmv.d.ts +18 -14
  112. package/dist/src/primitives/spmv.d.ts.map +1 -1
  113. package/dist/src/primitives/spmv.js +94 -58
  114. package/dist/src/primitives/spmv.js.map +1 -1
  115. package/dist/src/primitives/verify.d.ts +49 -0
  116. package/dist/src/primitives/verify.d.ts.map +1 -0
  117. package/dist/src/primitives/verify.js +229 -0
  118. package/dist/src/primitives/verify.js.map +1 -0
  119. package/dist/src/types/context.d.ts +53 -0
  120. package/dist/src/types/context.d.ts.map +1 -1
  121. package/dist/src/types/layout.d.ts +20 -0
  122. package/dist/src/types/layout.d.ts.map +1 -1
  123. package/dist/src/wgsl/counting-scatter.wgsl.d.ts +8 -0
  124. package/dist/src/wgsl/counting-scatter.wgsl.d.ts.map +1 -0
  125. package/dist/src/wgsl/counting-scatter.wgsl.js +17 -0
  126. package/dist/src/wgsl/counting-scatter.wgsl.js.map +1 -0
  127. package/dist/src/wgsl/fa2-attraction.wgsl.d.ts +23 -11
  128. package/dist/src/wgsl/fa2-attraction.wgsl.d.ts.map +1 -1
  129. package/dist/src/wgsl/fa2-attraction.wgsl.js +98 -20
  130. package/dist/src/wgsl/fa2-attraction.wgsl.js.map +1 -1
  131. package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts +6 -2
  132. package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts.map +1 -1
  133. package/dist/src/wgsl/fa2-stats-finalize.wgsl.js +22 -1
  134. package/dist/src/wgsl/fa2-stats-finalize.wgsl.js.map +1 -1
  135. package/dist/src/wgsl/grid-cell-key.wgsl.d.ts +8 -0
  136. package/dist/src/wgsl/grid-cell-key.wgsl.d.ts.map +1 -0
  137. package/dist/src/wgsl/grid-cell-key.wgsl.js +30 -0
  138. package/dist/src/wgsl/grid-cell-key.wgsl.js.map +1 -0
  139. package/dist/src/wgsl/grid-centroid-hub.wgsl.d.ts +8 -0
  140. package/dist/src/wgsl/grid-centroid-hub.wgsl.d.ts.map +1 -0
  141. package/dist/src/wgsl/grid-centroid-hub.wgsl.js +29 -0
  142. package/dist/src/wgsl/grid-centroid-hub.wgsl.js.map +1 -0
  143. package/dist/src/wgsl/grid-centroid.wgsl.d.ts +8 -0
  144. package/dist/src/wgsl/grid-centroid.wgsl.d.ts.map +1 -0
  145. package/dist/src/wgsl/grid-centroid.wgsl.js +29 -0
  146. package/dist/src/wgsl/grid-centroid.wgsl.js.map +1 -0
  147. package/dist/src/wgsl/grid-downsample.wgsl.d.ts +7 -0
  148. package/dist/src/wgsl/grid-downsample.wgsl.d.ts.map +1 -0
  149. package/dist/src/wgsl/grid-downsample.wgsl.js +28 -0
  150. package/dist/src/wgsl/grid-downsample.wgsl.js.map +1 -0
  151. package/dist/src/wgsl/grid-far-field.wgsl.d.ts +13 -0
  152. package/dist/src/wgsl/grid-far-field.wgsl.d.ts.map +1 -0
  153. package/dist/src/wgsl/grid-far-field.wgsl.js +98 -0
  154. package/dist/src/wgsl/grid-far-field.wgsl.js.map +1 -0
  155. package/dist/src/wgsl/grid-near-field.wgsl.d.ts +19 -0
  156. package/dist/src/wgsl/grid-near-field.wgsl.d.ts.map +1 -0
  157. package/dist/src/wgsl/grid-near-field.wgsl.js +129 -0
  158. package/dist/src/wgsl/grid-near-field.wgsl.js.map +1 -0
  159. package/dist/src/wgsl/histogram.wgsl.d.ts +7 -0
  160. package/dist/src/wgsl/histogram.wgsl.d.ts.map +1 -0
  161. package/dist/src/wgsl/histogram.wgsl.js +15 -0
  162. package/dist/src/wgsl/histogram.wgsl.js.map +1 -0
  163. package/dist/src/wgsl/indirect-finalize.wgsl.d.ts +8 -0
  164. package/dist/src/wgsl/indirect-finalize.wgsl.d.ts.map +1 -0
  165. package/dist/src/wgsl/indirect-finalize.wgsl.js +26 -0
  166. package/dist/src/wgsl/indirect-finalize.wgsl.js.map +1 -0
  167. package/dist/src/wgsl/radix-hist.wgsl.d.ts +9 -0
  168. package/dist/src/wgsl/radix-hist.wgsl.d.ts.map +1 -0
  169. package/dist/src/wgsl/radix-hist.wgsl.js +31 -0
  170. package/dist/src/wgsl/radix-hist.wgsl.js.map +1 -0
  171. package/dist/src/wgsl/radix-scatter.wgsl.d.ts +9 -0
  172. package/dist/src/wgsl/radix-scatter.wgsl.d.ts.map +1 -0
  173. package/dist/src/wgsl/radix-scatter.wgsl.js +40 -0
  174. package/dist/src/wgsl/radix-scatter.wgsl.js.map +1 -0
  175. package/dist/src/wgsl/scan-add.wgsl.d.ts +6 -0
  176. package/dist/src/wgsl/scan-add.wgsl.d.ts.map +1 -0
  177. package/dist/src/wgsl/scan-add.wgsl.js +14 -0
  178. package/dist/src/wgsl/scan-add.wgsl.js.map +1 -0
  179. package/dist/src/wgsl/scan-block.wgsl.d.ts +8 -0
  180. package/dist/src/wgsl/scan-block.wgsl.d.ts.map +1 -0
  181. package/dist/src/wgsl/scan-block.wgsl.js +30 -0
  182. package/dist/src/wgsl/scan-block.wgsl.js.map +1 -0
  183. package/dist/src/wgsl/segmented-reduce.wgsl.d.ts +22 -8
  184. package/dist/src/wgsl/segmented-reduce.wgsl.d.ts.map +1 -1
  185. package/dist/src/wgsl/segmented-reduce.wgsl.js +84 -15
  186. package/dist/src/wgsl/segmented-reduce.wgsl.js.map +1 -1
  187. package/dist/src/wgsl/spmv-pull.wgsl.d.ts +22 -11
  188. package/dist/src/wgsl/spmv-pull.wgsl.d.ts.map +1 -1
  189. package/dist/src/wgsl/spmv-pull.wgsl.js +110 -36
  190. package/dist/src/wgsl/spmv-pull.wgsl.js.map +1 -1
  191. package/dist/tsconfig.build.tsbuildinfo +1 -1
  192. package/dist/webgpu-graph-algorithms.js +3815 -1003
  193. package/dist/webgpu-graph-algorithms.js.map +1 -1
  194. package/package.json +5 -4
  195. package/src/algorithms/components.ts +12 -16
  196. package/src/algorithms/degree.ts +58 -43
  197. package/src/algorithms/pagerank.ts +20 -18
  198. package/src/algorithms/power-iteration.ts +19 -18
  199. package/src/constants.ts +38 -8
  200. package/src/errors.ts +3 -1
  201. package/src/index.ts +14 -4
  202. package/src/kernel/dispatch.ts +18 -7
  203. package/src/kernel/kernel.ts +59 -5
  204. package/src/kernel/prelude.ts +9 -0
  205. package/src/kernel/profiler.ts +28 -4
  206. package/src/kernels.ts +356 -18
  207. package/src/layouts/calibrate.ts +187 -0
  208. package/src/layouts/force-simulation.ts +91 -23
  209. package/src/layouts/forceatlas2.ts +331 -106
  210. package/src/layouts/fruchterman-reingold.ts +255 -74
  211. package/src/layouts/model-common.ts +98 -3
  212. package/src/layouts/repulsion-grid.ts +451 -0
  213. package/src/layouts/spring-electrical.ts +257 -78
  214. package/src/memory/residency.ts +126 -20
  215. package/src/primitives/core-shape.ts +91 -4
  216. package/src/primitives/grid-pyramid.ts +221 -0
  217. package/src/primitives/grid.ts +349 -0
  218. package/src/primitives/histogram.ts +273 -0
  219. package/src/primitives/radix-sort.ts +246 -0
  220. package/src/primitives/scan.ts +197 -0
  221. package/src/primitives/segmented-reduce.ts +214 -56
  222. package/src/primitives/spmv.ts +125 -65
  223. package/src/primitives/verify.ts +249 -0
  224. package/src/types/context.ts +56 -0
  225. package/src/types/layout.ts +22 -0
  226. package/src/wgsl/counting-scatter.wgsl.ts +16 -0
  227. package/src/wgsl/fa2-attraction.wgsl.ts +98 -20
  228. package/src/wgsl/fa2-stats-finalize.wgsl.ts +22 -1
  229. package/src/wgsl/grid-cell-key.wgsl.ts +29 -0
  230. package/src/wgsl/grid-centroid-hub.wgsl.ts +28 -0
  231. package/src/wgsl/grid-centroid.wgsl.ts +28 -0
  232. package/src/wgsl/grid-downsample.wgsl.ts +27 -0
  233. package/src/wgsl/grid-far-field.wgsl.ts +97 -0
  234. package/src/wgsl/grid-near-field.wgsl.ts +128 -0
  235. package/src/wgsl/histogram.wgsl.ts +14 -0
  236. package/src/wgsl/indirect-finalize.wgsl.ts +25 -0
  237. package/src/wgsl/radix-hist.wgsl.ts +30 -0
  238. package/src/wgsl/radix-scatter.wgsl.ts +39 -0
  239. package/src/wgsl/scan-add.wgsl.ts +13 -0
  240. package/src/wgsl/scan-block.wgsl.ts +29 -0
  241. package/src/wgsl/segmented-reduce.wgsl.ts +84 -15
  242. package/src/wgsl/spmv-pull.wgsl.ts +110 -36
  243. package/dist/chunks/context-BR7fx3vR.js.map +0 -1
@@ -3,6 +3,7 @@ const WORKGROUP_SIZE = 256;
3
3
  const MAX_WORKGROUPS_PER_DIM = 65535;
4
4
  const MAX_1D_ITEMS = MAX_WORKGROUPS_PER_DIM * WORKGROUP_SIZE;
5
5
  const U32_MAX = 4294967295;
6
+ const RADIX_BINS = 256;
6
7
  const ARC_WINDOW_ALIGN = 64;
7
8
  const STORAGE_ALIGN = 256;
8
9
  const UNIFORM_SLOT_BYTES = 256;
@@ -81,6 +82,12 @@ const SE_DEFAULTS = Object.freeze({
81
82
  iterationsPerStep: 1,
82
83
  maxInFlight: 2
83
84
  });
85
+ const GRID_MIN_SIDE = 8;
86
+ const GRID_COARSEST_SIDE = 4;
87
+ const GRID_HUB_CELL = 1024;
88
+ const GRID_EXTENT_FLOOR = 1e-6;
89
+ const GRID_BBOX_MARGIN = 1.01;
90
+ const GRID_SORT_BITS = 24;
84
91
  const PASSTHROUGH_FORMAT_CODES = Object.freeze(["E_GPU_INELIGIBLE", "E_UNKNOWN_NODE", "E_UNKNOWN_COLUMN", "E_COLUMN_LENGTH"]);
85
92
  const EMPTY_DETAILS = Object.freeze({});
86
93
  class WebGpuGraphError extends Error {
@@ -543,6 +550,7 @@ function deviceLostError(info) {
543
550
  message: info.message
544
551
  });
545
552
  }
553
+ const INDIRECT_ARGS_STRIDE = 16;
546
554
  const bufferIds = /* @__PURE__ */ new WeakMap();
547
555
  let nextBufferId = 1;
548
556
  function bufferId(buffer) {
@@ -733,6 +741,42 @@ class Kernel {
733
741
  * @param dynamicOffsets - one byte offset per entry of bound.dynamicGroups
734
742
  */
735
743
  dispatch(pass, bound, plan, dynamicOffsets) {
744
+ this.check(bound, dynamicOffsets);
745
+ if (plan.x === 0) {
746
+ return;
747
+ }
748
+ this.setUp(pass, bound, dynamicOffsets);
749
+ pass.dispatchWorkgroups(plan.x, plan.y, 1);
750
+ }
751
+ /**
752
+ * setPipeline + the bind groups exactly as dispatch(), then dispatchWorkgroupsIndirect(args.buffer, args.offset +
753
+ * INDIRECT_ARGS_STRIDE * slot) (spec 5.4): the (x, y, 1) of the slot were written by the indirect-finalize kernel
754
+ * earlier in the same pass. A slot beyond the binding is E_INVALID_ARGUMENT.
755
+ * @param pass - the open compute pass
756
+ * @param bound - a BoundKernel of THIS kernel
757
+ * @param args - the args buffer range (usage INDIRECT | STORAGE)
758
+ * @param slot - the 16-byte slot index inside the range
759
+ * @param dynamicOffsets - one byte offset per entry of bound.dynamicGroups
760
+ */
761
+ dispatchIndirect(pass, bound, args, slot, dynamicOffsets) {
762
+ this.check(bound, dynamicOffsets);
763
+ if (!Number.isInteger(slot) || slot < 0 || (slot + 1) * INDIRECT_ARGS_STRIDE > args.size) {
764
+ throw new WebGpuGraphError("E_INVALID_ARGUMENT", `${this.spec.id}: args slot ${slot} is outside the binding`, {
765
+ argument: "slot",
766
+ value: slot,
767
+ expected: `0 <= slot < ${Math.floor(args.size / INDIRECT_ARGS_STRIDE)}`
768
+ });
769
+ }
770
+ this.setUp(pass, bound, dynamicOffsets);
771
+ pass.dispatchWorkgroupsIndirect(args.buffer, args.offset + INDIRECT_ARGS_STRIDE * slot);
772
+ }
773
+ /**
774
+ * The argument checks dispatch() and dispatchIndirect() share: a BoundKernel of another kernel or an offset list
775
+ * of the wrong length is E_INVALID_ARGUMENT.
776
+ * @param bound - the bound kernel
777
+ * @param dynamicOffsets - the offsets, if any
778
+ */
779
+ check(bound, dynamicOffsets) {
736
780
  const { id } = this.spec;
737
781
  if (bound.kernel !== this) {
738
782
  throw new WebGpuGraphError(
@@ -756,9 +800,15 @@ class Kernel {
756
800
  }
757
801
  );
758
802
  }
759
- if (plan.x === 0) {
760
- return;
761
- }
803
+ }
804
+ /**
805
+ * setPipeline + setBindGroup for every group (the dynamic offsets replicated over the uniform bindings of each
806
+ * dynamic group), the prefix dispatch() and dispatchIndirect() share.
807
+ * @param pass - the open compute pass
808
+ * @param bound - a BoundKernel of this kernel
809
+ * @param dynamicOffsets - one byte offset per entry of bound.dynamicGroups (absent = 0)
810
+ */
811
+ setUp(pass, bound, dynamicOffsets) {
762
812
  pass.setPipeline(this.pipeline);
763
813
  let next = 0;
764
814
  bound.bindGroups.forEach((bindGroup, group) => {
@@ -775,7 +825,6 @@ class Kernel {
775
825
  pass.setBindGroup(group, bindGroup);
776
826
  }
777
827
  });
778
- pass.dispatchWorkgroups(plan.x, plan.y, 1);
779
828
  }
780
829
  /** Drops cached bind groups (a layout's buffers changed). */
781
830
  invalidate() {
@@ -842,6 +891,11 @@ const FA2_FLAG_FIRST: u32 = ${FA2_FLAG_FIRST}u;
842
891
  const FA2_FLAG_ADAPTIVE: u32 = ${FA2_FLAG_ADAPTIVE}u;
843
892
  const FR_COOLING_STEP: f32 = ${wgslF32Literal(FR_COOLING_STEP)};
844
893
  const FR_COOLING_PATIENCE: u32 = ${FR_COOLING_PATIENCE}u;
894
+ const GRID_HUB_CELL: u32 = ${GRID_HUB_CELL}u;
895
+ const GRID_EXTENT_FLOOR: f32 = ${wgslF32Literal(GRID_EXTENT_FLOOR)};
896
+ const GRID_BBOX_MARGIN: f32 = ${wgslF32Literal(GRID_BBOX_MARGIN)};
897
+ const RADIX_BINS: u32 = ${RADIX_BINS}u;
898
+ const RADIX_DIGIT_MASK: u32 = ${RADIX_BINS - 1}u;
845
899
  const F32_MAX: f32 = 0x1.fffffep+127;
846
900
  override WG: u32 = ${WORKGROUP_SIZE}u;
847
901
  override USE_PERM: bool = false;
@@ -1510,8 +1564,10 @@ class Profiler {
1510
1564
  */
1511
1565
  constructor(device, enabled, quantised, slots) {
1512
1566
  this.labels = /* @__PURE__ */ new WeakMap();
1567
+ this.partialRequests = /* @__PURE__ */ new WeakSet();
1513
1568
  this.pending = [];
1514
1569
  this.cursor = 0;
1570
+ this.refused = 0;
1515
1571
  this.destroyed = false;
1516
1572
  const count = slots ?? PROFILER_QUERY_SLOTS;
1517
1573
  this.quantised = quantised;
@@ -1531,12 +1587,17 @@ class Profiler {
1531
1587
  }
1532
1588
  }
1533
1589
  /**
1534
- * The timestampWrites descriptor for a new pass, or undefined when disabled / out of slots.
1590
+ * The timestampWrites descriptor for a new pass, or undefined when disabled / out of slots. A pass refused for
1591
+ * want of slots is counted, and the next `resolveInto()` marks its request partial.
1535
1592
  * @param label - the pass name reported by `timings()`
1536
1593
  * @returns the descriptor a CommandBatch puts on beginComputePass, or undefined
1537
1594
  */
1538
1595
  beginPass(label) {
1539
- if (this.querySet === null || this.destroyed || this.cursor + 2 > this.slots) {
1596
+ if (this.querySet === null || this.destroyed) {
1597
+ return void 0;
1598
+ }
1599
+ if (this.cursor + 2 > this.slots) {
1600
+ this.refused++;
1540
1601
  return void 0;
1541
1602
  }
1542
1603
  const begin = this.cursor;
@@ -1561,10 +1622,23 @@ class Profiler {
1561
1622
  request,
1562
1623
  this.pending.map((pass) => pass.label)
1563
1624
  );
1625
+ if (this.refused > 0) {
1626
+ this.partialRequests.add(request);
1627
+ }
1564
1628
  this.pending = [];
1565
1629
  this.cursor = 0;
1630
+ this.refused = 0;
1566
1631
  return request;
1567
1632
  }
1633
+ /**
1634
+ * Whether a pass of the batch was refused a slot pair, so `timings()` covers only its first slots / 2 passes
1635
+ * and their sum is not the batch's duration.
1636
+ * @param request - the request `resolveInto()` returned
1637
+ * @returns true when at least one pass of that batch went untimed
1638
+ */
1639
+ partial(request) {
1640
+ return this.partialRequests.has(request);
1641
+ }
1568
1642
  /**
1569
1643
  * Decodes the timings of a batch from its readback bytes.
1570
1644
  * @param bytes - the resolved readback of the batch
@@ -1597,6 +1671,7 @@ class Profiler {
1597
1671
  this.destroyed = true;
1598
1672
  this.pending = [];
1599
1673
  this.cursor = 0;
1674
+ this.refused = 0;
1600
1675
  if (this.querySet !== null) {
1601
1676
  this.querySet.destroy();
1602
1677
  }
@@ -2165,7 +2240,7 @@ class Readback {
2165
2240
  }
2166
2241
  }
2167
2242
  const CORE_ORDER$1 = Object.freeze(["rowPtr", "colIdx", "weights", "arcToEdge", "edgeToArc"]);
2168
- const ARC_INDEXED = /* @__PURE__ */ new Set(["colIdx", "weights", "arcToEdge"]);
2243
+ const ARC_INDEXED$1 = /* @__PURE__ */ new Set(["colIdx", "weights", "arcToEdge"]);
2169
2244
  function coreByteLengths(s) {
2170
2245
  const arcs = s.arcCount;
2171
2246
  const identity = s.flags.arcToEdgeIsIdentity;
@@ -2339,13 +2414,17 @@ function planUpload(s, caps, need) {
2339
2414
  const windows = planArcWindows(s.rowPtr, s.arcCount, arcsPerWindow);
2340
2415
  const { buffers, placed } = placeWindows(windows, 4 * s.arcCount, bufferLimit);
2341
2416
  const arrays = names.map(
2342
- (name) => ARC_INDEXED.has(name) ? { name, byteLength: lengths[name], buffers } : { name, byteLength: lengths[name], buffers: [{ byteOffset: 0, byteLength: lengths[name] }] }
2417
+ (name) => ARC_INDEXED$1.has(name) ? { name, byteLength: lengths[name], buffers } : { name, byteLength: lengths[name], buffers: [{ byteOffset: 0, byteLength: lengths[name] }] }
2343
2418
  );
2344
2419
  return { kind: "windowed", arrays, windows: placed, arcsPerWindow };
2345
2420
  }
2346
2421
  const DEFAULT_NEED = Object.freeze(["rowPtr", "colIdx", "weights"]);
2347
2422
  const CORE_ORDER = Object.freeze(["rowPtr", "colIdx", "weights", "arcToEdge", "edgeToArc"]);
2423
+ const ARC_INDEXED = Object.freeze(["colIdx", "weights", "arcToEdge"]);
2348
2424
  const RESIDENT_USAGE = BufferUsage.STORAGE | BufferUsage.COPY_DST | BufferUsage.COPY_SRC;
2425
+ function isArcIndexed(name) {
2426
+ return ARC_INDEXED.includes(name);
2427
+ }
2349
2428
  function isSharedBuffer(buffer) {
2350
2429
  return Object.prototype.toString.call(buffer) === "[object SharedArrayBuffer]";
2351
2430
  }
@@ -2431,8 +2510,9 @@ class GraphResidency {
2431
2510
  }
2432
2511
  /**
2433
2512
  * Uploads (or finds) the core; `need` defaults to ["rowPtr", "colIdx", "weights"]; cold segments on demand
2434
- * (spec 4.2). Never materialises an identity permutation. A windowed plan throws E_TOO_LARGE { path: "windowed" }
2435
- * until P4 (PLAN DECISION 2); a tombstoned serial is lifted and re-uploaded (PLAN DECISION 7).
2513
+ * (spec 4.2). Never materialises an identity permutation. A windowed plan (spec 4.2, PD-8) uploads every
2514
+ * arc-indexed array as the plan's buffer ranges and binds window 0 as its default binding; rowPtr and edgeToArc
2515
+ * stay whole. A tombstoned serial is lifted and re-uploaded (PLAN DECISION 7).
2436
2516
  * @param s - the snapshot
2437
2517
  * @param need - the core arrays to bind (rowPtr is always included; absent arrays are ignored)
2438
2518
  * @returns the core binding (a frozen object; grows as cold segments are added)
@@ -2443,18 +2523,6 @@ class GraphResidency {
2443
2523
  const wanted = need ?? DEFAULT_NEED;
2444
2524
  const names = CORE_ORDER.filter((name) => (name === "rowPtr" || wanted.includes(name)) && isPresent(s, name));
2445
2525
  const plan = planUpload(s, this.caps, names);
2446
- if (plan.kind === "windowed") {
2447
- throw new WebGpuGraphError(
2448
- "E_TOO_LARGE",
2449
- `snapshot ${s.serial}: an arc array of ${4 * s.arcCount} bytes needs arc windows (${plan.windows.length}), which P1-P3 plan but do not execute`,
2450
- {
2451
- needed: 4 * s.arcCount,
2452
- limit: this.caps.limits.maxStorageBufferBindingSize,
2453
- path: "windowed",
2454
- algorithm: null
2455
- }
2456
- );
2457
- }
2458
2526
  const record = this.ensureRecord(s);
2459
2527
  if (record.plan === null) {
2460
2528
  record.plan = plan.kind;
@@ -2468,10 +2536,17 @@ class GraphResidency {
2468
2536
  );
2469
2537
  record.arena = { buffer: resident.buffer, segments: plan.segments };
2470
2538
  }
2539
+ if (plan.kind === "windowed") {
2540
+ record.windows = plan.windows;
2541
+ }
2471
2542
  }
2472
2543
  for (const name of names) {
2473
2544
  if (!record.bindings.has(name)) {
2474
- record.bindings.set(name, this.bindCore(record, s, name));
2545
+ const planned = plan.kind === "windowed" ? plan.arrays.find((a) => a.name === name) : void 0;
2546
+ record.bindings.set(
2547
+ name,
2548
+ planned !== void 0 && planned.buffers.length > 0 && record.windows !== null && isArcIndexed(name) ? this.bindWindowed(record, s, name, planned.buffers, record.windows[0]) : this.bindCore(record, s, name)
2549
+ );
2475
2550
  }
2476
2551
  }
2477
2552
  const rowPtr = record.bindings.get("rowPtr");
@@ -2487,7 +2562,12 @@ class GraphResidency {
2487
2562
  weights,
2488
2563
  arcToEdge: record.bindings.get("arcToEdge") ?? null,
2489
2564
  edgeToArc: record.bindings.get("edgeToArc") ?? null,
2490
- windows: null,
2565
+ windows: record.windows,
2566
+ arcBuffers: record.windows === null ? null : Object.freeze({
2567
+ colIdx: Object.freeze([...record.arcBuffers.get("colIdx") ?? []]),
2568
+ weights: Object.freeze([...record.arcBuffers.get("weights") ?? []]),
2569
+ arcToEdge: Object.freeze([...record.arcBuffers.get("arcToEdge") ?? []])
2570
+ }),
2491
2571
  hasWeights: weights !== null
2492
2572
  });
2493
2573
  }
@@ -2593,6 +2673,18 @@ class GraphResidency {
2593
2673
  const rev = s.reverse();
2594
2674
  if (!s.directed) {
2595
2675
  const core = this.core(s, ["rowPtr", "colIdx", "weights"]);
2676
+ if (core.plan === "windowed") {
2677
+ throw new WebGpuGraphError(
2678
+ "E_TOO_LARGE",
2679
+ `snapshot ${s.serial}: the undirected reverse view is the core, which needs arc windows (${core.windows?.length ?? 0}) that no view executes`,
2680
+ {
2681
+ needed: 4 * s.arcCount,
2682
+ limit: this.caps.limits.maxStorageBufferBindingSize,
2683
+ path: "windowed",
2684
+ algorithm: null
2685
+ }
2686
+ );
2687
+ }
2596
2688
  const bindings2 = { rowPtr: core.rowPtr };
2597
2689
  if (core.colIdx !== null) {
2598
2690
  bindings2.colIdx = core.colIdx;
@@ -2892,6 +2984,50 @@ class GraphResidency {
2892
2984
  const resident = this.upload(record, array, array, `residency:core:${record.serial}:${name}`);
2893
2985
  return { buffer: resident.buffer, offset: 0, size: resident.byteLength, window: null };
2894
2986
  }
2987
+ /**
2988
+ * Uploads one arc-indexed array as the windowed plan's buffer ranges (spec 4.2: an array above maxBufferSize is
2989
+ * split across buffers at window boundaries), each keyed on a marker object of the record (windowKey), and
2990
+ * returns window 0's binding so a caller that ignores windows still binds something valid (PD-8).
2991
+ * @param record - the snapshot's record
2992
+ * @param s - the snapshot
2993
+ * @param name - a PRESENT arc-indexed core array
2994
+ * @param ranges - the plan's buffer ranges of the array
2995
+ * @param first - window 0
2996
+ * @returns the binding of window 0
2997
+ */
2998
+ bindWindowed(record, s, name, ranges, first) {
2999
+ const array = coreArray(s, name);
3000
+ const buffers = ranges.map(
3001
+ (range, index) => this.upload(
3002
+ record,
3003
+ this.windowKey(record, `${name}:${index}`),
3004
+ new Uint8Array(array.buffer, array.byteOffset + range.byteOffset, range.byteLength),
3005
+ `residency:core:${record.serial}:${name}:w${index}`
3006
+ ).buffer
3007
+ );
3008
+ record.arcBuffers.set(name, buffers);
3009
+ return {
3010
+ buffer: buffers[first.bufferIndex],
3011
+ offset: first.offset,
3012
+ size: 4 * (first.end - first.start),
3013
+ window: first
3014
+ };
3015
+ }
3016
+ /**
3017
+ * The upload key of one window buffer: a marker object allocated once per record and `<name>:<bufferIndex>`.
3018
+ * @param record - the owning record
3019
+ * @param memoKey - `<name>:<bufferIndex>`
3020
+ * @returns the stable marker object
3021
+ */
3022
+ windowKey(record, memoKey) {
3023
+ const existing = record.windowKeys.get(memoKey);
3024
+ if (existing !== void 0) {
3025
+ return existing;
3026
+ }
3027
+ const created = {};
3028
+ record.windowKeys.set(memoKey, created);
3029
+ return created;
3030
+ }
2895
3031
  /**
2896
3032
  * Uploads `data` into a new buffer keyed on `key`, or returns the resident already uploaded for that key by
2897
3033
  * the same owner (PLAN DECISION 11: a resident belongs to one record).
@@ -2955,6 +3091,9 @@ class GraphResidency {
2955
3091
  record.bindings.clear();
2956
3092
  record.views.clear();
2957
3093
  record.packKeys.clear();
3094
+ record.windowKeys.clear();
3095
+ record.arcBuffers.clear();
3096
+ record.windows = null;
2958
3097
  record.arena = null;
2959
3098
  record.plan = null;
2960
3099
  this.bySerial.delete(record.serial);
@@ -2982,6 +3121,9 @@ class GraphResidency {
2982
3121
  bindings: /* @__PURE__ */ new Map(),
2983
3122
  views: /* @__PURE__ */ new Map(),
2984
3123
  packKeys: /* @__PURE__ */ new Map(),
3124
+ windowKeys: /* @__PURE__ */ new Map(),
3125
+ windows: null,
3126
+ arcBuffers: /* @__PURE__ */ new Map(),
2985
3127
  released: false
2986
3128
  };
2987
3129
  this.bySerial.set(s.serial, record);
@@ -3307,31 +3449,39 @@ export {
3307
3449
  EXACT_MAX_NODES as E,
3308
3450
  FA2_DEFAULTS as F,
3309
3451
  GpuContext as G,
3452
+ INDIRECT_ARGS_STRIDE as I,
3310
3453
  LAYOUT_TUNING_DEFAULTS as L,
3311
3454
  MAX_WORKGROUPS_PER_DIM as M,
3312
3455
  PARTIAL_BYTES as P,
3456
+ RADIX_BINS as R,
3313
3457
  STATE_HEADER_BYTES as S,
3314
3458
  TRACE_RECORD_BYTES as T,
3315
3459
  UNIFORM_SLOT_BYTES as U,
3316
3460
  WebGpuGraphError as W,
3317
3461
  WGSL_RESERVED_WORDS as a,
3318
3462
  U32_MAX as b,
3319
- MAX_ITERATIONS_PER_STEP as c,
3463
+ GRID_COARSEST_SIDE as c,
3320
3464
  deviceLostError as d,
3321
- MAX_1D_ITEMS as e,
3322
- FA2_FLAG_FIRST as f,
3323
- FR_ADAPTIVE_MAX_ITERATIONS as g,
3324
- hasErrorCode as h,
3465
+ GRID_MIN_SIDE as e,
3466
+ GRID_SORT_BITS as f,
3467
+ MAX_ITERATIONS_PER_STEP as g,
3468
+ MAX_1D_ITEMS as h,
3325
3469
  isWebGpuGraphError as i,
3326
- FR_START_TEMPERATURE as j,
3327
- FA2_FLAG_ADAPTIVE as k,
3328
- FR_REHEAT_FRACTION as l,
3329
- FR_DEFAULTS as m,
3330
- SE_DEFAULTS as n,
3331
- SE_SCALE_REFERENCE_NODES as o,
3332
- PASSTHROUGH_FORMAT_CODES as p,
3333
- STORAGE_ALIGN as q,
3334
- WORKGROUP_SIZE as r,
3335
- isSoftwareAdapter as s
3470
+ hasErrorCode as j,
3471
+ FA2_FLAG_FIRST as k,
3472
+ GRID_HUB_CELL as l,
3473
+ GRID_BBOX_MARGIN as m,
3474
+ GRID_EXTENT_FLOOR as n,
3475
+ FR_ADAPTIVE_MAX_ITERATIONS as o,
3476
+ FR_START_TEMPERATURE as p,
3477
+ FA2_FLAG_ADAPTIVE as q,
3478
+ FR_REHEAT_FRACTION as r,
3479
+ FR_DEFAULTS as s,
3480
+ SE_DEFAULTS as t,
3481
+ SE_SCALE_REFERENCE_NODES as u,
3482
+ PASSTHROUGH_FORMAT_CODES as v,
3483
+ STORAGE_ALIGN as w,
3484
+ WORKGROUP_SIZE as x,
3485
+ isSoftwareAdapter as y
3336
3486
  };
3337
- //# sourceMappingURL=context-BR7fx3vR.js.map
3487
+ //# sourceMappingURL=context-BXqgCifx.js.map