@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,4 +1,4 @@
1
- import { syncCanvasSize } from '../canvas2dUtils.js';
1
+ import { getDpr, syncCanvasSize } from '../canvas2dUtils.js';
2
2
  import { canvasContextError, noteCanvasContext } from '../canvasContext.js';
3
3
  import { GpuHalBase } from './gpuHalBase.js';
4
4
  function createShader(gl, type, source) {
@@ -43,8 +43,7 @@ function bindUniformBlock(gl, program, blockName, bindingPoint) {
43
43
  gl.uniformBlockBinding(program, idx, bindingPoint);
44
44
  }
45
45
  }
46
- // Set `DEBUG.webgl2 = true` in devtools (or `?webgl2-debug=1` in URL) to
47
- // enable verbose logging. Kept guarded so production builds stay quiet.
46
+ // `DEBUG.webgl2 = true` in devtools, or `?webgl2-debug=1`, logs verbosely.
48
47
  function debugEnabled() {
49
48
  if (typeof window === 'undefined') {
50
49
  return false;
@@ -91,59 +90,35 @@ function glBlendFactor(gl, factor) {
91
90
  return gl.ONE_MINUS_SRC_ALPHA;
92
91
  }
93
92
  }
94
- // Module-scope lifecycle tracking — Firefox caps active WebGL contexts
95
- // around 16 and Chrome around 8. Context leaks force the oldest contexts to
96
- // lose. These counters surface the leak when it happens.
93
+ // Live-context counts for the debug log: a leaked context evicts the oldest
94
+ // once the browser's cap is reached.
97
95
  let totalCreated = 0;
98
96
  let totalDisposed = 0;
99
- // The vertex-buffer ceiling this HAL refuses past, in bytes.
100
- //
101
- // WebGL2 exposes no max-buffer-size parameter, so unlike WebGPU there is
102
- // nothing to ask — but "ask the driver" was never what the guard was for. Its
103
- // job is to turn one pathological upload into the "zoom in" banner, and without
104
- // it the same upload that banners on WebGPU takes Chrome's context down here
105
- // instead. That is strictly worse than a blank track on a page at the context
106
- // ceiling: the loss evicts a sibling, whose recovery evicts another, and
107
- // GPU_CONTEXT_BUDGET.md is that cascade.
108
- //
109
- // 256 MiB is WebGPU's *spec default* `maxBufferSize`. It is not parity with
110
- // what the WebGPU backend actually refuses at: `gpuDevice.acquire` raises the
111
- // limit to `adapter.limits.maxBufferSize`, which is 2147483644 bytes (~2 GiB)
112
- // on the Firefox Nightly / Intel UHD 630 this was checked on, so WebGL2 is the
113
- // stricter of the two and a region can banner here while rendering there. That
114
- // asymmetry is accepted — a quarter-gigabyte single vertex buffer is not
115
- // something to hand an API that answers a failed allocation by dropping the
116
- // context.
117
- //
118
- // Deliberately a ceiling and not a budget: it catches the single upload
119
- // nothing else bounds, and says nothing about the total, which is
120
- // ARCHITECTURAL_LIMITS.md §"No session-level GPU memory budget".
97
+ // WebGPU's spec-default `maxBufferSize`, since WebGL2 has none to query: an
98
+ // upload past it banners rather than dropping the context
99
+ // (ARCHITECTURAL_LIMITS.md).
121
100
  const MAX_VERTEX_BUFFER_BYTES = 256 * 1024 * 1024;
122
- // Behavioral parity with WebGPUHal is enforced by tests, not by this file:
123
- // products/jbrowse-web/browser-tests/compare-backends.ts pixel-diffs webgl vs
124
- // webgpu vs canvas2d output, and shared buffer bookkeeping is covered by
125
- // hal/regionRegistry.test.ts. Neither HAL is where attribute layout is checked —
126
- // `assertVertexInputsMatch` does that at `pnpm gen:shaders` time, per shader and
127
- // per target. Mirror any behavior change in webgpuHal.ts — and note that
128
- // `GpuHalBase` already holds the parts that were only ever mirrored: the
129
- // descriptor map, the buffer registry, both upload shells and their over-limit
130
- // wording, and the dispose guard.
101
+ const NO_TEXTURES = [];
131
102
  export class WebGL2Hal extends GpuHalBase {
132
103
  gl;
133
104
  canvas;
134
- // Compiled passes, filled on demand by `getPass`. A pass whose program failed
135
- // to build caches `null` so the failure is reported once, not every frame.
136
- passes;
105
+ // `null` is a pass whose link failed, so it is reported once, not per frame.
106
+ passes = new Map();
107
+ glsl;
108
+ // By vertex source, then fragment source, attribute names and sampler as
109
+ // JSON: every pass id over one program shares it, and a failed link keeps
110
+ // its error.
111
+ programs = new Map();
112
+ // Each pass's textures, by the sampler that reads each.
113
+ passTextures = new Map();
137
114
  ubo;
138
- // Asked once: `getParameter` is a synchronous driver query and `limits()` is
139
- // now read on every upload, not only on a texture one.
115
+ // `getParameter` is a synchronous driver query; `limits()` runs per upload.
140
116
  maxTextureDim;
141
117
  debug = false;
142
118
  instanceId = 0;
143
119
  firstDrawSeen = new Set();
144
- // Latched on context loss, never cleared. GL objects from before the loss are
145
- // invalid even after restore — isContextLost()===false by then, so a live
146
- // check alone can't guard dispose().
120
+ // Never cleared: GL objects from before a loss stay invalid after restore,
121
+ // when isContextLost() already answers false.
147
122
  contextWasLost = false;
148
123
  contextLostListener = null;
149
124
  contextRestoredListener = null;
@@ -156,9 +131,18 @@ export class WebGL2Hal extends GpuHalBase {
156
131
  console.error(`[WebGL2Hal] GL error at "${label}": ${glErrorName(this.gl, err)}`);
157
132
  }
158
133
  }
159
- constructor(canvas, descriptors, uniformByteSize) {
134
+ /**
135
+ * Loads every pass's GLSL before the context is acquired, so a load that
136
+ * fails leaves the canvas free for Canvas2D.
137
+ */
138
+ static async create(canvas, descriptors, sampleCount = 4) {
139
+ const glsl = await Promise.all(descriptors.map(async (d) => [d.id, await d.source.glsl()]));
140
+ return new WebGL2Hal(canvas, descriptors, new Map(glsl), sampleCount);
141
+ }
142
+ constructor(canvas, descriptors, glsl, sampleCount) {
160
143
  super(descriptors, 'WebGL2Hal');
161
144
  this.canvas = canvas;
145
+ this.glsl = glsl;
162
146
  this.debug = debugEnabled();
163
147
  totalCreated += 1;
164
148
  this.instanceId = totalCreated;
@@ -178,23 +162,14 @@ export class WebGL2Hal extends GpuHalBase {
178
162
  canvas.addEventListener('webglcontextrestored', onContextRestored, false);
179
163
  this.contextLostListener = onContextLost;
180
164
  this.contextRestoredListener = onContextRestored;
181
- // premultipliedAlpha:true is required for correct AA edge blending.
182
- // The canvas is cleared to (0,0,0,0) and drawn with SRC_ALPHA,ONE_MINUS_SRC_ALPHA
183
- // blend, which produces premultiplied-alpha values in the framebuffer
184
- // (edge pixel: rgb = color*alpha, a = alpha). With premultipliedAlpha:true
185
- // the browser compositor reads those as premultiplied and composites correctly:
186
- // output = fb.rgb + bg*(1-fb.a)
187
- // With premultipliedAlpha:false the compositor treats them as straight alpha and
188
- // multiplies rgb by alpha a second time, making AA edges appear too dark.
189
- // The WebGPU HAL uses alphaMode:'premultiplied' for the same reason.
165
+ // The blend leaves premultiplied values in the framebuffer, which a
166
+ // straight-alpha compositor would darken at AA edges; WebGPU's alphaMode
167
+ // matches.
190
168
  const gl = canvas.getContext('webgl2', {
191
- antialias: true,
169
+ antialias: sampleCount > 1,
192
170
  premultipliedAlpha: true,
193
171
  });
194
172
  if (!gl) {
195
- // Was a bare 'WebGL2 not supported', which is the wrong diagnosis for the
196
- // case that actually reaches here in production: a re-init on a canvas the
197
- // WebGPU rung already committed. See canvasContext.ts.
198
173
  throw canvasContextError(canvas, 'webgl2');
199
174
  }
200
175
  noteCanvasContext(canvas, 'webgl2');
@@ -202,30 +177,21 @@ export class WebGL2Hal extends GpuHalBase {
202
177
  this.maxTextureDim = Number(gl.getParameter(gl.MAX_TEXTURE_SIZE));
203
178
  this.ubo = gl.createBuffer();
204
179
  gl.bindBuffer(gl.UNIFORM_BUFFER, this.ubo);
205
- gl.bufferData(gl.UNIFORM_BUFFER, uniformByteSize, gl.DYNAMIC_DRAW);
206
- this.passes = new Map();
207
- // Programs are built on first use (see `getPass`), not here: a renderer
208
- // declares every pass it could ever draw — alignments alone declares 21,
209
- // most of them behind a colorBy/arc/per-base setting — and linking one
210
- // costs tens of ms of driver time on the main thread. A profile of a
211
- // three-track LGV linked 29 programs and drew with 14.
212
- //
213
- // The first descriptor still links eagerly as a canary, so a GL stack that
214
- // can't compile our shaders at all throws from the constructor and
215
- // `createGpuHal` falls back to Canvas2D — the ladder only runs at
216
- // construction. Per-pass compile failures after that are caught in
217
- // `getPass` and surfaced through the error handler.
180
+ gl.bufferData(gl.UNIFORM_BUFFER, this.uniformByteSize, gl.DYNAMIC_DRAW);
181
+ // Programs link on first draw (`getPass`). The first links here as a
182
+ // canary, so a GL stack that cannot compile our shaders throws while
183
+ // `createGpuHal` can still fall back to Canvas2D.
218
184
  const canary = descriptors[0];
219
185
  if (canary) {
220
- this.compilePass(canary);
186
+ this.passes.set(canary.id, {
187
+ descriptor: canary,
188
+ linked: this.link(canary),
189
+ });
221
190
  }
222
191
  gl.enable(gl.BLEND);
223
192
  }
224
- // A fixed buffer ceiling rather than a queried one — see
225
- // MAX_VERTEX_BUFFER_BYTES for why the guard exists at all and why the number
226
- // is WebGPU's. Past it, an unguarded bufferData loses the context in Chrome
227
- // and throws RangeError in Firefox; neither is a getError() case, so the
228
- // base's report is the only place the display can be told.
193
+ // Past the ceiling an unguarded bufferData loses the context in Chrome and
194
+ // throws in Firefox, neither of them a getError() case.
229
195
  limits() {
230
196
  return {
231
197
  maxBufferBytes: MAX_VERTEX_BUFFER_BYTES,
@@ -239,28 +205,55 @@ export class WebGL2Hal extends GpuHalBase {
239
205
  gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
240
206
  return { vbo, count };
241
207
  }
242
- // No mid-frame deferral, unlike `WebGPUHal`'s hook: GL is immediate-mode, so
243
- // a draw already issued has consumed the buffer and deleting it between
244
- // beginFrame and endFrame is defined. The parity that matters is the
245
- // caller's — the same upload sequence is legal on both.
208
+ // No mid-frame deferral, unlike `WebGPUHal`: GL is immediate-mode, so a draw
209
+ // already issued has consumed the buffer.
246
210
  destroyBuffer(buf) {
247
211
  const gl = this.gl;
248
212
  if (!this.contextWasLost && !gl.isContextLost()) {
249
213
  gl.deleteBuffer(buf.vbo);
250
214
  }
251
215
  }
252
- compilePass(desc) {
216
+ // Throws on a failed link, and again on every later ask for that content.
217
+ link(desc) {
253
218
  const gl = this.gl;
254
- const program = createProgram(gl, desc.glslVertex, desc.glslFragment);
219
+ const stages = this.glsl.get(desc.id);
220
+ if (!stages) {
221
+ throw new Error(`no GLSL was loaded for pass "${desc.id}"`);
222
+ }
223
+ const { GLSL_VERTEX, GLSL_FRAGMENT } = stages;
224
+ const textures = desc.textures ?? [];
225
+ const names = desc.vertexAttributes.map(attr => attr.name);
226
+ const samplers = textures.map(t => [t.glUniformName, t.glTextureUnit]);
227
+ const key = JSON.stringify([GLSL_FRAGMENT, names, samplers]);
228
+ let byVertex = this.programs.get(GLSL_VERTEX);
229
+ if (!byVertex) {
230
+ byVertex = new Map();
231
+ this.programs.set(GLSL_VERTEX, byVertex);
232
+ }
233
+ const known = byVertex.get(key);
234
+ if (known instanceof Error) {
235
+ throw known;
236
+ }
237
+ if (known) {
238
+ return known;
239
+ }
240
+ let program;
241
+ try {
242
+ program = createProgram(gl, GLSL_VERTEX, GLSL_FRAGMENT);
243
+ }
244
+ catch (e) {
245
+ byVertex.set(key, e instanceof Error ? e : new Error(String(e)));
246
+ throw e;
247
+ }
255
248
  bindUniformBlock(gl, program, 'Uniforms', 0);
256
249
  this.checkGlError(`link pass "${desc.id}"`);
257
- const attrLocs = desc.vertexAttributes.map(attr => gl.getAttribLocation(program, attr.name));
250
+ const attrLocs = names.map(name => gl.getAttribLocation(program, name));
258
251
  if (this.debug) {
259
- const pairs = desc.vertexAttributes.map((a, i) => `${a.name}@${attrLocs[i]}`);
252
+ const pairs = names.map((name, i) => `${name}@${attrLocs[i]}`);
260
253
  console.warn(`[WebGL2Hal] pass "${desc.id}" stride=${desc.instanceStride} attrs: ${pairs.join(', ')}`);
261
- const missing = desc.vertexAttributes.filter((_, i) => attrLocs[i] < 0);
254
+ const missing = names.filter((_, i) => attrLocs[i] < 0);
262
255
  if (missing.length > 0) {
263
- console.warn(`[WebGL2Hal] pass "${desc.id}" missing attribute locations: ${missing.map(a => a.name).join(', ')}`);
256
+ console.warn(`[WebGL2Hal] pass "${desc.id}" missing attribute locations: ${missing.join(', ')}`);
264
257
  }
265
258
  }
266
259
  const vao = gl.createVertexArray();
@@ -272,26 +265,16 @@ export class WebGL2Hal extends GpuHalBase {
272
265
  }
273
266
  }
274
267
  gl.bindVertexArray(null);
275
- let textureState = null;
276
- const tb = desc.textures?.[0];
277
- if (tb) {
278
- // Bind the sampler uniform to the texture unit once — it never changes.
268
+ if (textures.length > 0) {
279
269
  gl.useProgram(program);
280
- gl.uniform1i(gl.getUniformLocation(program, tb.glUniformName), tb.glTextureUnit);
281
- textureState = { texture: null, unit: tb.glTextureUnit };
282
- }
283
- const state = {
284
- program,
285
- vao,
286
- descriptor: desc,
287
- textureState,
288
- attrLocs,
289
- };
290
- this.passes.set(desc.id, state);
291
- return state;
270
+ for (const tb of textures) {
271
+ gl.uniform1i(gl.getUniformLocation(program, tb.glUniformName), tb.glTextureUnit);
272
+ }
273
+ }
274
+ const linked = { program, vao, attrLocs };
275
+ byVertex.set(key, linked);
276
+ return linked;
292
277
  }
293
- // Compiled pass for `passId`, building it the first time it's asked for.
294
- // Returns undefined for an unknown id or a pass whose program failed to link.
295
278
  getPass(passId) {
296
279
  const existing = this.passes.get(passId);
297
280
  if (existing !== undefined) {
@@ -302,7 +285,9 @@ export class WebGL2Hal extends GpuHalBase {
302
285
  return undefined;
303
286
  }
304
287
  try {
305
- return this.compilePass(desc);
288
+ const pass = { descriptor: desc, linked: this.link(desc) };
289
+ this.passes.set(passId, pass);
290
+ return pass;
306
291
  }
307
292
  catch (e) {
308
293
  this.passes.set(passId, null);
@@ -311,26 +296,50 @@ export class WebGL2Hal extends GpuHalBase {
311
296
  }
312
297
  }
313
298
  resize(width, height) {
299
+ // A render autorun can fire between `dispose` and the model dropping its
300
+ // backend reference, and nothing destroys what a disposed HAL allocates.
301
+ if (this.disposed) {
302
+ return { x: getDpr(), y: getDpr() };
303
+ }
314
304
  return syncCanvasSize(this.canvas, width, height).scale;
315
305
  }
306
+ /**
307
+ * Nothing to give back. WebGL2 multisamples in the drawing buffer the context
308
+ * owns (`antialias` above one sample), so this HAL holds no separate target —
309
+ * releasing its samples means dropping the context, which costs a re-acquire
310
+ * against a hard 16-context ceiling and a full shader recompile per display.
311
+ * `agent-docs/reference/GPU_PORTABILITY.md` measured that trade and
312
+ * declined it; what makes the WebGPU side worth doing is that it pays
313
+ * neither.
314
+ */
315
+ releaseRenderTargets() { }
316
316
  createTexture(passId, binding, data, width, height) {
317
317
  const gl = this.gl;
318
- const ts = this.getPass(passId)?.textureState;
319
- if (!ts) {
320
- return;
318
+ let byName = this.passTextures.get(passId);
319
+ if (!byName) {
320
+ byName = new Map();
321
+ this.passTextures.set(passId, byName);
321
322
  }
322
- if (ts.texture) {
323
- gl.deleteTexture(ts.texture);
323
+ const existing = byName.get(binding.name);
324
+ if (existing) {
325
+ gl.deleteTexture(existing);
324
326
  }
325
327
  const tex = gl.createTexture();
326
328
  gl.bindTexture(gl.TEXTURE_2D, tex);
327
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
329
+ if (data instanceof Uint8Array) {
330
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
331
+ }
332
+ else {
333
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
334
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, data);
335
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
336
+ }
328
337
  const filter = binding.filter === 'linear' ? gl.LINEAR : gl.NEAREST;
329
338
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
330
339
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);
331
340
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
332
341
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
333
- ts.texture = tex;
342
+ byName.set(binding.name, tex);
334
343
  }
335
344
  writeUniforms(data) {
336
345
  const gl = this.gl;
@@ -360,10 +369,9 @@ export class WebGL2Hal extends GpuHalBase {
360
369
  clearViewport() {
361
370
  this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
362
371
  }
363
- drawPass(passId, regionKey, bufferPassId) {
372
+ drawPass(passId, regionKey, bufferPassId, verticesPerInstance) {
364
373
  const gl = this.gl;
365
- // Buffer first, program second: a pass with nothing to draw must not be
366
- // the reason its shader gets compiled.
374
+ // Buffer first, so a pass with nothing to draw links no program.
367
375
  const regionBuf = this.regions.get(regionKey, bufferPassId ?? passId);
368
376
  if (!regionBuf || regionBuf.count === 0) {
369
377
  return;
@@ -372,18 +380,26 @@ export class WebGL2Hal extends GpuHalBase {
372
380
  if (!pass) {
373
381
  return;
374
382
  }
383
+ const textures = this.passTextures.get(passId);
384
+ const samplers = pass.descriptor.textures ?? NO_TEXTURES;
385
+ if (samplers.some(tb => !textures?.has(tb.name))) {
386
+ return;
387
+ }
375
388
  this.applyBlendState(pass.descriptor);
376
- gl.useProgram(pass.program);
377
- gl.bindVertexArray(pass.vao);
389
+ gl.useProgram(pass.linked.program);
390
+ gl.bindVertexArray(pass.linked.vao);
378
391
  this.bindAttributes(pass, regionBuf.vbo);
379
- this.bindTextures(pass);
392
+ for (const tb of samplers) {
393
+ gl.activeTexture(gl.TEXTURE0 + tb.glTextureUnit);
394
+ gl.bindTexture(gl.TEXTURE_2D, textures.get(tb.name));
395
+ }
380
396
  const topo = pass.descriptor.topology ?? 'triangle-list';
381
397
  const glMode = topo === 'triangle-strip'
382
398
  ? gl.TRIANGLE_STRIP
383
399
  : topo === 'line-list'
384
400
  ? gl.LINES
385
401
  : gl.TRIANGLES;
386
- gl.drawArraysInstanced(glMode, 0, pass.descriptor.verticesPerInstance, regionBuf.count);
402
+ gl.drawArraysInstanced(glMode, 0, verticesPerInstance ?? pass.descriptor.verticesPerInstance, regionBuf.count);
387
403
  if (this.debug && !this.firstDrawSeen.has(passId)) {
388
404
  this.firstDrawSeen.add(passId);
389
405
  const err = gl.getError();
@@ -402,10 +418,6 @@ export class WebGL2Hal extends GpuHalBase {
402
418
  if (this.debug) {
403
419
  console.warn(`[WebGL2Hal #${this.instanceId}] DISPOSING context (live=${totalCreated - totalDisposed}/${totalCreated})`);
404
420
  }
405
- // Remove canvas event listeners to prevent closure references from keeping
406
- // the context alive after disposal. This is critical for test suites where
407
- // multiple contexts are created in sequence (e.g. Puppeteer page navigation).
408
- // Double-dispose is short-circuited above, so listeners are guaranteed set.
409
421
  if (this.contextLostListener) {
410
422
  this.canvas.removeEventListener('webglcontextlost', this.contextLostListener);
411
423
  }
@@ -414,24 +426,26 @@ export class WebGL2Hal extends GpuHalBase {
414
426
  }
415
427
  this.regions.deleteAll();
416
428
  if (!this.contextWasLost && !gl.isContextLost()) {
417
- for (const pass of this.passes.values()) {
418
- if (pass) {
419
- gl.deleteVertexArray(pass.vao);
420
- gl.deleteProgram(pass.program);
421
- if (pass.textureState?.texture) {
422
- gl.deleteTexture(pass.textureState.texture);
429
+ for (const byVertex of this.programs.values()) {
430
+ for (const linked of byVertex.values()) {
431
+ if (!(linked instanceof Error)) {
432
+ gl.deleteVertexArray(linked.vao);
433
+ gl.deleteProgram(linked.program);
423
434
  }
424
435
  }
425
436
  }
437
+ for (const byName of this.passTextures.values()) {
438
+ for (const texture of byName.values()) {
439
+ gl.deleteTexture(texture);
440
+ }
441
+ }
426
442
  gl.deleteBuffer(this.ubo);
427
443
  }
428
444
  this.passes.clear();
429
- // Firefox appears to treat WEBGL_lose_context.loseContext() as a
430
- // driver-wide reset: calling it on one disposed HAL synchronously
431
- // knocks out sibling live contexts too, so tracks go blank en masse.
432
- // Chrome only needs this as a test-suite optimisation; for production we
433
- // let the browser reclaim the context when the canvas is GC'd. If we
434
- // need explicit release again, gate it on navigator.userAgent.
445
+ this.programs.clear();
446
+ this.passTextures.clear();
447
+ // No WEBGL_lose_context.loseContext(): Firefox treats it as a driver-wide
448
+ // reset that blanks sibling live contexts, so the canvas's GC reclaims it.
435
449
  }
436
450
  applyBlendState(desc) {
437
451
  const gl = this.gl;
@@ -442,38 +456,27 @@ export class WebGL2Hal extends GpuHalBase {
442
456
  gl.enable(gl.BLEND);
443
457
  const bs = desc.blendState;
444
458
  if (bs?.op === 'max') {
445
- // MIN/MAX ignore blend factors: the framebuffer keeps the per-channel
446
- // max(src, dst). Used by same-color AA lines so overlapping segments union
447
- // instead of darkening. Reset explicitly below for every other pass.
448
459
  gl.blendEquation(gl.MAX);
449
460
  }
461
+ else if (bs?.op === 'behind') {
462
+ gl.blendEquation(gl.FUNC_ADD);
463
+ gl.blendFuncSeparate(gl.ONE_MINUS_DST_ALPHA, gl.ONE, gl.ONE_MINUS_DST_ALPHA, gl.ONE);
464
+ }
450
465
  else {
451
466
  gl.blendEquation(gl.FUNC_ADD);
452
- // RGB and alpha get different blend factors (blendFuncSeparate):
453
- // RGB: out = src_rgb * srcFactor + dst_rgb * dstFactor (default: src-alpha / 1-src-alpha)
454
- // Alpha: out = src_alpha * 1 + dst_alpha * (1 - src_alpha)
455
- // The alpha channel uses ONE/ONE_MINUS_SRC_ALPHA regardless of the custom blend state;
456
- // using the RGB srcFactor for alpha too would give out_alpha = src_alpha² + ..., which is wrong.
467
+ // Alpha keeps ONE / ONE_MINUS_SRC_ALPHA whatever the RGB factors: the
468
+ // RGB srcFactor on alpha would square it.
457
469
  const src = bs ? glBlendFactor(gl, bs.srcFactor) : gl.SRC_ALPHA;
458
470
  const dst = bs ? glBlendFactor(gl, bs.dstFactor) : gl.ONE_MINUS_SRC_ALPHA;
459
471
  gl.blendFuncSeparate(src, dst, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
460
472
  }
461
473
  }
462
- bindTextures(pass) {
463
- const gl = this.gl;
464
- const ts = pass.textureState;
465
- if (ts?.texture) {
466
- gl.activeTexture(gl.TEXTURE0 + ts.unit);
467
- gl.bindTexture(gl.TEXTURE_2D, ts.texture);
468
- // sampler uniform set once at construction — no uniform1i here
469
- }
470
- }
471
474
  bindAttributes(pass, vbo) {
472
475
  const gl = this.gl;
473
476
  const desc = pass.descriptor;
474
477
  gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
475
478
  for (let i = 0; i < desc.vertexAttributes.length; i++) {
476
- const loc = pass.attrLocs[i];
479
+ const loc = pass.linked.attrLocs[i];
477
480
  if (loc < 0) {
478
481
  continue;
479
482
  }
@@ -1,5 +1,5 @@
1
1
  import { GpuHalBase } from './gpuHalBase.ts';
2
- import type { GpuHal, PipelineDescriptor, SampleCount, TextureBinding } from './types.ts';
2
+ import type { GpuHal, PipelineDescriptor, SampleCount, TextureBinding, TextureSource } from './types.ts';
3
3
  interface RegionPassBuffer {
4
4
  dataBuffer: GPUBuffer;
5
5
  count: number;
@@ -11,13 +11,13 @@ export declare class WebGPUHal extends GpuHalBase<RegionPassBuffer> implements G
11
11
  private pipelines;
12
12
  private passTextures;
13
13
  private passBindGroups;
14
- private layouts;
15
14
  private alignedUniformSize;
16
15
  private uniformRingBuffer;
17
16
  private uniformStaging;
17
+ private uniformSlots;
18
18
  private uniformSlot;
19
+ private outgrownRings;
19
20
  private warnedUniformSlots;
20
- private uniformOnlyBindGroup;
21
21
  private sampleCount;
22
22
  private msaaTexture;
23
23
  private msaaView;
@@ -36,7 +36,7 @@ export declare class WebGPUHal extends GpuHalBase<RegionPassBuffer> implements G
36
36
  * selection spanning two sections re-uploads `OVERLAY_REGION` once per
37
37
  * section, inside the block loop.
38
38
  *
39
- * Deferring is what makes mid-frame replacement legal instead of merely
39
+ * Deferring the destroy makes mid-frame replacement legal instead of merely
40
40
  * warned about; synteny's `ensureUploaded` has always deleted a pass mid-frame
41
41
  * that the open pass never referenced, and that case was never the bug.
42
42
  */
@@ -47,7 +47,14 @@ export declare class WebGPUHal extends GpuHalBase<RegionPassBuffer> implements G
47
47
  private appliedViewport;
48
48
  private warnedSwapChainLoss;
49
49
  private swapChainUnrecoverable;
50
+ private reportedSurfaceFailure;
50
51
  private constructor();
52
+ private createRing;
53
+ private bindUntexturedPasses;
54
+ private growRing;
55
+ private bindGroupLayoutOf;
56
+ private createBindGroup;
57
+ private bindingResource;
51
58
  protected limits(): {
52
59
  maxBufferBytes: number;
53
60
  maxTextureDimensionPx: number;
@@ -66,8 +73,9 @@ export declare class WebGPUHal extends GpuHalBase<RegionPassBuffer> implements G
66
73
  * configured context behind with nothing to release it.
67
74
  */
68
75
  private configureContext;
69
- static create(canvas: HTMLCanvasElement, descriptors: PipelineDescriptor[], uniformByteSize: number, sampleCount: SampleCount): Promise<WebGPUHal | null>;
76
+ static create(canvas: HTMLCanvasElement, descriptors: PipelineDescriptor[], sampleCount: SampleCount): Promise<WebGPUHal | null>;
70
77
  resize(width: number, height: number): import("./types.ts").CanvasScale;
78
+ releaseRenderTargets(): void;
71
79
  private recreateMsaaTexture;
72
80
  /**
73
81
  * Bind group matching the pipeline layout of `passId`.
@@ -80,24 +88,23 @@ export declare class WebGPUHal extends GpuHalBase<RegionPassBuffer> implements G
80
88
  * (and a dropped draw) the moment one side samples a texture.
81
89
  *
82
90
  * Nothing in a bind group varies per region either: it references the
83
- * HAL-wide uniform ring buffer plus the pass's own texture/sampler. So
84
- * uniform-only passes all share `uniformOnlyBindGroup`, and a textured pass
85
- * gets exactly one, cached until `uploadTexture` replaces its texture.
86
- * Returns undefined when a pass needs a texture that hasn't arrived yet;
87
- * drawPass skips those.
91
+ * HAL-wide uniform ring buffer plus the pass's own texture/sampler. Returns
92
+ * undefined when a pass needs a texture that hasn't arrived yet; drawPass
93
+ * skips those.
88
94
  */
89
95
  private getBindGroup;
96
+ private bindTexture;
90
97
  /**
91
98
  * The one place a GPU resource this HAL owns is released — see
92
99
  * {@link pendingDestroy} for why the frame decides when.
93
100
  *
94
101
  * Every buffer release routes here through the `RegionRegistry` destroy hook,
95
- * so `uploadBuffer`, `deleteBuffer`, `deleteRegion`, `pruneRegions` and
102
+ * so `uploadBuffer`, `deleteBuffer`, `deleteRegion` and
96
103
  * `dispose` are all covered without any of them knowing a frame is open.
97
104
  */
98
105
  private destroyWhenIdle;
99
106
  private drainPendingDestroy;
100
- protected createTexture(passId: string, binding: TextureBinding, data: Uint8Array, width: number, height: number): void;
107
+ protected createTexture(passId: string, binding: TextureBinding, data: TextureSource, width: number, height: number): void;
101
108
  writeUniforms(data: ArrayBuffer): void;
102
109
  /**
103
110
  * The canvas texture to draw this frame into, or null when the swap chain is
@@ -120,8 +127,17 @@ export declare class WebGPUHal extends GpuHalBase<RegionPassBuffer> implements G
120
127
  * Retry is what builds a fresh HAL.
121
128
  */
122
129
  private acquireTextureView;
130
+ /**
131
+ * Once per HAL, not once per frame: the refusal holds for every frame at this
132
+ * canvas size, and dotplot and synteny keep their canvas mounted through a
133
+ * `renderError`, so a per-frame report there never stops.
134
+ *
135
+ * Not terminal — a refusal tracks the canvas dimensions, so a resize can clear
136
+ * it. `swapChainUnrecoverable` stays for the case it was written for.
137
+ */
138
+ private reportSurfaceFailure;
123
139
  beginFrame(clearR: number, clearG: number, clearB: number, clearA?: number): void;
124
- drawPass(passId: string, regionKey: number, bufferPassId?: string): void;
140
+ drawPass(passId: string, regionKey: number, bufferPassId?: string, verticesPerInstance?: number): void;
125
141
  /**
126
142
  * Close the frame's state whether the submit landed or threw.
127
143
  *