@perspective-dev/viewer-charts 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/LICENSE.md +193 -0
  2. package/dist/cdn/perspective-viewer-charts.js +3 -0
  3. package/dist/cdn/perspective-viewer-charts.js.map +7 -0
  4. package/dist/esm/axis/axis-primitives.d.ts +24 -0
  5. package/dist/esm/axis/bar-axis.d.ts +51 -0
  6. package/dist/esm/axis/canvas.d.ts +24 -0
  7. package/dist/esm/axis/categorical-axis-core.d.ts +42 -0
  8. package/dist/esm/axis/categorical-axis.d.ts +27 -0
  9. package/dist/esm/axis/facet-chrome.d.ts +13 -0
  10. package/dist/esm/axis/label-geometry.d.ts +41 -0
  11. package/dist/esm/axis/legend.d.ts +44 -0
  12. package/dist/esm/axis/numeric-axis.d.ts +20 -0
  13. package/dist/esm/charts/candlestick/candlestick-build.d.ts +129 -0
  14. package/dist/esm/charts/candlestick/candlestick-interact.d.ts +10 -0
  15. package/dist/esm/charts/candlestick/candlestick-render.d.ts +24 -0
  16. package/dist/esm/charts/candlestick/candlestick.d.ts +144 -0
  17. package/dist/esm/charts/candlestick/glyphs/draw-candlesticks.d.ts +36 -0
  18. package/dist/esm/charts/candlestick/glyphs/draw-ohlc.d.ts +33 -0
  19. package/dist/esm/charts/canvas-types.d.ts +15 -0
  20. package/dist/esm/charts/cartesian/cartesian-build.d.ts +14 -0
  21. package/dist/esm/charts/cartesian/cartesian-interact.d.ts +20 -0
  22. package/dist/esm/charts/cartesian/cartesian-render.d.ts +26 -0
  23. package/dist/esm/charts/cartesian/cartesian.d.ts +239 -0
  24. package/dist/esm/charts/cartesian/glyph.d.ts +53 -0
  25. package/dist/esm/charts/cartesian/glyphs/density.d.ts +142 -0
  26. package/dist/esm/charts/cartesian/glyphs/lines.d.ts +23 -0
  27. package/dist/esm/charts/cartesian/glyphs/points.d.ts +24 -0
  28. package/dist/esm/charts/cartesian/label-interner.d.ts +21 -0
  29. package/dist/esm/charts/cartesian/tooltip-lines.d.ts +11 -0
  30. package/dist/esm/charts/chart-base.d.ts +402 -0
  31. package/dist/esm/charts/chart.d.ts +338 -0
  32. package/dist/esm/charts/common/band-layout.d.ts +32 -0
  33. package/dist/esm/charts/common/categorical-y-chart.d.ts +53 -0
  34. package/dist/esm/charts/common/category-axis-resolver.d.ts +90 -0
  35. package/dist/esm/charts/common/chrome-cache.d.ts +18 -0
  36. package/dist/esm/charts/common/draw-tooltip-box.d.ts +9 -0
  37. package/dist/esm/charts/common/leaf-color.d.ts +33 -0
  38. package/dist/esm/charts/common/node-store.d.ts +81 -0
  39. package/dist/esm/charts/common/tree-chart.d.ts +48 -0
  40. package/dist/esm/charts/common/tree-chrome.d.ts +31 -0
  41. package/dist/esm/charts/common/tree-data.d.ts +54 -0
  42. package/dist/esm/charts/common/visible-extent.d.ts +51 -0
  43. package/dist/esm/charts/heatmap/heatmap-build.d.ts +86 -0
  44. package/dist/esm/charts/heatmap/heatmap-interact.d.ts +19 -0
  45. package/dist/esm/charts/heatmap/heatmap-render.d.ts +19 -0
  46. package/dist/esm/charts/heatmap/heatmap-y-axis.d.ts +46 -0
  47. package/dist/esm/charts/heatmap/heatmap.d.ts +117 -0
  48. package/dist/esm/charts/map/map.d.ts +67 -0
  49. package/dist/esm/charts/registry.d.ts +14 -0
  50. package/dist/esm/charts/series/glyphs/draw-areas.d.ts +30 -0
  51. package/dist/esm/charts/series/glyphs/draw-bars.d.ts +15 -0
  52. package/dist/esm/charts/series/glyphs/draw-lines.d.ts +34 -0
  53. package/dist/esm/charts/series/glyphs/draw-scatter.d.ts +33 -0
  54. package/dist/esm/charts/series/series-build.d.ts +228 -0
  55. package/dist/esm/charts/series/series-interact.d.ts +35 -0
  56. package/dist/esm/charts/series/series-render.d.ts +41 -0
  57. package/dist/esm/charts/series/series-type.d.ts +49 -0
  58. package/dist/esm/charts/series/series.d.ts +317 -0
  59. package/dist/esm/charts/sunburst/sunburst-interact.d.ts +7 -0
  60. package/dist/esm/charts/sunburst/sunburst-layout.d.ts +33 -0
  61. package/dist/esm/charts/sunburst/sunburst-render.d.ts +22 -0
  62. package/dist/esm/charts/sunburst/sunburst.d.ts +85 -0
  63. package/dist/esm/charts/treemap/treemap-interact.d.ts +12 -0
  64. package/dist/esm/charts/treemap/treemap-layout.d.ts +28 -0
  65. package/dist/esm/charts/treemap/treemap-render.d.ts +18 -0
  66. package/dist/esm/charts/treemap/treemap.d.ts +74 -0
  67. package/dist/esm/config.d.ts +27 -0
  68. package/dist/esm/data/lazy-row.d.ts +32 -0
  69. package/dist/esm/data/split-groups.d.ts +20 -0
  70. package/dist/esm/data/view-reader.d.ts +35 -0
  71. package/dist/esm/event-detail.d.ts +28 -0
  72. package/dist/esm/index.d.ts +3 -0
  73. package/dist/esm/interaction/hit-test.d.ts +30 -0
  74. package/dist/esm/interaction/host-sink-dom.d.ts +19 -0
  75. package/dist/esm/interaction/host-sink-message.d.ts +46 -0
  76. package/dist/esm/interaction/lazy-tooltip.d.ts +61 -0
  77. package/dist/esm/interaction/raw-event-forwarder.d.ts +27 -0
  78. package/dist/esm/interaction/spatial-grid.d.ts +15 -0
  79. package/dist/esm/interaction/tooltip-controller.d.ts +193 -0
  80. package/dist/esm/interaction/zoom-controller.d.ts +106 -0
  81. package/dist/esm/interaction/zoom-router.d.ts +48 -0
  82. package/dist/esm/layout/facet-grid.d.ts +126 -0
  83. package/dist/esm/layout/plot-layout.d.ts +104 -0
  84. package/dist/esm/layout/ticks.d.ts +17 -0
  85. package/dist/esm/map/mercator.d.ts +102 -0
  86. package/dist/esm/map/tile-cache.d.ts +38 -0
  87. package/dist/esm/map/tile-layer.d.ts +66 -0
  88. package/dist/esm/map/tile-loader.d.ts +52 -0
  89. package/dist/esm/map/tile-source.d.ts +66 -0
  90. package/dist/esm/perspective-viewer-charts.js +3 -0
  91. package/dist/esm/perspective-viewer-charts.js.map +7 -0
  92. package/dist/esm/plugin/charts.d.ts +40 -0
  93. package/dist/esm/plugin/plugin.d.ts +95 -0
  94. package/dist/esm/render/scheduler.d.ts +41 -0
  95. package/dist/esm/theme/gradient.d.ts +48 -0
  96. package/dist/esm/theme/palette.d.ts +13 -0
  97. package/dist/esm/theme/theme-snapshot.d.ts +7 -0
  98. package/dist/esm/theme/theme.d.ts +53 -0
  99. package/dist/esm/transport/protocol.d.ts +430 -0
  100. package/dist/esm/transport/renderer-transport.d.ts +201 -0
  101. package/dist/esm/utils/css.d.ts +1 -0
  102. package/dist/esm/utils/font-snapshot.d.ts +50 -0
  103. package/dist/esm/webgl/buffer-pool.d.ts +62 -0
  104. package/dist/esm/webgl/context-manager.d.ts +184 -0
  105. package/dist/esm/webgl/gradient-texture.d.ts +17 -0
  106. package/dist/esm/webgl/instanced-attrs.d.ts +44 -0
  107. package/dist/esm/webgl/plot-frame.d.ts +39 -0
  108. package/dist/esm/webgl/program-cache.d.ts +13 -0
  109. package/dist/esm/webgl/shader-manifest.d.ts +53 -0
  110. package/dist/esm/webgl/shader-registry.d.ts +22 -0
  111. package/dist/esm/worker/boot.d.ts +0 -0
  112. package/dist/esm/worker/dispatch.d.ts +9 -0
  113. package/dist/esm/worker/font-loader.d.ts +2 -0
  114. package/dist/esm/worker/renderer.worker.d.ts +115 -0
  115. package/dist/esm/worker/session-host.d.ts +26 -0
  116. package/package.json +47 -0
  117. package/src/css/perspective-viewer-charts.css +95 -0
  118. package/src/ts/axis/axis-primitives.ts +125 -0
  119. package/src/ts/axis/bar-axis.ts +345 -0
  120. package/src/ts/axis/canvas.ts +64 -0
  121. package/src/ts/axis/categorical-axis-core.ts +125 -0
  122. package/src/ts/axis/categorical-axis.ts +716 -0
  123. package/src/ts/axis/facet-chrome.ts +42 -0
  124. package/src/ts/axis/label-geometry.ts +188 -0
  125. package/src/ts/axis/legend.ts +218 -0
  126. package/src/ts/axis/numeric-axis.ts +353 -0
  127. package/src/ts/charts/candlestick/candlestick-build.ts +516 -0
  128. package/src/ts/charts/candlestick/candlestick-interact.ts +256 -0
  129. package/src/ts/charts/candlestick/candlestick-render.ts +387 -0
  130. package/src/ts/charts/candlestick/candlestick.ts +367 -0
  131. package/src/ts/charts/candlestick/glyphs/draw-candlesticks.ts +432 -0
  132. package/src/ts/charts/candlestick/glyphs/draw-ohlc.ts +317 -0
  133. package/src/ts/charts/canvas-types.ts +30 -0
  134. package/src/ts/charts/cartesian/cartesian-build.ts +616 -0
  135. package/src/ts/charts/cartesian/cartesian-interact.ts +355 -0
  136. package/src/ts/charts/cartesian/cartesian-render.ts +948 -0
  137. package/src/ts/charts/cartesian/cartesian.ts +469 -0
  138. package/src/ts/charts/cartesian/glyph.ts +81 -0
  139. package/src/ts/charts/cartesian/glyphs/density.ts +1263 -0
  140. package/src/ts/charts/cartesian/glyphs/lines.ts +320 -0
  141. package/src/ts/charts/cartesian/glyphs/points.ts +239 -0
  142. package/src/ts/charts/cartesian/label-interner.ts +56 -0
  143. package/src/ts/charts/cartesian/tooltip-lines.ts +80 -0
  144. package/src/ts/charts/chart-base.ts +840 -0
  145. package/src/ts/charts/chart.ts +427 -0
  146. package/src/ts/charts/common/band-layout.ts +63 -0
  147. package/src/ts/charts/common/categorical-y-chart.ts +81 -0
  148. package/src/ts/charts/common/category-axis-resolver.ts +314 -0
  149. package/src/ts/charts/common/chrome-cache.ts +79 -0
  150. package/src/ts/charts/common/draw-tooltip-box.ts +84 -0
  151. package/src/ts/charts/common/leaf-color.ts +92 -0
  152. package/src/ts/charts/common/node-store.ts +235 -0
  153. package/src/ts/charts/common/tree-chart.ts +76 -0
  154. package/src/ts/charts/common/tree-chrome.ts +123 -0
  155. package/src/ts/charts/common/tree-data.ts +623 -0
  156. package/src/ts/charts/common/visible-extent.ts +112 -0
  157. package/src/ts/charts/heatmap/heatmap-build.ts +426 -0
  158. package/src/ts/charts/heatmap/heatmap-interact.ts +274 -0
  159. package/src/ts/charts/heatmap/heatmap-render.ts +815 -0
  160. package/src/ts/charts/heatmap/heatmap-y-axis.ts +351 -0
  161. package/src/ts/charts/heatmap/heatmap.ts +368 -0
  162. package/src/ts/charts/map/map.ts +201 -0
  163. package/src/ts/charts/registry.ts +65 -0
  164. package/src/ts/charts/series/glyphs/draw-areas.ts +331 -0
  165. package/src/ts/charts/series/glyphs/draw-bars.ts +113 -0
  166. package/src/ts/charts/series/glyphs/draw-lines.ts +320 -0
  167. package/src/ts/charts/series/glyphs/draw-scatter.ts +328 -0
  168. package/src/ts/charts/series/series-build.ts +848 -0
  169. package/src/ts/charts/series/series-interact.ts +604 -0
  170. package/src/ts/charts/series/series-render.ts +1109 -0
  171. package/src/ts/charts/series/series-type.ts +99 -0
  172. package/src/ts/charts/series/series.ts +794 -0
  173. package/src/ts/charts/sunburst/sunburst-interact.ts +460 -0
  174. package/src/ts/charts/sunburst/sunburst-layout.ts +238 -0
  175. package/src/ts/charts/sunburst/sunburst-render.ts +887 -0
  176. package/src/ts/charts/sunburst/sunburst.ts +248 -0
  177. package/src/ts/charts/treemap/treemap-interact.ts +445 -0
  178. package/src/ts/charts/treemap/treemap-layout.ts +328 -0
  179. package/src/ts/charts/treemap/treemap-render.ts +886 -0
  180. package/src/ts/charts/treemap/treemap.ts +247 -0
  181. package/src/ts/config.ts +41 -0
  182. package/src/ts/data/lazy-row.ts +140 -0
  183. package/src/ts/data/split-groups.ts +97 -0
  184. package/src/ts/data/view-reader.ts +107 -0
  185. package/src/ts/event-detail.ts +44 -0
  186. package/src/ts/index.ts +53 -0
  187. package/src/ts/interaction/hit-test.ts +106 -0
  188. package/src/ts/interaction/host-sink-dom.ts +85 -0
  189. package/src/ts/interaction/host-sink-message.ts +75 -0
  190. package/src/ts/interaction/lazy-tooltip.ts +102 -0
  191. package/src/ts/interaction/raw-event-forwarder.ts +175 -0
  192. package/src/ts/interaction/spatial-grid.ts +100 -0
  193. package/src/ts/interaction/tooltip-controller.ts +407 -0
  194. package/src/ts/interaction/zoom-controller.ts +468 -0
  195. package/src/ts/interaction/zoom-router.ts +230 -0
  196. package/src/ts/layout/facet-grid.ts +346 -0
  197. package/src/ts/layout/plot-layout.ts +277 -0
  198. package/src/ts/layout/ticks.ts +168 -0
  199. package/src/ts/map/mercator.ts +204 -0
  200. package/src/ts/map/tile-cache.ts +96 -0
  201. package/src/ts/map/tile-layer.ts +382 -0
  202. package/src/ts/map/tile-loader.ts +143 -0
  203. package/src/ts/map/tile-source.ts +156 -0
  204. package/src/ts/plugin/charts.ts +286 -0
  205. package/src/ts/plugin/plugin.ts +668 -0
  206. package/src/ts/render/scheduler.ts +339 -0
  207. package/src/ts/shaders/area.frag.glsl +20 -0
  208. package/src/ts/shaders/area.vert.glsl +19 -0
  209. package/src/ts/shaders/bar.frag.glsl +25 -0
  210. package/src/ts/shaders/bar.vert.glsl +60 -0
  211. package/src/ts/shaders/candlestick-body.frag.glsl +19 -0
  212. package/src/ts/shaders/candlestick-body.vert.glsl +34 -0
  213. package/src/ts/shaders/density-extreme.frag.glsl +30 -0
  214. package/src/ts/shaders/density-mrt.frag.glsl +44 -0
  215. package/src/ts/shaders/density-mrt.vert.glsl +48 -0
  216. package/src/ts/shaders/density-resolve.frag.glsl +89 -0
  217. package/src/ts/shaders/density-resolve.vert.glsl +23 -0
  218. package/src/ts/shaders/density-splat.frag.glsl +34 -0
  219. package/src/ts/shaders/density-splat.vert.glsl +52 -0
  220. package/src/ts/shaders/gridline.frag.glsl +18 -0
  221. package/src/ts/shaders/gridline.vert.glsl +18 -0
  222. package/src/ts/shaders/heatmap.frag.glsl +23 -0
  223. package/src/ts/shaders/heatmap.vert.glsl +42 -0
  224. package/src/ts/shaders/line-uniform.frag.glsl +26 -0
  225. package/src/ts/shaders/line-uniform.vert.glsl +54 -0
  226. package/src/ts/shaders/line.frag.glsl +28 -0
  227. package/src/ts/shaders/line.vert.glsl +87 -0
  228. package/src/ts/shaders/scatter.frag.glsl +39 -0
  229. package/src/ts/shaders/scatter.vert.glsl +67 -0
  230. package/src/ts/shaders/sunburst-arc.frag.glsl +19 -0
  231. package/src/ts/shaders/sunburst-arc.vert.glsl +79 -0
  232. package/src/ts/shaders/tile.frag.glsl +27 -0
  233. package/src/ts/shaders/tile.vert.glsl +35 -0
  234. package/src/ts/shaders/treemap.frag.glsl +19 -0
  235. package/src/ts/shaders/treemap.vert.glsl +25 -0
  236. package/src/ts/shaders/y-scatter.frag.glsl +30 -0
  237. package/src/ts/shaders/y-scatter.vert.glsl +31 -0
  238. package/src/ts/theme/gradient.ts +312 -0
  239. package/src/ts/theme/palette.ts +64 -0
  240. package/src/ts/theme/theme-snapshot.ts +66 -0
  241. package/src/ts/theme/theme.ts +166 -0
  242. package/src/ts/transport/protocol.ts +497 -0
  243. package/src/ts/transport/renderer-transport.ts +788 -0
  244. package/src/ts/utils/css.ts +36 -0
  245. package/src/ts/utils/font-snapshot.ts +159 -0
  246. package/src/ts/webgl/buffer-pool.ts +163 -0
  247. package/src/ts/webgl/context-manager.ts +414 -0
  248. package/src/ts/webgl/gradient-texture.ts +84 -0
  249. package/src/ts/webgl/instanced-attrs.ts +139 -0
  250. package/src/ts/webgl/plot-frame.ts +91 -0
  251. package/src/ts/webgl/program-cache.ts +46 -0
  252. package/src/ts/webgl/shader-manifest.ts +148 -0
  253. package/src/ts/webgl/shader-registry.ts +97 -0
  254. package/src/ts/worker/boot.ts +22 -0
  255. package/src/ts/worker/dispatch.ts +99 -0
  256. package/src/ts/worker/font-loader.ts +89 -0
  257. package/src/ts/worker/renderer.worker.ts +734 -0
  258. package/src/ts/worker/session-host.ts +118 -0
