@jbrowse/render-core 5.0.0-beta.1 → 5.0.0-beta.11

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 (353) hide show
  1. package/esm/RenderCanvas.d.ts +3 -3
  2. package/esm/RenderCanvas.js +1 -1
  3. package/esm/RenderLifecycleMixin.d.ts +52 -20
  4. package/esm/RenderLifecycleMixin.js +89 -23
  5. package/esm/canvas2dUtils.d.ts +90 -23
  6. package/esm/canvas2dUtils.js +120 -37
  7. package/esm/colorRampLut.d.ts +5 -5
  8. package/esm/colorRampLut.js +6 -6
  9. package/esm/computePipeline.d.ts +14 -0
  10. package/esm/computePipeline.js +62 -0
  11. package/esm/contractReports.d.ts +54 -0
  12. package/esm/contractReports.js +112 -0
  13. package/esm/coverageBand.d.ts +13 -8
  14. package/esm/coverageBand.js +15 -5
  15. package/esm/coverageBandBuffers.d.ts +2 -9
  16. package/esm/coverageBandBuffers.js +1 -13
  17. package/esm/createRenderingBackend.d.ts +16 -20
  18. package/esm/createRenderingBackend.js +6 -4
  19. package/esm/displayPhase.d.ts +83 -104
  20. package/esm/displayPhase.js +42 -82
  21. package/esm/encodeMemo.d.ts +14 -0
  22. package/esm/encodeMemo.js +48 -0
  23. package/esm/gpuContextLostError.d.ts +30 -0
  24. package/esm/gpuContextLostError.js +40 -0
  25. package/esm/gpuDevice.d.ts +29 -10
  26. package/esm/gpuDevice.js +35 -16
  27. package/esm/graphicsCapabilities.d.ts +27 -19
  28. package/esm/graphicsCapabilities.js +41 -19
  29. package/esm/hal/createHal.d.ts +4 -5
  30. package/esm/hal/createHal.js +15 -5
  31. package/esm/hal/deviceGpuCache.d.ts +25 -30
  32. package/esm/hal/deviceGpuCache.js +85 -97
  33. package/esm/hal/gpuHalBase.d.ts +5 -4
  34. package/esm/hal/gpuHalBase.js +25 -14
  35. package/esm/hal/index.d.ts +2 -2
  36. package/esm/hal/index.js +1 -1
  37. package/esm/hal/mockHal.d.ts +15 -10
  38. package/esm/hal/mockHal.js +31 -18
  39. package/esm/hal/oomReporter.d.ts +7 -0
  40. package/esm/hal/oomReporter.js +10 -0
  41. package/esm/hal/passIds.d.ts +8 -7
  42. package/esm/hal/passIds.js +11 -10
  43. package/esm/hal/regionRegistry.d.ts +0 -1
  44. package/esm/hal/regionRegistry.js +3 -15
  45. package/esm/hal/types.d.ts +53 -18
  46. package/esm/hal/webgl2Hal.d.ts +23 -6
  47. package/esm/hal/webgl2Hal.js +158 -155
  48. package/esm/hal/webgpuHal.d.ts +29 -13
  49. package/esm/hal/webgpuHal.js +220 -153
  50. package/esm/installUpload.d.ts +16 -3
  51. package/esm/installUpload.js +13 -41
  52. package/esm/instanceCache.d.ts +10 -7
  53. package/esm/instanceCache.js +14 -15
  54. package/esm/instancePass.d.ts +2 -1
  55. package/esm/mapUploadSync.d.ts +2 -10
  56. package/esm/mapUploadSync.js +2 -3
  57. package/esm/marks/barMark.d.ts +30 -0
  58. package/esm/marks/barMark.js +149 -0
  59. package/esm/marks/colorFill.d.ts +17 -0
  60. package/esm/marks/colorFill.js +26 -0
  61. package/esm/marks/drawAgainstHit.d.ts +113 -0
  62. package/esm/marks/drawAgainstHit.js +407 -0
  63. package/esm/marks/ellipseDistance.d.ts +10 -0
  64. package/esm/marks/ellipseDistance.js +136 -0
  65. package/esm/marks/glyphPaint.d.ts +36 -0
  66. package/esm/marks/glyphPaint.js +77 -0
  67. package/esm/marks/index.d.ts +33 -0
  68. package/esm/marks/index.js +20 -0
  69. package/esm/marks/lineMark.d.ts +37 -0
  70. package/esm/marks/lineMark.js +416 -0
  71. package/esm/marks/linkMark.d.ts +76 -0
  72. package/esm/marks/linkMark.js +585 -0
  73. package/esm/marks/markBackend.d.ts +118 -0
  74. package/esm/marks/markBackend.js +196 -0
  75. package/esm/marks/markHit.d.ts +32 -0
  76. package/esm/marks/markHit.js +61 -0
  77. package/esm/marks/markInk.d.ts +18 -0
  78. package/esm/marks/markInk.js +71 -0
  79. package/esm/marks/markPaint.d.ts +14 -0
  80. package/esm/marks/markPaint.js +32 -0
  81. package/esm/marks/markPlan.d.ts +25 -0
  82. package/esm/marks/markPlan.js +20 -0
  83. package/esm/marks/markRamp.d.ts +74 -0
  84. package/esm/marks/markRamp.js +205 -0
  85. package/esm/marks/nearestMarkHit.d.ts +55 -0
  86. package/esm/marks/nearestMarkHit.js +158 -0
  87. package/esm/marks/pathRecorder.d.ts +14 -0
  88. package/esm/marks/pathRecorder.js +80 -0
  89. package/esm/marks/pointMark.d.ts +42 -0
  90. package/esm/marks/pointMark.js +125 -0
  91. package/esm/marks/rowLane.d.ts +39 -0
  92. package/esm/marks/rowLane.js +41 -0
  93. package/esm/marks/rowSpanIndex.d.ts +30 -0
  94. package/esm/marks/rowSpanIndex.js +151 -0
  95. package/esm/marks/rowTable.d.ts +41 -0
  96. package/esm/marks/rowTable.js +75 -0
  97. package/esm/marks/ruleMark.d.ts +21 -0
  98. package/esm/marks/ruleMark.js +91 -0
  99. package/esm/marks/spanMark.d.ts +42 -0
  100. package/esm/marks/spanMark.js +81 -0
  101. package/esm/marks/types.d.ts +261 -0
  102. package/esm/marks/types.js +175 -0
  103. package/esm/marks/valueScale.d.ts +23 -0
  104. package/esm/marks/valueScale.js +11 -0
  105. package/esm/namedReactions.d.ts +3 -4
  106. package/esm/namedReactions.js +3 -4
  107. package/esm/perRegionRenderingBackend.d.ts +21 -3
  108. package/esm/perRegionRenderingBackend.js +37 -3
  109. package/esm/regionDataMap.d.ts +2 -2
  110. package/esm/regionDataMap.js +25 -23
  111. package/esm/renderBlock.d.ts +14 -0
  112. package/esm/renderBlock.js +25 -0
  113. package/esm/renderingBackendBase.d.ts +20 -14
  114. package/esm/renderingBackendBase.js +12 -7
  115. package/esm/scoreScale.d.ts +27 -0
  116. package/esm/scoreScale.js +88 -0
  117. package/esm/shaders/barMark.generated.d.ts +3 -0
  118. package/esm/shaders/barMark.generated.js +7 -0
  119. package/esm/shaders/barMark.glsl.generated.d.ts +2 -0
  120. package/esm/shaders/barMark.glsl.generated.js +4 -0
  121. package/esm/shaders/barMark.iface.generated.d.ts +90 -0
  122. package/esm/shaders/barMark.iface.generated.js +205 -0
  123. package/esm/shaders/barMark.wgsl.generated.d.ts +1 -0
  124. package/esm/shaders/barMark.wgsl.generated.js +3 -0
  125. package/esm/shaders/capsule.js.generated.d.ts +1 -0
  126. package/esm/shaders/capsule.js.generated.js +11 -0
  127. package/esm/shaders/clipStrip.generated.d.ts +2 -0
  128. package/esm/shaders/clipStrip.generated.js +4 -0
  129. package/esm/shaders/clipStrip.js.generated.d.ts +1 -0
  130. package/esm/shaders/clipStrip.js.generated.js +21 -0
  131. package/esm/shaders/colorRampLut.js.generated.d.ts +1 -0
  132. package/esm/shaders/colorRampLut.js.generated.js +17 -0
  133. package/esm/shaders/coverageBar.generated.d.ts +2 -3
  134. package/esm/shaders/coverageBar.generated.js +4 -3
  135. package/esm/shaders/coverageBar.glsl.generated.d.ts +2 -0
  136. package/esm/shaders/coverageBar.glsl.generated.js +4 -0
  137. package/esm/shaders/coverageBar.iface.generated.js +1 -1
  138. package/esm/shaders/coverageBar.wgsl.generated.d.ts +1 -0
  139. package/esm/shaders/coverageBar.wgsl.generated.js +3 -0
  140. package/esm/shaders/coverageClip.generated.d.ts +3 -0
  141. package/esm/shaders/coverageClip.generated.js +7 -0
  142. package/esm/shaders/coverageClip.glsl.generated.d.ts +2 -0
  143. package/esm/shaders/coverageClip.glsl.generated.js +4 -0
  144. package/esm/shaders/coverageClip.iface.generated.d.ts +85 -0
  145. package/esm/shaders/coverageClip.iface.generated.js +114 -0
  146. package/esm/shaders/coverageClip.wgsl.generated.d.ts +1 -0
  147. package/esm/shaders/coverageClip.wgsl.generated.js +3 -0
  148. package/esm/shaders/coverageIndicator.generated.d.ts +2 -3
  149. package/esm/shaders/coverageIndicator.generated.js +4 -3
  150. package/esm/shaders/coverageIndicator.glsl.generated.d.ts +2 -0
  151. package/esm/shaders/coverageIndicator.glsl.generated.js +4 -0
  152. package/esm/shaders/coverageIndicator.iface.generated.js +1 -1
  153. package/esm/shaders/coverageIndicator.wgsl.generated.d.ts +1 -0
  154. package/esm/shaders/coverageIndicator.wgsl.generated.js +3 -0
  155. package/esm/shaders/coverageInterbase.generated.d.ts +2 -3
  156. package/esm/shaders/coverageInterbase.generated.js +4 -3
  157. package/esm/shaders/coverageInterbase.glsl.generated.d.ts +2 -0
  158. package/esm/shaders/coverageInterbase.glsl.generated.js +4 -0
  159. package/esm/shaders/coverageInterbase.iface.generated.js +1 -1
  160. package/esm/shaders/coverageInterbase.wgsl.generated.d.ts +1 -0
  161. package/esm/shaders/coverageInterbase.wgsl.generated.js +3 -0
  162. package/esm/shaders/coverageMod.generated.d.ts +2 -3
  163. package/esm/shaders/coverageMod.generated.js +4 -3
  164. package/esm/shaders/coverageMod.glsl.generated.d.ts +2 -0
  165. package/esm/shaders/coverageMod.glsl.generated.js +4 -0
  166. package/esm/shaders/coverageMod.iface.generated.js +1 -1
  167. package/esm/shaders/coverageMod.wgsl.generated.d.ts +1 -0
  168. package/esm/shaders/coverageMod.wgsl.generated.js +3 -0
  169. package/esm/shaders/coverageSnp.generated.d.ts +2 -3
  170. package/esm/shaders/coverageSnp.generated.js +4 -3
  171. package/esm/shaders/coverageSnp.glsl.generated.d.ts +2 -0
  172. package/esm/shaders/coverageSnp.glsl.generated.js +4 -0
  173. package/esm/shaders/coverageSnp.iface.generated.js +1 -1
  174. package/esm/shaders/coverageSnp.wgsl.generated.d.ts +1 -0
  175. package/esm/shaders/coverageSnp.wgsl.generated.js +3 -0
  176. package/esm/shaders/curveDistance.js.generated.d.ts +5 -0
  177. package/esm/shaders/curveDistance.js.generated.js +87 -0
  178. package/esm/shaders/hpmath.js.generated.d.ts +2 -1
  179. package/esm/shaders/hpmath.js.generated.js +5 -2
  180. package/esm/shaders/lineCenterMark.generated.d.ts +3 -0
  181. package/esm/shaders/lineCenterMark.generated.js +7 -0
  182. package/esm/shaders/lineCenterMark.glsl.generated.d.ts +2 -0
  183. package/esm/shaders/lineCenterMark.glsl.generated.js +4 -0
  184. package/esm/shaders/lineCenterMark.iface.generated.d.ts +104 -0
  185. package/esm/shaders/lineCenterMark.iface.generated.js +237 -0
  186. package/esm/shaders/lineCenterMark.wgsl.generated.d.ts +1 -0
  187. package/esm/shaders/lineCenterMark.wgsl.generated.js +3 -0
  188. package/esm/shaders/lineCommon.generated.d.ts +2 -0
  189. package/esm/shaders/lineCommon.generated.js +4 -0
  190. package/esm/shaders/lineStepMark.generated.d.ts +3 -0
  191. package/esm/shaders/lineStepMark.generated.js +7 -0
  192. package/esm/shaders/lineStepMark.glsl.generated.d.ts +2 -0
  193. package/esm/shaders/lineStepMark.glsl.generated.js +4 -0
  194. package/esm/shaders/lineStepMark.iface.generated.d.ts +98 -0
  195. package/esm/shaders/lineStepMark.iface.generated.js +225 -0
  196. package/esm/shaders/lineStepMark.wgsl.generated.d.ts +1 -0
  197. package/esm/shaders/lineStepMark.wgsl.generated.js +3 -0
  198. package/esm/shaders/linkMark.consts.generated.d.ts +14 -0
  199. package/esm/shaders/linkMark.consts.generated.js +16 -0
  200. package/esm/shaders/linkMark.generated.d.ts +4 -0
  201. package/esm/shaders/linkMark.generated.js +8 -0
  202. package/esm/shaders/linkMark.glsl.generated.d.ts +2 -0
  203. package/esm/shaders/linkMark.glsl.generated.js +4 -0
  204. package/esm/shaders/linkMark.iface.generated.d.ts +142 -0
  205. package/esm/shaders/linkMark.iface.generated.js +1829 -0
  206. package/esm/shaders/linkMark.js.generated.d.ts +7 -0
  207. package/esm/shaders/linkMark.js.generated.js +159 -0
  208. package/esm/shaders/linkMark.wgsl.generated.d.ts +1 -0
  209. package/esm/shaders/linkMark.wgsl.generated.js +3 -0
  210. package/esm/shaders/markColor.generated.d.ts +9 -0
  211. package/esm/shaders/markColor.generated.js +11 -0
  212. package/esm/shaders/pointGlyph.js.generated.d.ts +2 -0
  213. package/esm/shaders/pointGlyph.js.generated.js +15 -0
  214. package/esm/shaders/pointMark.consts.generated.d.ts +7 -0
  215. package/esm/shaders/pointMark.consts.generated.js +9 -0
  216. package/esm/shaders/pointMark.generated.d.ts +4 -0
  217. package/esm/shaders/pointMark.generated.js +8 -0
  218. package/esm/shaders/pointMark.glsl.generated.d.ts +2 -0
  219. package/esm/shaders/pointMark.glsl.generated.js +4 -0
  220. package/esm/shaders/pointMark.iface.generated.d.ts +98 -0
  221. package/esm/shaders/pointMark.iface.generated.js +219 -0
  222. package/esm/shaders/pointMark.js.generated.d.ts +3 -0
  223. package/esm/shaders/pointMark.js.generated.js +93 -0
  224. package/esm/shaders/pointMark.wgsl.generated.d.ts +1 -0
  225. package/esm/shaders/pointMark.wgsl.generated.js +3 -0
  226. package/esm/shaders/rowRect.generated.d.ts +0 -1
  227. package/esm/shaders/rowRect.generated.js +0 -1
  228. package/esm/shaders/rowTable.generated.d.ts +1 -0
  229. package/esm/shaders/rowTable.generated.js +3 -0
  230. package/esm/shaders/rowTable.js.generated.d.ts +5 -0
  231. package/esm/shaders/rowTable.js.generated.js +29 -0
  232. package/esm/shaders/scoreScale.generated.d.ts +2 -0
  233. package/esm/shaders/scoreScale.generated.js +4 -0
  234. package/esm/shaders/scoreScale.js.generated.d.ts +1 -0
  235. package/esm/shaders/scoreScale.js.generated.js +50 -23
  236. package/esm/shaders/spanMark.generated.d.ts +3 -0
  237. package/esm/shaders/spanMark.generated.js +7 -0
  238. package/esm/shaders/spanMark.glsl.generated.d.ts +2 -0
  239. package/esm/shaders/spanMark.glsl.generated.js +4 -0
  240. package/esm/shaders/spanMark.iface.generated.d.ts +81 -0
  241. package/esm/shaders/spanMark.iface.generated.js +215 -0
  242. package/esm/shaders/spanMark.wgsl.generated.d.ts +1 -0
  243. package/esm/shaders/spanMark.wgsl.generated.js +3 -0
  244. package/esm/sharedBackendKey.d.ts +18 -0
  245. package/esm/{keyedRenderingBackend.js → sharedBackendKey.js} +5 -0
  246. package/esm/slangPass.d.ts +17 -11
  247. package/esm/slangPass.js +12 -4
  248. package/esm/useGraphicsCapabilities.d.ts +1 -1
  249. package/esm/useGraphicsCapabilities.js +1 -1
  250. package/esm/useRenderingBackend.d.ts +13 -13
  251. package/esm/useRenderingBackend.js +58 -20
  252. package/esm/webgpuUtils.d.ts +3 -11
  253. package/esm/webgpuUtils.js +12 -33
  254. package/package.json +32 -7
  255. package/src/shaders/antialias.slang +46 -0
  256. package/src/shaders/arcHull.test.ts +267 -0
  257. package/src/shaders/barMark.generated.ts +11 -0
  258. package/src/shaders/barMark.glsl.generated.ts +6 -0
  259. package/src/shaders/barMark.iface.generated.ts +285 -0
  260. package/src/shaders/barMark.slang +167 -0
  261. package/src/shaders/barMark.wgsl.generated.ts +4 -0
  262. package/src/shaders/capsule.js.generated.ts +14 -0
  263. package/src/shaders/capsule.slang +13 -5
  264. package/src/shaders/clipStrip.generated.ts +6 -0
  265. package/src/shaders/clipStrip.js.generated.ts +22 -0
  266. package/src/shaders/clipStrip.slang +34 -0
  267. package/src/shaders/clipStrip.test.ts +39 -0
  268. package/src/shaders/colorPack.slang +5 -1
  269. package/src/shaders/colorRampLut.js.generated.ts +22 -0
  270. package/src/shaders/colorRampLut.slang +15 -1
  271. package/src/shaders/coverageBand.slang +64 -32
  272. package/src/shaders/coverageBar.generated.ts +6 -5
  273. package/src/shaders/coverageBar.glsl.generated.ts +6 -0
  274. package/src/shaders/coverageBar.iface.generated.ts +1 -1
  275. package/src/shaders/coverageBar.slang +10 -10
  276. package/src/shaders/coverageBar.wgsl.generated.ts +4 -0
  277. package/src/shaders/coverageClip.generated.ts +11 -0
  278. package/src/shaders/coverageClip.glsl.generated.ts +6 -0
  279. package/src/shaders/coverageClip.iface.generated.ts +172 -0
  280. package/src/shaders/coverageClip.slang +38 -0
  281. package/src/shaders/coverageClip.wgsl.generated.ts +4 -0
  282. package/src/shaders/coverageIndicator.generated.ts +6 -5
  283. package/src/shaders/coverageIndicator.glsl.generated.ts +6 -0
  284. package/src/shaders/coverageIndicator.iface.generated.ts +1 -1
  285. package/src/shaders/coverageIndicator.wgsl.generated.ts +4 -0
  286. package/src/shaders/coverageInterbase.generated.ts +6 -5
  287. package/src/shaders/coverageInterbase.glsl.generated.ts +6 -0
  288. package/src/shaders/coverageInterbase.iface.generated.ts +1 -1
  289. package/src/shaders/coverageInterbase.wgsl.generated.ts +4 -0
  290. package/src/shaders/coverageMod.generated.ts +6 -5
  291. package/src/shaders/coverageMod.glsl.generated.ts +6 -0
  292. package/src/shaders/coverageMod.iface.generated.ts +1 -1
  293. package/src/shaders/coverageMod.wgsl.generated.ts +4 -0
  294. package/src/shaders/coverageSnp.generated.ts +6 -5
  295. package/src/shaders/coverageSnp.glsl.generated.ts +6 -0
  296. package/src/shaders/coverageSnp.iface.generated.ts +1 -1
  297. package/src/shaders/coverageSnp.wgsl.generated.ts +4 -0
  298. package/src/shaders/curveDistance.js.generated.ts +91 -0
  299. package/src/shaders/curveDistance.slang +265 -0
  300. package/src/shaders/hpClampFloor.test.ts +87 -0
  301. package/src/shaders/hpmath.js.generated.ts +6 -2
  302. package/src/shaders/hpmath.slang +100 -48
  303. package/src/shaders/insetValueScale.test.ts +67 -0
  304. package/src/shaders/lineCenterMark.generated.ts +11 -0
  305. package/src/shaders/lineCenterMark.glsl.generated.ts +6 -0
  306. package/src/shaders/lineCenterMark.iface.generated.ts +328 -0
  307. package/src/shaders/lineCenterMark.slang +127 -0
  308. package/src/shaders/lineCenterMark.wgsl.generated.ts +4 -0
  309. package/src/shaders/lineCommon.generated.ts +6 -0
  310. package/src/shaders/lineCommon.slang +102 -0
  311. package/src/shaders/lineStepMark.generated.ts +11 -0
  312. package/src/shaders/lineStepMark.glsl.generated.ts +6 -0
  313. package/src/shaders/lineStepMark.iface.generated.ts +311 -0
  314. package/src/shaders/lineStepMark.slang +125 -0
  315. package/src/shaders/lineStepMark.wgsl.generated.ts +4 -0
  316. package/src/shaders/linkMark.consts.generated.ts +30 -0
  317. package/src/shaders/linkMark.generated.ts +12 -0
  318. package/src/shaders/linkMark.glsl.generated.ts +6 -0
  319. package/src/shaders/linkMark.iface.generated.ts +1954 -0
  320. package/src/shaders/linkMark.js.generated.ts +167 -0
  321. package/src/shaders/linkMark.slang +439 -0
  322. package/src/shaders/linkMark.wgsl.generated.ts +4 -0
  323. package/src/shaders/markColor.generated.ts +20 -0
  324. package/src/shaders/markColor.slang +128 -0
  325. package/src/shaders/pointGlyph.js.generated.ts +20 -0
  326. package/src/shaders/pointGlyph.slang +23 -7
  327. package/src/shaders/pointGlyphPad.test.ts +242 -0
  328. package/src/shaders/pointMark.consts.generated.ts +16 -0
  329. package/src/shaders/pointMark.generated.ts +12 -0
  330. package/src/shaders/pointMark.glsl.generated.ts +6 -0
  331. package/src/shaders/pointMark.iface.generated.ts +304 -0
  332. package/src/shaders/pointMark.js.generated.ts +99 -0
  333. package/src/shaders/pointMark.slang +227 -0
  334. package/src/shaders/pointMark.wgsl.generated.ts +4 -0
  335. package/src/shaders/rowRect.generated.ts +0 -2
  336. package/src/shaders/rowRect.slang +39 -58
  337. package/src/shaders/rowTable.generated.ts +4 -0
  338. package/src/shaders/rowTable.js.generated.ts +37 -0
  339. package/src/shaders/rowTable.slang +98 -0
  340. package/src/shaders/scoreScale.generated.ts +6 -0
  341. package/src/shaders/scoreScale.js.generated.ts +48 -23
  342. package/src/shaders/scoreScale.slang +49 -55
  343. package/src/shaders/sdEllipse.test.ts +211 -0
  344. package/src/shaders/spanMark.generated.ts +11 -0
  345. package/src/shaders/spanMark.glsl.generated.ts +6 -0
  346. package/src/shaders/spanMark.iface.generated.ts +289 -0
  347. package/src/shaders/spanMark.slang +77 -0
  348. package/src/shaders/spanMark.wgsl.generated.ts +4 -0
  349. package/src/shaders/valueScale.slang +26 -0
  350. package/src/shaders/wideCircleLeg.test.ts +85 -0
  351. package/esm/globalRenderingBackend.d.ts +0 -90
  352. package/esm/globalRenderingBackend.js +0 -53
  353. package/esm/keyedRenderingBackend.d.ts +0 -41