@@ -0,0 +1,414 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ import { ShaderRegistry } from "./shader-registry";
14
+ import { SHADER_MANIFEST } from "./shader-manifest";
15
+ import { BufferPool } from "./buffer-pool";
16
+
17
+ export type WebGLCanvas = HTMLCanvasElement | OffscreenCanvas;
18
+
19
+ export interface WebGLContextManagerOptions {
20
+ /**
21
+ * If `true`, compile + link every shader in `SHADER_MANIFEST`
22
+ * during construction (and again after a context-loss/restore
23
+ * cycle). Trades ~30-100ms of init time for elimination of the
24
+ * compile/link cost on the first-frame path of every chart type
25
+ * that ends up rendered. Default `false` keeps the original
26
+ * lazy-compile behavior — programs are compiled on first
27
+ * `getOrCreate(name, ...)` call from a glyph's render path.
28
+ */
29
+ precompile?: boolean;
30
+ }
31
+
32
+ export class WebGLContextManager {
33
+ private _canvas: WebGLCanvas;
34
+ private _gl: WebGL2RenderingContext | WebGLRenderingContext;
35
+ private _isWebGL2: boolean;
36
+ private _shaders: ShaderRegistry;
37
+ private _buffers: BufferPool;
38
+ private _uploadedCount = 0;
39
+ private _cssWidth = 0;
40
+ private _cssHeight = 0;
41
+ private _dpr = 1;
42
+ private _precompile: boolean;
43
+ private _frameCallback: ((bitmap: ImageBitmap) => void) | null = null;
44
+
45
+ /**
46
+ * Per-instance `MessageChannel` used by `_yieldToTask` to resume a
47
+ * polling `awaitGpuFence` loop on the next task. Allocated lazily —
48
+ * the polling path is rarely hit when the GPU is idle, and many
49
+ * `WebGLContextManager` instances never need one.
50
+ *
51
+ * Must be per-instance: a module-level singleton races when two
52
+ * managers poll concurrently. Both call sites assign
53
+ * `port1.onmessage = resolve`, the second assignment overwrites the
54
+ * first, and the first poll's promise never settles — leaving its
55
+ * `awaitGpuFence` hung. The hang propagates up through the render
56
+ * scheduler's `present` → `uploadAndRender` → the worker's
57
+ * `uploadChunkAck` → the host's `with_typed_arrays` callback,
58
+ * stalling `draw()` indefinitely. (Now that fence waits across
59
+ * different `WebGLContextManager`s run in parallel inside one
60
+ * scheduler drain, the per-instance discipline matters even
61
+ * more — concurrent poll loops are the common case, not the
62
+ * exception.)
63
+ *
64
+ * Cost of per-instance allocation: one extra `MessageChannel` (two
65
+ * `MessagePort`s, ~negligible bytes, zero idle CPU) per chart on
66
+ * top of the per-chart proxy channel that the transport already
67
+ * holds. Bounded by chart count; safe even for pathological pages
68
+ * with hundreds of charts. The alternative — allocating a fresh
69
+ * channel on every `_yieldToTask` call — would churn ports on every
70
+ * fence poll (potentially many per frame on slow GPUs), which is
71
+ * far worse for the structured-clone subsystem than holding one
72
+ * port pair for the manager's lifetime.
73
+ */
74
+ private _yieldChannel: MessageChannel | null = null;
75
+
76
+ constructor(canvas: WebGLCanvas, options: WebGLContextManagerOptions = {}) {
77
+ this._canvas = canvas;
78
+ this._precompile = options.precompile ?? false;
79
+ const gl2 = canvas.getContext("webgl2", {
80
+ antialias: true,
81
+ alpha: true,
82
+ premultipliedAlpha: false,
83
+ });
84
+
85
+ if (gl2) {
86
+ this._gl = gl2 as WebGL2RenderingContext;
87
+ this._isWebGL2 = true;
88
+ } else {
89
+ const gl1 = canvas.getContext("webgl", {
90
+ antialias: true,
91
+ alpha: true,
92
+ premultipliedAlpha: false,
93
+ });
94
+
95
+ if (!gl1) {
96
+ throw new Error("WebGL is not supported");
97
+ }
98
+
99
+ this._gl = gl1 as WebGLRenderingContext;
100
+ this._isWebGL2 = false;
101
+ }
102
+
103
+ this._shaders = new ShaderRegistry(this._gl);
104
+ this._buffers = new BufferPool(this._gl);
105
+
106
+ if (this._precompile) {
107
+ this._shaders.precompile(SHADER_MANIFEST);
108
+ }
109
+
110
+ // Both `HTMLCanvasElement` and `OffscreenCanvas` dispatch
111
+ // `webglcontextlost` / `webglcontextrestored` events on the
112
+ // canvas itself. `addEventListener` exists on both.
113
+ (canvas as EventTarget).addEventListener(
114
+ "webglcontextlost",
115
+ (e: Event) => {
116
+ e.preventDefault();
117
+ },
118
+ );
119
+
120
+ (canvas as EventTarget).addEventListener("webglcontextrestored", () => {
121
+ this._shaders.releaseAll();
122
+ this._buffers.releaseAll();
123
+ this._shaders = new ShaderRegistry(this._gl);
124
+ this._buffers = new BufferPool(this._gl);
125
+ this._uploadedCount = 0;
126
+
127
+ // Re-prime the cache after restore so post-recovery
128
+ // first-frame doesn't re-pay the lazy compile cost.
129
+ if (this._precompile) {
130
+ this._shaders.precompile(SHADER_MANIFEST);
131
+ }
132
+ });
133
+ }
134
+
135
+ get gl(): WebGL2RenderingContext | WebGLRenderingContext {
136
+ return this._gl;
137
+ }
138
+
139
+ get isWebGL2(): boolean {
140
+ return this._isWebGL2;
141
+ }
142
+
143
+ get shaders(): ShaderRegistry {
144
+ return this._shaders;
145
+ }
146
+
147
+ get bufferPool(): BufferPool {
148
+ return this._buffers;
149
+ }
150
+
151
+ get uploadedCount(): number {
152
+ return this._uploadedCount;
153
+ }
154
+
155
+ set uploadedCount(count: number) {
156
+ this._uploadedCount = count;
157
+ }
158
+
159
+ /**
160
+ * Resize the GL canvas's bitmap to match the host's CSS layout. The
161
+ * Host is responsible for measuring the DOM element (or otherwise
162
+ * deciding the target CSS size) and the device pixel ratio — the
163
+ * manager itself does not touch DOM, so the same code path works
164
+ * whether the canvas is an `HTMLCanvasElement` (in-process) or an
165
+ * `OffscreenCanvas` (in-process via transfer, or in a worker).
166
+ */
167
+ resize(cssWidth: number, cssHeight: number, dpr: number): void {
168
+ this._cssWidth = cssWidth;
169
+ this._cssHeight = cssHeight;
170
+ this._dpr = dpr;
171
+
172
+ const width = Math.round(cssWidth * dpr);
173
+ const height = Math.round(cssHeight * dpr);
174
+
175
+ if (this._canvas.width !== width || this._canvas.height !== height) {
176
+ this._canvas.width = width;
177
+ this._canvas.height = height;
178
+ this._gl.viewport(0, 0, width, height);
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Pending dimensions to apply at the start of the next render.
184
+ * `null` when no resize is queued. See {@link requestResize} /
185
+ * {@link applyPendingResize}.
186
+ */
187
+ private _pendingResize: {
188
+ cssWidth: number;
189
+ cssHeight: number;
190
+ dpr: number;
191
+ } | null = null;
192
+
193
+ /**
194
+ * Record a dimension change to be applied at the start of the
195
+ * next render's Phase 1, *before* `_fullRender` runs. The actual
196
+ * `canvas.width = N` assignment (which clears the drawing buffer
197
+ * per the WebGL spec) happens inside `applyPendingResize()`,
198
+ * paired in the same synchronous task as the paint that fills
199
+ * the new buffer.
200
+ *
201
+ * Why split the dimension change off from the existing
202
+ * {@link resize} method: in direct / in-process modes the
203
+ * GL canvas IS the host's visible canvas, and `canvas.width = N`
204
+ * is immediately observable to the browser's compositor as a
205
+ * cleared buffer. If the resize lands in the message handler
206
+ * (one task) but the matching `_fullRender` lands in the next
207
+ * RAF (a later task), the compositor cycles between them and
208
+ * presents one full frame of empty canvas — visible flicker.
209
+ * Deferring the dimension change to the same RAF as the paint
210
+ * eliminates the inter-frame gap; both happen inside Phase 1's
211
+ * un-yielded loop.
212
+ *
213
+ * Multiple `requestResize` calls before the next render coalesce
214
+ * to last-write-wins — five rapid width changes from a window
215
+ * drag produce one resize+paint, not five.
216
+ */
217
+ requestResize(cssWidth: number, cssHeight: number, dpr: number): void {
218
+ this._pendingResize = { cssWidth, cssHeight, dpr };
219
+ }
220
+
221
+ /**
222
+ * Apply any pending dimension change recorded by
223
+ * {@link requestResize}. Called by the scheduler's Phase 1
224
+ * (immediately before each entry's `fullRender`) and by the
225
+ * `snapshotPng` bypass path. Returns `true` when a resize was
226
+ * applied, `false` when there was nothing pending — useful for
227
+ * callers that want to skip a no-op render.
228
+ */
229
+ applyPendingResize(): boolean {
230
+ if (!this._pendingResize) {
231
+ return false;
232
+ }
233
+
234
+ const { cssWidth, cssHeight, dpr } = this._pendingResize;
235
+ this._pendingResize = null;
236
+ this.resize(cssWidth, cssHeight, dpr);
237
+ return true;
238
+ }
239
+
240
+ /**
241
+ * Last CSS width passed to `resize()`.
242
+ */
243
+ get cssWidth(): number {
244
+ return this._cssWidth;
245
+ }
246
+
247
+ /**
248
+ * Last CSS height passed to `resize()`.
249
+ */
250
+ get cssHeight(): number {
251
+ return this._cssHeight;
252
+ }
253
+
254
+ /**
255
+ * Last device pixel ratio passed to `resize()`.
256
+ */
257
+ get dpr(): number {
258
+ return this._dpr;
259
+ }
260
+
261
+ clear(): void {
262
+ this._gl.clearColor(0, 0, 0, 0);
263
+ this._gl.clear(this._gl.COLOR_BUFFER_BIT | this._gl.DEPTH_BUFFER_BIT);
264
+ this._uploadedCount = 0;
265
+ }
266
+
267
+ /**
268
+ * Register a per-frame hook invoked at the end of each render. In
269
+ * blit-mode rendering, the worker installs a callback that
270
+ * transfers an `ImageBitmap` from `_canvas` (an `OffscreenCanvas`)
271
+ * back to the host so the visible display canvas can `drawImage`
272
+ * it. In direct mode the callback is left null and `endFrame` is a
273
+ * no-op.
274
+ *
275
+ * Pass `null` to detach.
276
+ */
277
+ setFrameCallback(cb: ((bitmap: ImageBitmap) => void) | null): void {
278
+ this._frameCallback = cb;
279
+ }
280
+
281
+ /**
282
+ * Called by chart impls at the bottom of `_fullRender` (and any
283
+ * other path that produces a complete frame). When a frame
284
+ * callback is registered AND the GL surface is an
285
+ * `OffscreenCanvas`, ship its current contents as an
286
+ * `ImageBitmap` to the host. Otherwise no-op — direct-mode
287
+ * rendering has nothing to ship; the visible canvas already holds
288
+ * the drawing buffer.
289
+ */
290
+ endFrame(): void {
291
+ if (!this._frameCallback) {
292
+ return;
293
+ }
294
+
295
+ const canvas = this._canvas as
296
+ | OffscreenCanvas
297
+ | (HTMLCanvasElement & { transferToImageBitmap?: never });
298
+ if (
299
+ typeof (canvas as OffscreenCanvas).transferToImageBitmap !==
300
+ "function"
301
+ ) {
302
+ return;
303
+ }
304
+
305
+ const bitmap = (canvas as OffscreenCanvas).transferToImageBitmap();
306
+ this._frameCallback(bitmap);
307
+ }
308
+
309
+ /**
310
+ * Resolve when every GL command submitted up to this call has been
311
+ * executed by the GPU.
312
+ *
313
+ * On WebGL2 this issues a `fenceSync(SYNC_GPU_COMMANDS_COMPLETE)`
314
+ * and polls `clientWaitSync` with a zero timeout, yielding to the
315
+ * task queue between polls. The first poll passes
316
+ * `SYNC_FLUSH_COMMANDS_BIT` so the fence becomes reachable without
317
+ * a separate `gl.flush()`.
318
+ *
319
+ * On WebGL1 there is no fenceSync; we fall back to the blocking
320
+ * `gl.finish()`. This is acceptable in a worker — never call this
321
+ * from the main thread on a heavy frame.
322
+ *
323
+ * Used as a per-frame "GPU is idle" barrier so callers can serialize
324
+ * follow-on work (`endFrame` snapshot, present roundtrip, the next
325
+ * chunk upload) against actual GPU completion instead of the
326
+ * implicit, implementation-defined timing of `transferToImageBitmap`.
327
+ */
328
+ async awaitGpuFence(): Promise<void> {
329
+ if (!this._isWebGL2) {
330
+ this._gl.finish();
331
+ return;
332
+ }
333
+
334
+ const gl = this._gl as WebGL2RenderingContext;
335
+ const fence = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
336
+ if (!fence) {
337
+ gl.finish();
338
+ return;
339
+ }
340
+
341
+ try {
342
+ let flags: GLbitfield = gl.SYNC_FLUSH_COMMANDS_BIT;
343
+ while (true) {
344
+ const status = gl.clientWaitSync(fence, flags, 0);
345
+ if (
346
+ status === gl.ALREADY_SIGNALED ||
347
+ status === gl.CONDITION_SATISFIED
348
+ ) {
349
+ return;
350
+ }
351
+
352
+ if (status === gl.WAIT_FAILED) {
353
+ gl.finish();
354
+ return;
355
+ }
356
+
357
+ flags = 0;
358
+ await this._yieldToTask();
359
+ }
360
+ } finally {
361
+ gl.deleteSync(fence);
362
+ }
363
+ }
364
+
365
+ ensureBufferCapacity(totalRows: number): void {
366
+ this._buffers.ensureCapacity(totalRows);
367
+ }
368
+
369
+ /**
370
+ * Yield to the task queue between fence polls. We avoid
371
+ * `setTimeout(0)` because Chromium clamps nested `setTimeout` to
372
+ * ~4ms in workers, which would inflate the measured cost of
373
+ * `awaitGpuFence`. A reused per-instance `MessageChannel` lands the
374
+ * resume in the next task with sub-ms latency.
375
+ *
376
+ * `addEventListener(..., { once: true })` is used over
377
+ * `port1.onmessage = ...` so concurrent in-flight resumes (should
378
+ * any path ever introduce them) cannot clobber each other's
379
+ * resolvers — the previous module-level singleton lost a resolver
380
+ * on every overlap and hung one chart's `draw()` indefinitely.
381
+ */
382
+ private _yieldToTask(): Promise<void> {
383
+ if (!this._yieldChannel) {
384
+ this._yieldChannel = new MessageChannel();
385
+ // `addEventListener` does not auto-start a `MessagePort` —
386
+ // only `onmessage = ...` does. Start once at allocation so
387
+ // posted resumes are actually delivered.
388
+ this._yieldChannel.port1.start();
389
+ }
390
+
391
+ return new Promise<void>((resolve) => {
392
+ const ch = this._yieldChannel!;
393
+ ch.port1.addEventListener("message", () => resolve(), {
394
+ once: true,
395
+ });
396
+ ch.port2.postMessage(null);
397
+ });
398
+ }
399
+
400
+ destroy(): void {
401
+ this._buffers.releaseAll();
402
+ this._shaders.releaseAll();
403
+ if (this._yieldChannel) {
404
+ this._yieldChannel.port1.close();
405
+ this._yieldChannel.port2.close();
406
+ this._yieldChannel = null;
407
+ }
408
+
409
+ const ext = this._gl.getExtension("WEBGL_lose_context");
410
+ if (ext) {
411
+ ext.loseContext();
412
+ }
413
+ }
414
+ }
@@ -0,0 +1,84 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ import { buildGradientLUT, type GradientStop } from "../theme/gradient";
14
+ import type { WebGLContextManager } from "./context-manager";
15
+
16
+ const LUT_SIZE = 256;
17
+
18
+ export interface GradientTextureCache {
19
+ texture: WebGLTexture;
20
+
21
+ // The `GradientStop[]` reference last uploaded. `resolveTheme` returns a
22
+ // fresh object per render, so comparing the array reference is enough to
23
+ // detect a theme change and skip the upload otherwise.
24
+ lastStops: GradientStop[] | null;
25
+ }
26
+
27
+ /**
28
+ * Allocate a 256×1 RGBA8 texture (once) and re-upload the LUT only when
29
+ * `stops` has changed since the last call. Typical render path: zero
30
+ * GPU work beyond binding an already-uploaded texture.
31
+ */
32
+ export function ensureGradientTexture(
33
+ glManager: WebGLContextManager,
34
+ cache: GradientTextureCache | null,
35
+ stops: GradientStop[],
36
+ ): GradientTextureCache {
37
+ const gl = glManager.gl;
38
+
39
+ let texture: WebGLTexture;
40
+ if (cache?.texture) {
41
+ texture = cache.texture;
42
+ if (cache.lastStops === stops) {
43
+ return cache;
44
+ } // no-op fast path
45
+ } else {
46
+ texture = gl.createTexture()!;
47
+ }
48
+
49
+ const lut = buildGradientLUT(stops, LUT_SIZE);
50
+ gl.bindTexture(gl.TEXTURE_2D, texture);
51
+ gl.texImage2D(
52
+ gl.TEXTURE_2D,
53
+ 0,
54
+ gl.RGBA,
55
+ LUT_SIZE,
56
+ 1,
57
+ 0,
58
+ gl.RGBA,
59
+ gl.UNSIGNED_BYTE,
60
+ lut,
61
+ );
62
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
63
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
64
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
65
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
66
+
67
+ return { texture, lastStops: stops };
68
+ }
69
+
70
+ /**
71
+ * Bind `texture` to a texture unit and set the sampler uniform. Call after
72
+ * `useProgram`.
73
+ */
74
+ export function bindGradientTexture(
75
+ glManager: WebGLContextManager,
76
+ texture: WebGLTexture,
77
+ samplerLoc: WebGLUniformLocation | null,
78
+ unit: number = 0,
79
+ ): void {
80
+ const gl = glManager.gl;
81
+ gl.activeTexture(gl.TEXTURE0 + unit);
82
+ gl.bindTexture(gl.TEXTURE_2D, texture);
83
+ gl.uniform1i(samplerLoc, unit);
84
+ }
@@ -0,0 +1,139 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ import type { WebGLContextManager } from "./context-manager";
14
+
15
+ type GL = WebGL2RenderingContext | WebGLRenderingContext;
16
+
17
+ export interface Instancing {
18
+ setDivisor(location: number, divisor: number): void;
19
+ drawArraysInstanced(
20
+ mode: number,
21
+ first: number,
22
+ count: number,
23
+ instances: number,
24
+ ): void;
25
+ }
26
+
27
+ /**
28
+ * Return an Instancing helper for the given GL context. On WebGL2, native
29
+ * `vertexAttribDivisor`/`drawArraysInstanced` are used; on WebGL1 the
30
+ * ANGLE_instanced_arrays extension is looked up and cached by caller.
31
+ * Negative attribute locations (optimized-out attributes) are tolerated by
32
+ * `setDivisor` and ignored.
33
+ */
34
+ export function getInstancing(glManager: WebGLContextManager): Instancing {
35
+ const gl = glManager.gl;
36
+ if (glManager.isWebGL2) {
37
+ const gl2 = gl as WebGL2RenderingContext;
38
+ return {
39
+ setDivisor(location, divisor) {
40
+ if (location < 0) {
41
+ return;
42
+ }
43
+
44
+ gl2.vertexAttribDivisor(location, divisor);
45
+ },
46
+ drawArraysInstanced(mode, first, count, instances) {
47
+ gl2.drawArraysInstanced(mode, first, count, instances);
48
+ },
49
+ };
50
+ }
51
+
52
+ const ext = gl.getExtension(
53
+ "ANGLE_instanced_arrays",
54
+ ) as ANGLE_instanced_arrays | null;
55
+ return {
56
+ setDivisor(location, divisor) {
57
+ if (location < 0) {
58
+ return;
59
+ }
60
+
61
+ ext?.vertexAttribDivisorANGLE(location, divisor);
62
+ },
63
+ drawArraysInstanced(mode, first, count, instances) {
64
+ ext?.drawArraysInstancedANGLE(mode, first, count, instances);
65
+ },
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Allocate the static `[0, 1, 2, 3]` line-strip corner buffer used by
71
+ * every instanced line/wick glyph: each corner index is multiplied by
72
+ * `LINE_WIDTH_PX` in the shader to expand a 2-vertex segment into a
73
+ * `TRIANGLE_STRIP` quad. Identical contents across all callers; one
74
+ * helper avoids the four-way `createBuffer` / `bufferData` boilerplate.
75
+ */
76
+ export function createLineCornerBuffer(gl: GL): WebGLBuffer {
77
+ const buffer = gl.createBuffer()!;
78
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
79
+ gl.bufferData(
80
+ gl.ARRAY_BUFFER,
81
+ new Float32Array([0, 1, 2, 3]),
82
+ gl.STATIC_DRAW,
83
+ );
84
+ return buffer;
85
+ }
86
+
87
+ /**
88
+ * Allocate the static `[(0,0), (1,0), (0,1), (1,1)]` quad-strip corner
89
+ * buffer used by instanced rect glyphs (candlestick body). Used as a
90
+ * `vec2 a_corner` attribute that the shader scales by per-instance
91
+ * width/height to expand into a `TRIANGLE_STRIP` rect.
92
+ */
93
+ export function createQuadCornerBuffer(gl: GL): WebGLBuffer {
94
+ const buffer = gl.createBuffer()!;
95
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
96
+ gl.bufferData(
97
+ gl.ARRAY_BUFFER,
98
+ new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]),
99
+ gl.STATIC_DRAW,
100
+ );
101
+ return buffer;
102
+ }
103
+
104
+ /**
105
+ * Bind a per-instance float attribute from a named buffer in the buffer
106
+ * pool. Returns `true` when the bind happened, `false` when the buffer
107
+ * has not yet been allocated (caller should skip the draw rather than
108
+ * paint zero data). No-op return `true` when `attr` is negative
109
+ * (optimized-out attribute — drawing the rest is still valid).
110
+ *
111
+ * Render-path uses `peek`, never `getOrCreate`: the latter recreates
112
+ * with zero-initialized contents when `_totalCapacity` has grown past
113
+ * the current buffer, which would wipe the previous draw's vertex
114
+ * data and leave `drawArraysInstanced` to render zeros. See
115
+ * {@link BufferPool#peek} for the full rationale.
116
+ */
117
+ export function bindInstancedFloatAttr(
118
+ glManager: WebGLContextManager,
119
+ instancing: Instancing,
120
+ attr: number,
121
+ name: string,
122
+ components: number,
123
+ ): boolean {
124
+ if (attr < 0) {
125
+ return true;
126
+ }
127
+
128
+ const buf = glManager.bufferPool.peek(name);
129
+ if (!buf) {
130
+ return false;
131
+ }
132
+
133
+ const gl: GL = glManager.gl;
134
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf.buffer);
135
+ gl.enableVertexAttribArray(attr);
136
+ gl.vertexAttribPointer(attr, components, gl.FLOAT, false, 0, 0);
137
+ instancing.setDivisor(attr, 1);
138
+ return true;
139
+ }