@@ -1,120 +1,108 @@
1
1
  /// <reference types="@webgpu/types" />
2
+ import { STANDARD_BLEND_STATE, stageVisibility, toGpuVertexFormat, } from '../webgpuUtils.js';
3
+ // A device's layouts and pipelines, shared by every WebGPUHal on it. Held per
4
+ // module copy rather than on the `globalThis` cell: a second bundled copy
5
+ // builds a duplicate set, never a wrong one, since WebGPU compares layouts
6
+ // structurally.
7
+ const perDevice = new WeakMap();
2
8
  /**
3
- * The GPU objects that belong to the **device** rather than to one `WebGPUHal`,
4
- * memoized so the second display of a track type builds none of them.
5
- *
6
- * WebGL2 has no equivalent and wants none: a program is owned by the context
7
- * that linked it, and each display owns a context, so nothing there is
8
- * shareable. WebGPU inverts that — `gpuDevice.ts` hands every display one
9
- * device, and a `GPURenderPipeline` is a device object — and until this module
10
- * the HAL kept building per-display copies anyway. Ten alignments tracks
11
- * compiled 230 pipelines for 23 distinct programs.
12
- *
13
- * **The key is the descriptor object, and that is what makes it correct.** A
14
- * plugin's pass list is a module-level const (`ALIGNMENTS_PASSES`) and
15
- * `slangPass` reads `wgslSource` off a generated module const, so every display
16
- * of a type hands the HAL the *same* `PipelineDescriptor` objects by reference.
17
- * Identity therefore means "same shader, same layout, same blend, same
18
- * topology" without comparing any of them, and two passes that merely share a
19
- * `.slang` shape module (MAF and multi-row both draw `rowRect`) get separate
20
- * entries because `slangPass` built them separate objects. The **sample count**
21
- * is the one pipeline input the descriptor cannot express, so it is the inner
22
- * key; see the `pipelines` field.
23
- *
24
- * The one field of a pipeline that is not on the descriptor is the uniform
25
- * size, and it cannot make two HALs disagree here:
26
- * `createUniformOnlyBindGroupLayout` declares no `minBindingSize`, so the
27
- * layout — and thus the pipeline built against it — is the same whatever a
28
- * display's `uniformByteSize` is. The size reaches the GPU through the bind
29
- * group's `size` and the dynamic offset, both of which stay per-HAL.
30
- *
31
- * Per copy of this module rather than on the `globalThis` cell, for the reason
32
- * `createHal.ts` gives for `warnedSoftwareRasterizer`: a second bundled copy
33
- * builds a second set, which costs a duplicate and never a wrong answer.
34
- * Layouts compare structurally in WebGPU, so a bind group from one copy is
35
- * valid against a pipeline from the other.
9
+ * A render pass's bind-group layout, read off its shader's reflected table:
10
+ * each binding visible to exactly the stages that read it, so the layout
11
+ * cannot disagree with the shader about which stage reads what. The uniform
12
+ * block takes a dynamic offset into the HAL's ring.
36
13
  */
37
- const perDevice = new WeakMap();
38
- function createLayouts(device) {
39
- const uniformEntry = {
40
- binding: 1,
41
- visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
42
- buffer: { type: 'uniform', hasDynamicOffset: true },
43
- };
44
- const uniformOnlyBindGroupLayout = device.createBindGroupLayout({
45
- entries: [uniformEntry],
46
- });
47
- const texturedBindGroupLayout = device.createBindGroupLayout({
48
- entries: [
49
- uniformEntry,
50
- {
51
- binding: 2,
52
- visibility: GPUShaderStage.FRAGMENT,
53
- texture: { sampleType: 'float' },
54
- },
55
- {
56
- binding: 3,
57
- visibility: GPUShaderStage.FRAGMENT,
58
- sampler: { type: 'filtering' },
59
- },
60
- ],
14
+ export function bindGroupLayoutEntries(bindings) {
15
+ return bindings.map((b) => {
16
+ const at = { binding: b.index, visibility: stageVisibility(b.stages) };
17
+ switch (b.kind) {
18
+ case 'uniform':
19
+ return { ...at, buffer: { type: 'uniform', hasDynamicOffset: true } };
20
+ case 'texture':
21
+ return { ...at, texture: { sampleType: 'float' } };
22
+ case 'sampler':
23
+ return { ...at, sampler: { type: 'filtering' } };
24
+ default:
25
+ throw new Error(`a render pass binds no ${b.kind}, and '${b.name}' is one at ` +
26
+ `binding ${b.index}`);
27
+ }
61
28
  });
29
+ }
30
+ function gpuBlendState(bs) {
31
+ // WebGPU rejects any factor but 'one' under max, which ignores them. Alpha
32
+ // accumulates as webgl2Hal's applyBlendState does.
33
+ const max = { srcFactor: 'one', dstFactor: 'one', operation: 'max' };
34
+ const behind = { srcFactor: 'one-minus-dst-alpha', dstFactor: 'one' };
35
+ return bs.op === 'max'
36
+ ? { color: max, alpha: max }
37
+ : bs.op === 'behind'
38
+ ? { color: behind, alpha: behind }
39
+ : {
40
+ color: { srcFactor: bs.srcFactor, dstFactor: bs.dstFactor },
41
+ alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha' },
42
+ };
43
+ }
44
+ export function pipelineRecipe(desc, wgslSource, sampleCount) {
62
45
  return {
63
- uniformOnlyBindGroupLayout,
64
- uniformOnlyPipelineLayout: device.createPipelineLayout({
65
- bindGroupLayouts: [uniformOnlyBindGroupLayout],
66
- }),
67
- texturedBindGroupLayout,
68
- texturedPipelineLayout: device.createPipelineLayout({
69
- bindGroupLayouts: [texturedBindGroupLayout],
70
- }),
46
+ wgslSource,
47
+ bindGroupLayout: bindGroupLayoutEntries(desc.bindings),
48
+ vertexBuffer: {
49
+ arrayStride: desc.instanceStride,
50
+ stepMode: 'instance',
51
+ attributes: desc.vertexAttributes.map((attr, i) => ({
52
+ shaderLocation: i,
53
+ offset: attr.offsetBytes,
54
+ format: toGpuVertexFormat(attr),
55
+ })),
56
+ },
57
+ blend: desc.blend
58
+ ? desc.blendState
59
+ ? gpuBlendState(desc.blendState)
60
+ : STANDARD_BLEND_STATE
61
+ : undefined,
62
+ topology: desc.topology ?? 'triangle-list',
63
+ sampleCount,
71
64
  };
72
65
  }
73
66
  function cacheFor(device) {
74
67
  let entry = perDevice.get(device);
75
68
  if (!entry) {
76
- entry = { layouts: createLayouts(device), pipelines: new WeakMap() };
69
+ entry = { layouts: new Map(), pipelines: new Map() };
77
70
  perDevice.set(device, entry);
78
71
  }
79
72
  return entry;
80
73
  }
81
- /**
82
- * The bind group and pipeline layouts for `device`, built once per device.
83
- *
84
- * A device that is lost drops out of the map with itself — `gpuDevice.ts`
85
- * releases its reference in the `.lost` handler and the next acquisition is a
86
- * new object, so the layouts and pipelines built against the dead one are
87
- * unreachable and there is no cache to invalidate by hand.
88
- */
89
- export function getDeviceLayouts(device) {
90
- return cacheFor(device).layouts;
74
+ export function getPassLayout(device, entries) {
75
+ const { layouts } = cacheFor(device);
76
+ const key = JSON.stringify(entries);
77
+ let layout = layouts.get(key);
78
+ if (!layout) {
79
+ const bindGroupLayout = device.createBindGroupLayout({ entries });
80
+ layout = {
81
+ bindGroupLayout,
82
+ pipelineLayout: device.createPipelineLayout({
83
+ bindGroupLayouts: [bindGroupLayout],
84
+ }),
85
+ };
86
+ layouts.set(key, layout);
87
+ }
88
+ return layout;
91
89
  }
92
- /**
93
- * The pipeline for `desc` at `sampleCount` on `device`, building it through
94
- * `build` on the first ask and handing every later one the same promise.
95
- *
96
- * `build` receives the sample count rather than closing over one, so the key
97
- * and the pipeline stored under it cannot disagree.
98
- */
99
- export function getOrBuildPipeline(device, desc, sampleCount, build) {
100
- const { layouts, pipelines } = cacheFor(device);
101
- let bySampleCount = pipelines.get(desc);
102
- if (!bySampleCount) {
103
- bySampleCount = new Map();
104
- pipelines.set(desc, bySampleCount);
90
+ export function getOrBuildPipeline(device, recipe, build) {
91
+ const { pipelines } = cacheFor(device);
92
+ const { wgslSource, ...rest } = recipe;
93
+ let bySource = pipelines.get(wgslSource);
94
+ if (!bySource) {
95
+ bySource = new Map();
96
+ pipelines.set(wgslSource, bySource);
105
97
  }
106
- let pipeline = bySampleCount.get(sampleCount);
98
+ const key = JSON.stringify(rest);
99
+ let pipeline = bySource.get(key);
107
100
  if (!pipeline) {
108
- pipeline = build(layouts, sampleCount);
109
- bySampleCount.set(sampleCount, pipeline);
101
+ pipeline = build(recipe);
102
+ bySource.set(key, pipeline);
110
103
  }
111
104
  return pipeline;
112
105
  }
113
- /**
114
- * Drop every device's entry. Tests only — nothing in the app invalidates this
115
- * cache, because losing the device is what invalidates it (see
116
- * {@link getDeviceLayouts}).
117
- */
118
106
  export function resetDeviceGpuCacheForTests(device) {
119
107
  perDevice.delete(device);
120
108
  }
@@ -1,6 +1,6 @@
1
1
  import { OomReporter } from './oomReporter.ts';
2
2
  import { RegionRegistry } from './regionRegistry.ts';
3
- import type { PipelineDescriptor, TextureBinding } from './types.ts';
3
+ import type { PipelineDescriptor, TextureBinding, TextureSource } from './types.ts';
4
4
  /**
5
5
  * The device ceilings the shared upload shells refuse past. WebGPU asks the
6
6
  * device, WebGL2 has no such parameter for buffers and carries its own constant
@@ -31,6 +31,7 @@ export declare abstract class GpuHalBase<Buf extends {
31
31
  protected descriptors: Map<string, PipelineDescriptor>;
32
32
  protected regions: RegionRegistry<Buf>;
33
33
  protected oom: OomReporter;
34
+ readonly uniformByteSize: number;
34
35
  protected disposed: boolean;
35
36
  constructor(descriptors: PipelineDescriptor[], halName: string);
36
37
  protected abstract limits(): HalLimits;
@@ -41,7 +42,7 @@ export declare abstract class GpuHalBase<Buf extends {
41
42
  * reached for a pass that declares a binding, and only within
42
43
  * `maxTextureDimensionPx`.
43
44
  */
44
- protected abstract createTexture(passId: string, binding: TextureBinding, data: Uint8Array, width: number, height: number): void;
45
+ protected abstract createTexture(passId: string, binding: TextureBinding, data: TextureSource, width: number, height: number): void;
45
46
  /** Release this HAL's own GPU objects. Runs exactly once, from `dispose`. */
46
47
  protected abstract releaseResources(): void;
47
48
  setErrorHandler(handler: (error: Error) => void): void;
@@ -49,7 +50,7 @@ export declare abstract class GpuHalBase<Buf extends {
49
50
  getBufferCount(regionKey: number, passId: string): number;
50
51
  deleteBuffer(regionKey: number, passId: string): void;
51
52
  deleteRegion(regionKey: number): void;
52
- pruneRegions(active: Iterable<number>): void;
53
- uploadTexture(passId: string, data: Uint8Array, width: number, height: number): void;
53
+ uploadTexture(passId: string, data: TextureSource, width: number, height: number, sampler?: string): void;
54
+ protected textureBinding(passId: string, sampler?: string): TextureBinding;
54
55
  dispose(): void;
55
56
  }
@@ -19,11 +19,13 @@ export class GpuHalBase {
19
19
  descriptors;
20
20
  regions;
21
21
  oom;
22
+ uniformByteSize;
22
23
  // Guards dispose() against double invocation (pagehide + React cleanup can
23
24
  // both fire).
24
25
  disposed = false;
25
26
  constructor(descriptors, halName) {
26
27
  this.descriptors = new Map(descriptors.map(d => [d.id, d]));
28
+ this.uniformByteSize = Math.max(0, ...descriptors.map(d => d.uniformByteSize));
27
29
  this.oom = new OomReporter(halName);
28
30
  this.regions = new RegionRegistry(buf => {
29
31
  this.destroyBuffer(buf);
@@ -55,22 +57,31 @@ export class GpuHalBase {
55
57
  deleteRegion(regionKey) {
56
58
  this.regions.deleteRegion(regionKey);
57
59
  }
58
- pruneRegions(active) {
59
- this.regions.prune(active);
60
+ uploadTexture(passId, data, width, height, sampler) {
61
+ const binding = this.textureBinding(passId, sampler);
62
+ const { maxTextureDimensionPx } = this.limits();
63
+ if (width > maxTextureDimensionPx || height > maxTextureDimensionPx) {
64
+ this.oom.report(`This region is too large to render on this GPU — zoom in. (texture ${width}×${height} exceeds max texture size ${maxTextureDimensionPx})`);
65
+ }
66
+ else {
67
+ this.createTexture(passId, binding, data, width, height);
68
+ }
60
69
  }
61
- uploadTexture(passId, data, width, height) {
62
- // Read the binding off the descriptor, so a pass with no texture is
63
- // answered without touching the backend's pipeline state at all.
64
- const binding = this.descriptors.get(passId)?.textures?.[0];
65
- if (binding) {
66
- const { maxTextureDimensionPx } = this.limits();
67
- if (width > maxTextureDimensionPx || height > maxTextureDimensionPx) {
68
- this.oom.report(`This region is too large to render on this GPU — zoom in. (texture ${width}×${height} exceeds max texture size ${maxTextureDimensionPx})`);
69
- }
70
- else {
71
- this.createTexture(passId, binding, data, width, height);
72
- }
70
+ textureBinding(passId, sampler) {
71
+ const textures = this.descriptors.get(passId)?.textures ?? [];
72
+ const binding = sampler === undefined
73
+ ? textures.length === 1
74
+ ? textures[0]
75
+ : undefined
76
+ : textures.find(t => t.name === sampler);
77
+ if (!binding) {
78
+ const names = textures.map(t => `'${t.name}'`).join(', ');
79
+ const has = names ? `samples ${names}` : 'samples no texture';
80
+ throw new Error(sampler === undefined
81
+ ? `uploadTexture: pass '${passId}' ${has}; name the sampler this texture is for`
82
+ : `uploadTexture: pass '${passId}' has no sampler '${sampler}'; it ${has}`);
73
83
  }
84
+ return binding;
74
85
  }
75
86
  dispose() {
76
87
  if (!this.disposed) {
@@ -1,7 +1,7 @@
1
1
  export { createGpuHal } from './createHal.ts';
2
- export { MockHal } from './mockHal.ts';
2
+ export { MOCK_SHADER_SOURCE, MockHal } from './mockHal.ts';
3
3
  export { assertUniquePassIds } from './passIds.ts';
4
4
  export { WebGL2Hal } from './webgl2Hal.ts';
5
5
  export { WebGPUHal } from './webgpuHal.ts';
6
6
  export type { GpuHalOptions } from './createHal.ts';
7
- export type { BlendState, VertexAttributeLayout, GpuHal, PipelineDescriptor, SampleCount, ShaderBinding, TextureBinding, } from './types.ts';
7
+ export type { BlendState, VertexAttributeLayout, GpuHal, PipelineDescriptor, SampleCount, ShaderBinding, ShaderSource, ShaderStage, TextureBinding, TextureSource, } from './types.ts';
package/esm/hal/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { createGpuHal } from './createHal.js';
2
- export { MockHal } from './mockHal.js';
2
+ export { MOCK_SHADER_SOURCE, MockHal } from './mockHal.js';
3
3
  export { assertUniquePassIds } from './passIds.js';
4
4
  export { WebGL2Hal } from './webgl2Hal.js';
5
5
  export { WebGPUHal } from './webgpuHal.js';
@@ -1,5 +1,7 @@
1
1
  import { GpuHalBase } from './gpuHalBase.ts';
2
- import type { GpuHal, PipelineDescriptor, TextureBinding } from './types.ts';
2
+ import type { GpuHal, PipelineDescriptor, ShaderSource, TextureBinding, TextureSource } from './types.ts';
3
+ /** Text for a pass built only for a `MockHal`, which never loads it. */
4
+ export declare const MOCK_SHADER_SOURCE: ShaderSource;
3
5
  export interface MockCall {
4
6
  method: string;
5
7
  args: unknown[];
@@ -17,7 +19,7 @@ export interface MockRect {
17
19
  /**
18
20
  * One draw, with the state that was in force when it was issued — the clip, and
19
21
  * which `writeUniforms` it reads. `null` scissor/viewport means unclipped, the
20
- * full canvas, which is what both HALs owe after a `clearScissor` /
22
+ * full canvas, the state both HALs must restore after a `clearScissor` /
21
23
  * `clearViewport`.
22
24
  *
23
25
  * `uniformWrite` indexes {@link MockHal.getUniformWritesF32}, or is `-1` when
@@ -32,13 +34,15 @@ export interface MockRect {
32
34
  * does an immediate `bufferSubData` into one UBO. They agree only while a
33
35
  * renderer writes then draws, which is the convention every renderer in tree
34
36
  * follows and nothing enforces: batching the writes and then issuing the draws
35
- * is right on WebGL2 and Canvas2D and silently wrong on WebGPU. This field is
36
- * what lets a backend test pin its renderer's pairing.
37
+ * is right on WebGL2 and Canvas2D and wrong on WebGPU, where the draws read the
38
+ * last write. `uniformWrite` lets a backend test pin its renderer's pairing.
37
39
  */
38
40
  export interface MockDraw {
39
41
  passId: string;
40
42
  regionKey: number;
41
43
  bufferPassId: string | undefined;
44
+ /** The vertex count the draw asked for, its descriptor's where it asked for none. */
45
+ verticesPerInstance: number;
42
46
  scissor: MockRect | null;
43
47
  viewport: MockRect | null;
44
48
  uniformWrite: number;
@@ -59,8 +63,8 @@ export declare class MockHal extends GpuHalBase<MockBuffer> implements GpuHal {
59
63
  * rect without re-issuing a full-canvas one, so every draw after it went on
60
64
  * being clipped to the previous block while WebGL2 drew them unclipped — right
61
65
  * on Canvas2D, right on WebGL2, wrong on WebGPU alone. Nothing in tree clears
62
- * mid-frame today, which is exactly why nothing caught it. Recording the
63
- * effective clip per draw is what makes that assertable at all.
66
+ * mid-frame today, so no test caught it. Recording the effective clip per draw
67
+ * makes that assertable.
64
68
  */
65
69
  private scissor;
66
70
  private viewport;
@@ -90,7 +94,7 @@ export declare class MockHal extends GpuHalBase<MockBuffer> implements GpuHal {
90
94
  };
91
95
  protected destroyBuffer(): void;
92
96
  private textures;
93
- protected createTexture(passId: string, _binding: TextureBinding, data: Uint8Array, width: number, height: number): void;
97
+ protected createTexture(passId: string, binding: TextureBinding, data: TextureSource, width: number, height: number): void;
94
98
  protected releaseResources(): void;
95
99
  private record;
96
100
  errorHandler: ((error: Error) => void) | null;
@@ -99,15 +103,15 @@ export declare class MockHal extends GpuHalBase<MockBuffer> implements GpuHal {
99
103
  x: number;
100
104
  y: number;
101
105
  };
106
+ releaseRenderTargets(): void;
102
107
  uploadBuffer(regionKey: number, passId: string, data: ArrayBuffer | ArrayBufferView, count: number): void;
103
108
  deleteBuffer(regionKey: number, passId: string): void;
104
109
  deleteRegion(regionKey: number): void;
105
- pruneRegions(active: Iterable<number>): void;
106
110
  private noteReplaced;
107
111
  private noteRegionReplaced;
108
112
  writeUniforms(data: ArrayBuffer): void;
109
113
  beginFrame(clearR: number, clearG: number, clearB: number, clearA?: number): void;
110
- drawPass(passId: string, regionKey: number, bufferPassId?: string): void;
114
+ drawPass(passId: string, regionKey: number, bufferPassId?: string, verticesPerInstance?: number): void;
111
115
  private assertRegistered;
112
116
  endFrame(): void;
113
117
  setScissor(x: number, y: number, w: number, h: number): void;
@@ -121,7 +125,8 @@ export declare class MockHal extends GpuHalBase<MockBuffer> implements GpuHal {
121
125
  getUniformWritesF32(): Float32Array<ArrayBuffer>[];
122
126
  getUniformWritesU32(): Uint32Array<ArrayBuffer>[];
123
127
  getBuffer(regionKey: number, passId: string): MockBuffer | undefined;
124
- getTexture(passId: string): Uint8Array<ArrayBufferLike> | undefined;
128
+ /** What the pass's `sampler` samples, named as `uploadTexture` names it. */
129
+ getTexture(passId: string, sampler?: string): TextureSource | undefined;
125
130
  callsOf(method: string): MockCall[];
126
131
  /**
127
132
  * Every draw of the frame with the state it went out under, in order — the
@@ -1,6 +1,14 @@
1
1
  import { getDpr } from '../canvas2dUtils.js';
2
2
  import { GpuHalBase } from './gpuHalBase.js';
3
3
  import { assertUniquePassIds } from './passIds.js';
4
+ /** Text for a pass built only for a `MockHal`, which never loads it. */
5
+ export const MOCK_SHADER_SOURCE = {
6
+ wgsl: () => Promise.resolve({ WGSL_SOURCE: '' }),
7
+ glsl: () => Promise.resolve({ GLSL_VERTEX: '', GLSL_FRAGMENT: '' }),
8
+ };
9
+ function textureKey(passId, sampler) {
10
+ return `${passId}\u0000${sampler}`;
11
+ }
4
12
  export class MockHal extends GpuHalBase {
5
13
  calls = [];
6
14
  // Every write of the frame, in order, not just the last. A renderer that
@@ -23,8 +31,8 @@ export class MockHal extends GpuHalBase {
23
31
  * rect without re-issuing a full-canvas one, so every draw after it went on
24
32
  * being clipped to the previous block while WebGL2 drew them unclipped — right
25
33
  * on Canvas2D, right on WebGL2, wrong on WebGPU alone. Nothing in tree clears
26
- * mid-frame today, which is exactly why nothing caught it. Recording the
27
- * effective clip per draw is what makes that assertable at all.
34
+ * mid-frame today, so no test caught it. Recording the effective clip per draw
35
+ * makes that assertable.
28
36
  */
29
37
  scissor = null;
30
38
  viewport = null;
@@ -76,9 +84,10 @@ export class MockHal extends GpuHalBase {
76
84
  // reason the real HALs keep their texture: a test asserting which LUT a pass
77
85
  // samples needs the bytes, not the byteLength the call log records.
78
86
  textures = new Map();
79
- createTexture(passId, _binding, data, width, height) {
80
- this.textures.set(passId, data.slice());
81
- this.record('uploadTexture', passId, data.byteLength, width, height);
87
+ createTexture(passId, binding, data, width, height) {
88
+ const bytes = data instanceof Uint8Array;
89
+ this.textures.set(textureKey(passId, binding.name), bytes ? data.slice() : data);
90
+ this.record('uploadTexture', passId, bytes ? data.byteLength : 'canvas', width, height, binding.name);
82
91
  }
83
92
  releaseResources() {
84
93
  this.record('dispose');
@@ -94,6 +103,9 @@ export class MockHal extends GpuHalBase {
94
103
  this.record('setErrorHandler');
95
104
  }
96
105
  resize(width, height) {
106
+ if (this.disposed) {
107
+ return { x: getDpr(), y: getDpr() };
108
+ }
97
109
  this.record('resize', width, height);
98
110
  // The real HALs report the scale their backing store actually got, and
99
111
  // every device-px rect a renderer builds comes from this — so a mock
@@ -101,6 +113,9 @@ export class MockHal extends GpuHalBase {
101
113
  // the unclamped answer, which is what a mock canvas of any size would get.
102
114
  return { x: getDpr(), y: getDpr() };
103
115
  }
116
+ releaseRenderTargets() {
117
+ this.record('releaseRenderTargets');
118
+ }
104
119
  // The overrides below log and then defer to the base's shells, so the buffer
105
120
  // lifecycle a test observes (delete-before-count, prune) is the same code both
106
121
  // real HALs run rather than a twin that can drift out of parity.
@@ -119,16 +134,6 @@ export class MockHal extends GpuHalBase {
119
134
  this.noteRegionReplaced(regionKey);
120
135
  super.deleteRegion(regionKey);
121
136
  }
122
- pruneRegions(active) {
123
- const activeSet = new Set(active);
124
- this.record('pruneRegions', [...activeSet]);
125
- for (const regionKey of this.drawnThisFrame.keys()) {
126
- if (!activeSet.has(regionKey)) {
127
- this.noteRegionReplaced(regionKey);
128
- }
129
- }
130
- super.pruneRegions(activeSet);
131
- }
132
137
  noteReplaced(regionKey, passId) {
133
138
  if (this.frameOpen && this.drawnThisFrame.get(regionKey)?.has(passId)) {
134
139
  this.replacedWhileDrawnLog.push(`${regionKey}:${passId}`);
@@ -168,11 +173,15 @@ export class MockHal extends GpuHalBase {
168
173
  // `bufferPassId` is checked too: `drawPass(a, key, b)` runs pass `a`'s
169
174
  // pipeline over pass `b`'s buffer, and every such pair in tree names two
170
175
  // registered passes (canvas's chevron over line, continuation over rect).
171
- drawPass(passId, regionKey, bufferPassId) {
176
+ drawPass(passId, regionKey, bufferPassId, verticesPerInstance) {
172
177
  this.assertRegistered(passId, 'passId');
173
178
  if (bufferPassId !== undefined) {
174
179
  this.assertRegistered(bufferPassId, 'bufferPassId');
175
180
  }
181
+ const missing = (this.descriptors.get(passId)?.textures ?? []).some(t => !this.textures.has(textureKey(passId, t.name)));
182
+ if (missing) {
183
+ return;
184
+ }
176
185
  this.record('drawPass', passId, regionKey, bufferPassId);
177
186
  // The buffer the draw reads, which `bufferPassId` renames — that is the
178
187
  // resource a later upload in this frame would be replacing under it.
@@ -187,6 +196,8 @@ export class MockHal extends GpuHalBase {
187
196
  passId,
188
197
  regionKey,
189
198
  bufferPassId,
199
+ verticesPerInstance: verticesPerInstance ??
200
+ this.descriptors.get(passId).verticesPerInstance,
190
201
  scissor: this.scissor && { ...this.scissor },
191
202
  viewport: this.viewport && { ...this.viewport },
192
203
  uniformWrite: this.uniformWrites.length - 1,
@@ -244,8 +255,10 @@ export class MockHal extends GpuHalBase {
244
255
  getBuffer(regionKey, passId) {
245
256
  return this.regions.get(regionKey, passId);
246
257
  }
247
- getTexture(passId) {
248
- return this.textures.get(passId);
258
+ /** What the pass's `sampler` samples, named as `uploadTexture` names it. */
259
+ getTexture(passId, sampler) {
260
+ const { name } = this.textureBinding(passId, sampler);
261
+ return this.textures.get(textureKey(passId, name));
249
262
  }
250
263
  callsOf(method) {
251
264
  return this.calls.filter(c => c.method === method);
@@ -12,4 +12,11 @@ export declare class OomReporter {
12
12
  constructor(hal: string);
13
13
  setHandler(handler: (error: Error) => void): void;
14
14
  report(message: string): void;
15
+ /**
16
+ * Report an error the HAL has already classified, for a failure whose remedy
17
+ * is not `report`'s "zoom in". Kept separate so the classification stays with
18
+ * the caller that knows it — flagging every over-limit allocation as a context
19
+ * loss would offer "Use Canvas2D" for a view that only needs zooming.
20
+ */
21
+ reportClassified(error: Error): void;
15
22
  }
@@ -19,4 +19,14 @@ export class OomReporter {
19
19
  console.error(`[${this.hal}] ${message}`);
20
20
  this.handler?.(new Error(message));
21
21
  }
22
+ /**
23
+ * Report an error the HAL has already classified, for a failure whose remedy
24
+ * is not `report`'s "zoom in". Kept separate so the classification stays with
25
+ * the caller that knows it — flagging every over-limit allocation as a context
26
+ * loss would offer "Use Canvas2D" for a view that only needs zooming.
27
+ */
28
+ reportClassified(error) {
29
+ console.error(`[${this.hal}] ${error.message}`);
30
+ this.handler?.(error);
31
+ }
22
32
  }
@@ -2,13 +2,14 @@ import type { PipelineDescriptor } from './types.ts';
2
2
  /**
3
3
  * Throw if two passes in one display's registry share an `id`.
4
4
  *
5
- * A pass id is two keys at once — the pipeline (`pipelines.set(desc.id, …)` in
6
- * both HALs) and the instance buffer (`uploadBuffer(regionKey, passId, …)`).
7
- * A duplicate therefore collides both: the second registration replaces the
8
- * first's pipeline, both passes upload to one buffer so the last write wins,
9
- * and whichever shader has the larger stride reads off the end of it. No
10
- * validation error, no throw, no failing test — just wrong pixels on the GPU
11
- * backends while Canvas2D keeps drawing correctly.
5
+ * A pass id names a slot: the descriptor a draw of that id uses, its instance
6
+ * buffer per region, and its texture. Compiled pipelines and programs are keyed
7
+ * by content instead, so a second id over one shader costs no compile. A
8
+ * duplicate id collides the slot: the later descriptor answers every draw of
9
+ * it, both passes upload to one buffer so the last write wins, and whichever
10
+ * shader has the larger stride reads off the end of it. No validation error, no
11
+ * throw, no failing test — just wrong pixels on the GPU backends while Canvas2D
12
+ * keeps drawing correctly.
12
13
  *
13
14
  * Nothing structural stops it. A display's pass list is concatenated from
14
15
  * several registries (`ALIGNMENTS_PASSES` merges three plus a standalone
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * Throw if two passes in one display's registry share an `id`.
3
3
  *
4
- * A pass id is two keys at once — the pipeline (`pipelines.set(desc.id, …)` in
5
- * both HALs) and the instance buffer (`uploadBuffer(regionKey, passId, …)`).
6
- * A duplicate therefore collides both: the second registration replaces the
7
- * first's pipeline, both passes upload to one buffer so the last write wins,
8
- * and whichever shader has the larger stride reads off the end of it. No
9
- * validation error, no throw, no failing test — just wrong pixels on the GPU
10
- * backends while Canvas2D keeps drawing correctly.
4
+ * A pass id names a slot: the descriptor a draw of that id uses, its instance
5
+ * buffer per region, and its texture. Compiled pipelines and programs are keyed
6
+ * by content instead, so a second id over one shader costs no compile. A
7
+ * duplicate id collides the slot: the later descriptor answers every draw of
8
+ * it, both passes upload to one buffer so the last write wins, and whichever
9
+ * shader has the larger stride reads off the end of it. No validation error, no
10
+ * throw, no failing test — just wrong pixels on the GPU backends while Canvas2D
11
+ * keeps drawing correctly.
11
12
  *
12
13
  * Nothing structural stops it. A display's pass list is concatenated from
13
14
  * several registries (`ALIGNMENTS_PASSES` merges three plus a standalone
@@ -29,8 +30,8 @@ export function assertUniquePassIds(passes) {
29
30
  }
30
31
  if (duplicates.size > 0) {
31
32
  throw new Error(`duplicate pass id(s) ${[...duplicates].map(id => `'${id}'`).join(', ')}` +
32
- ` — a pass id keys both the pipeline and the instance buffer, so two` +
33
- ` passes sharing one draw each other's instances through their own` +
34
- ` shader. Rename one.`);
33
+ ` — a pass id keys the descriptor, the instance buffer and the` +
34
+ ` texture, so two passes sharing one draw each other's instances` +
35
+ ` through one shader. Rename one.`);
35
36
  }
36
37
  }
@@ -7,5 +7,4 @@ export declare class RegionRegistry<Buf> {
7
7
  deleteBuffer(regionKey: number, passId: string): void;
8
8
  deleteRegion(regionKey: number): void;
9
9
  deleteAll(): void;
10
- prune(active: Iterable<number>): void;
11
10
  }
@@ -1,9 +1,6 @@
1
- // Per-(region, pass) buffer registry shared by WebGL2Hal and WebGPUHal.
2
- // Owns the nested map and the destroy semantics so the two HALs only have to
3
- // supply a leaf-buffer type and its destroy hook. Both HALs used to ship
4
- // near-identical deleteBuffer/deleteRegion/deleteAll/prune/getOrCreate
5
- // implementations; centralizing avoids drift when one side's lifecycle is
6
- // tweaked and the other isn't. Contract pinned by regionRegistry.test.ts.
1
+ // Per-(region, pass) buffer registry shared by WebGL2Hal and WebGPUHal: the
2
+ // nested map plus destroy semantics, so each HAL supplies only its leaf-buffer
3
+ // type and destroy hook.
7
4
  export class RegionRegistry {
8
5
  destroy;
9
6
  regions = new Map();
@@ -54,13 +51,4 @@ export class RegionRegistry {
54
51
  }
55
52
  this.regions.clear();
56
53
  }
57
- // Delete every region whose key is NOT in `active`.
58
- prune(active) {
59
- const activeSet = new Set(active);
60
- for (const regionKey of this.regions.keys()) {
61
- if (!activeSet.has(regionKey)) {
62
- this.deleteRegion(regionKey);
63
- }
64
- }
65
- }
66
54
  }