@363045841yyt/klinechart-core 0.9.0-alpha.0 → 0.9.0-alpha.2

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 (372) hide show
  1. package/dist/controllers/createChartController.d.ts.map +1 -1
  2. package/dist/controllers/createChartController.js +31 -13
  3. package/dist/controllers/createChartController.js.map +1 -1
  4. package/dist/controllers/index.d.ts +1 -0
  5. package/dist/controllers/index.d.ts.map +1 -1
  6. package/dist/controllers/index.js.map +1 -1
  7. package/dist/controllers/types.d.ts +12 -0
  8. package/dist/controllers/types.d.ts.map +1 -1
  9. package/dist/engine/chart.d.ts +29 -11
  10. package/dist/engine/chart.d.ts.map +1 -1
  11. package/dist/engine/chart.js +147 -25
  12. package/dist/engine/chart.js.map +1 -1
  13. package/dist/engine/controller/interaction.d.ts +29 -4
  14. package/dist/engine/controller/interaction.d.ts.map +1 -1
  15. package/dist/engine/controller/interaction.js +78 -31
  16. package/dist/engine/controller/interaction.js.map +1 -1
  17. package/dist/engine/data/incrementalLoadHint.d.ts.map +1 -1
  18. package/dist/engine/data/incrementalLoadHint.js +5 -1
  19. package/dist/engine/data/incrementalLoadHint.js.map +1 -1
  20. package/dist/engine/drawing/DrawingState.d.ts +19 -34
  21. package/dist/engine/drawing/DrawingState.d.ts.map +1 -1
  22. package/dist/engine/drawing/DrawingState.js +110 -92
  23. package/dist/engine/drawing/DrawingState.js.map +1 -1
  24. package/dist/engine/drawing/index.d.ts +4 -1
  25. package/dist/engine/drawing/index.d.ts.map +1 -1
  26. package/dist/engine/drawing/index.js +9 -4
  27. package/dist/engine/drawing/index.js.map +1 -1
  28. package/dist/engine/drawing/interaction.d.ts +9 -29
  29. package/dist/engine/drawing/interaction.d.ts.map +1 -1
  30. package/dist/engine/drawing/interaction.js +23 -40
  31. package/dist/engine/drawing/interaction.js.map +1 -1
  32. package/dist/engine/indicators/chartIndicatorManager.d.ts.map +1 -1
  33. package/dist/engine/indicators/chartIndicatorManager.js +4 -12
  34. package/dist/engine/indicators/chartIndicatorManager.js.map +1 -1
  35. package/dist/engine/layout/chartPaneLayout.d.ts +0 -2
  36. package/dist/engine/layout/chartPaneLayout.d.ts.map +1 -1
  37. package/dist/engine/layout/chartPaneLayout.js +5 -9
  38. package/dist/engine/layout/chartPaneLayout.js.map +1 -1
  39. package/dist/engine/layout/pane.d.ts +0 -39
  40. package/dist/engine/layout/pane.d.ts.map +1 -1
  41. package/dist/engine/layout/pane.js +0 -9
  42. package/dist/engine/layout/pane.js.map +1 -1
  43. package/dist/engine/paneRenderer.d.ts +1 -10
  44. package/dist/engine/paneRenderer.d.ts.map +1 -1
  45. package/dist/engine/paneRenderer.js +2 -19
  46. package/dist/engine/paneRenderer.js.map +1 -1
  47. package/dist/engine/render/chartRenderer.d.ts +49 -8
  48. package/dist/engine/render/chartRenderer.d.ts.map +1 -1
  49. package/dist/engine/render/chartRenderer.js +143 -72
  50. package/dist/engine/render/chartRenderer.js.map +1 -1
  51. package/dist/engine/renderers/Indicator/atr.js +3 -13
  52. package/dist/engine/renderers/Indicator/atr.js.map +1 -1
  53. package/dist/engine/renderers/Indicator/boll.d.ts.map +1 -1
  54. package/dist/engine/renderers/Indicator/boll.js +12 -24
  55. package/dist/engine/renderers/Indicator/boll.js.map +1 -1
  56. package/dist/engine/renderers/Indicator/cci.js +6 -13
  57. package/dist/engine/renderers/Indicator/cci.js.map +1 -1
  58. package/dist/engine/renderers/Indicator/chaikinVol.js +3 -11
  59. package/dist/engine/renderers/Indicator/chaikinVol.js.map +1 -1
  60. package/dist/engine/renderers/Indicator/cmf.js +3 -11
  61. package/dist/engine/renderers/Indicator/cmf.js.map +1 -1
  62. package/dist/engine/renderers/Indicator/dema.js +3 -11
  63. package/dist/engine/renderers/Indicator/dema.js.map +1 -1
  64. package/dist/engine/renderers/Indicator/donchian.js +3 -11
  65. package/dist/engine/renderers/Indicator/donchian.js.map +1 -1
  66. package/dist/engine/renderers/Indicator/ene.d.ts.map +1 -1
  67. package/dist/engine/renderers/Indicator/ene.js +12 -24
  68. package/dist/engine/renderers/Indicator/ene.js.map +1 -1
  69. package/dist/engine/renderers/Indicator/expma.d.ts.map +1 -1
  70. package/dist/engine/renderers/Indicator/expma.js +5 -11
  71. package/dist/engine/renderers/Indicator/expma.js.map +1 -1
  72. package/dist/engine/renderers/Indicator/fastk.js +6 -13
  73. package/dist/engine/renderers/Indicator/fastk.js.map +1 -1
  74. package/dist/engine/renderers/Indicator/fib.js +3 -11
  75. package/dist/engine/renderers/Indicator/fib.js.map +1 -1
  76. package/dist/engine/renderers/Indicator/hma.js +3 -11
  77. package/dist/engine/renderers/Indicator/hma.js.map +1 -1
  78. package/dist/engine/renderers/Indicator/hv.js +3 -11
  79. package/dist/engine/renderers/Indicator/hv.js.map +1 -1
  80. package/dist/engine/renderers/Indicator/ichimoku.js +24 -18
  81. package/dist/engine/renderers/Indicator/ichimoku.js.map +1 -1
  82. package/dist/engine/renderers/Indicator/kama.js +3 -11
  83. package/dist/engine/renderers/Indicator/kama.js.map +1 -1
  84. package/dist/engine/renderers/Indicator/keltner.js +3 -11
  85. package/dist/engine/renderers/Indicator/keltner.js.map +1 -1
  86. package/dist/engine/renderers/Indicator/kst.js +9 -17
  87. package/dist/engine/renderers/Indicator/kst.js.map +1 -1
  88. package/dist/engine/renderers/Indicator/ma.d.ts.map +1 -1
  89. package/dist/engine/renderers/Indicator/ma.js +10 -21
  90. package/dist/engine/renderers/Indicator/ma.js.map +1 -1
  91. package/dist/engine/renderers/Indicator/macd.js +14 -44
  92. package/dist/engine/renderers/Indicator/macd.js.map +1 -1
  93. package/dist/engine/renderers/Indicator/mfi.js +3 -11
  94. package/dist/engine/renderers/Indicator/mfi.js.map +1 -1
  95. package/dist/engine/renderers/Indicator/mom.js +6 -13
  96. package/dist/engine/renderers/Indicator/mom.js.map +1 -1
  97. package/dist/engine/renderers/Indicator/obv.js +3 -11
  98. package/dist/engine/renderers/Indicator/obv.js.map +1 -1
  99. package/dist/engine/renderers/Indicator/parkinson.js +3 -11
  100. package/dist/engine/renderers/Indicator/parkinson.js.map +1 -1
  101. package/dist/engine/renderers/Indicator/pvt.js +3 -11
  102. package/dist/engine/renderers/Indicator/pvt.js.map +1 -1
  103. package/dist/engine/renderers/Indicator/roc.js +3 -11
  104. package/dist/engine/renderers/Indicator/roc.js.map +1 -1
  105. package/dist/engine/renderers/Indicator/rsi.js +12 -20
  106. package/dist/engine/renderers/Indicator/rsi.js.map +1 -1
  107. package/dist/engine/renderers/Indicator/shared/webglBand.d.ts +0 -2
  108. package/dist/engine/renderers/Indicator/shared/webglBand.d.ts.map +1 -1
  109. package/dist/engine/renderers/Indicator/shared/webglBand.js +0 -6
  110. package/dist/engine/renderers/Indicator/shared/webglBand.js.map +1 -1
  111. package/dist/engine/renderers/Indicator/stoch.js +9 -17
  112. package/dist/engine/renderers/Indicator/stoch.js.map +1 -1
  113. package/dist/engine/renderers/Indicator/tema.js +3 -11
  114. package/dist/engine/renderers/Indicator/tema.js.map +1 -1
  115. package/dist/engine/renderers/Indicator/trix.js +3 -11
  116. package/dist/engine/renderers/Indicator/trix.js.map +1 -1
  117. package/dist/engine/renderers/Indicator/vma.js +3 -11
  118. package/dist/engine/renderers/Indicator/vma.js.map +1 -1
  119. package/dist/engine/renderers/Indicator/vwap.js +3 -11
  120. package/dist/engine/renderers/Indicator/vwap.js.map +1 -1
  121. package/dist/engine/renderers/Indicator/wma.js +3 -11
  122. package/dist/engine/renderers/Indicator/wma.js.map +1 -1
  123. package/dist/engine/renderers/Indicator/wmsr.js +6 -13
  124. package/dist/engine/renderers/Indicator/wmsr.js.map +1 -1
  125. package/dist/engine/renderers/candle.d.ts.map +1 -1
  126. package/dist/engine/renderers/candle.js +21 -28
  127. package/dist/engine/renderers/candle.js.map +1 -1
  128. package/dist/engine/renderers/candleViaRenderer.d.ts +19 -0
  129. package/dist/engine/renderers/candleViaRenderer.d.ts.map +1 -0
  130. package/dist/engine/renderers/candleViaRenderer.js +15 -0
  131. package/dist/engine/renderers/candleViaRenderer.js.map +1 -0
  132. package/dist/engine/renderers/linesViaRenderer.d.ts +55 -0
  133. package/dist/engine/renderers/linesViaRenderer.d.ts.map +1 -0
  134. package/dist/engine/renderers/linesViaRenderer.js +169 -0
  135. package/dist/engine/renderers/linesViaRenderer.js.map +1 -0
  136. package/dist/engine/renderers/rectsViaRenderer.d.ts +18 -0
  137. package/dist/engine/renderers/rectsViaRenderer.d.ts.map +1 -0
  138. package/dist/engine/renderers/rectsViaRenderer.js +63 -0
  139. package/dist/engine/renderers/rectsViaRenderer.js.map +1 -0
  140. package/dist/engine/renderers/subVolume.js +7 -26
  141. package/dist/engine/renderers/subVolume.js.map +1 -1
  142. package/dist/engine/renderers/timeAxis.js +1 -1
  143. package/dist/engine/renderers/timeAxis.js.map +1 -1
  144. package/dist/engine/state/chartStateKernel.d.ts +11 -2
  145. package/dist/engine/state/chartStateKernel.d.ts.map +1 -1
  146. package/dist/engine/state/chartStateKernel.js +28 -12
  147. package/dist/engine/state/chartStateKernel.js.map +1 -1
  148. package/dist/engine/state/index.d.ts +1 -1
  149. package/dist/engine/state/index.d.ts.map +1 -1
  150. package/dist/engine/state/index.js +1 -1
  151. package/dist/engine/state/index.js.map +1 -1
  152. package/dist/engine/state/interactionState.d.ts +15 -7
  153. package/dist/engine/state/interactionState.d.ts.map +1 -1
  154. package/dist/engine/state/interactionState.js +55 -60
  155. package/dist/engine/state/interactionState.js.map +1 -1
  156. package/dist/engine/state/rendererState.d.ts +14 -0
  157. package/dist/engine/state/rendererState.d.ts.map +1 -0
  158. package/dist/engine/state/rendererState.js +25 -0
  159. package/dist/engine/state/rendererState.js.map +1 -0
  160. package/dist/engine/state/themeState.d.ts +11 -4
  161. package/dist/engine/state/themeState.d.ts.map +1 -1
  162. package/dist/engine/state/themeState.js +13 -5
  163. package/dist/engine/state/themeState.js.map +1 -1
  164. package/dist/engine/state/viewportState.d.ts.map +1 -1
  165. package/dist/engine/state/viewportState.js +7 -3
  166. package/dist/engine/state/viewportState.js.map +1 -1
  167. package/dist/engine/subPaneManager.d.ts.map +1 -1
  168. package/dist/engine/subPaneManager.js +2 -10
  169. package/dist/engine/subPaneManager.js.map +1 -1
  170. package/dist/foundation/config/chartSettings.d.ts +16 -4
  171. package/dist/foundation/config/chartSettings.d.ts.map +1 -1
  172. package/dist/foundation/config/chartSettings.js +21 -6
  173. package/dist/foundation/config/chartSettings.js.map +1 -1
  174. package/dist/foundation/plugin/index.d.ts +0 -1
  175. package/dist/foundation/plugin/index.d.ts.map +1 -1
  176. package/dist/foundation/plugin/rendererPluginManager.d.ts +1 -22
  177. package/dist/foundation/plugin/rendererPluginManager.d.ts.map +1 -1
  178. package/dist/foundation/plugin/rendererPluginManager.js +0 -84
  179. package/dist/foundation/plugin/rendererPluginManager.js.map +1 -1
  180. package/dist/foundation/plugin/types.d.ts +11 -17
  181. package/dist/foundation/plugin/types.d.ts.map +1 -1
  182. package/dist/foundation/plugin/types.js.map +1 -1
  183. package/dist/foundation/reactivity/frameTransaction.d.ts +71 -0
  184. package/dist/foundation/reactivity/frameTransaction.d.ts.map +1 -0
  185. package/dist/foundation/reactivity/frameTransaction.js +170 -0
  186. package/dist/foundation/reactivity/frameTransaction.js.map +1 -0
  187. package/dist/foundation/reactivity/index.d.ts +4 -2
  188. package/dist/foundation/reactivity/index.d.ts.map +1 -1
  189. package/dist/foundation/reactivity/index.js +2 -1
  190. package/dist/foundation/reactivity/index.js.map +1 -1
  191. package/dist/foundation/reactivity/signal.d.ts +20 -0
  192. package/dist/foundation/reactivity/signal.d.ts.map +1 -1
  193. package/dist/foundation/reactivity/signal.js +27 -0
  194. package/dist/foundation/reactivity/signal.js.map +1 -1
  195. package/dist/foundation/tokens/theme-dark.d.ts.map +1 -1
  196. package/dist/foundation/tokens/theme-dark.js +2 -1
  197. package/dist/foundation/tokens/theme-dark.js.map +1 -1
  198. package/dist/foundation/tokens/theme-light.d.ts.map +1 -1
  199. package/dist/foundation/tokens/theme-light.js +2 -1
  200. package/dist/foundation/tokens/theme-light.js.map +1 -1
  201. package/dist/rendering/render/Renderer.d.ts +29 -14
  202. package/dist/rendering/render/Renderer.d.ts.map +1 -1
  203. package/dist/rendering/render/Renderer.js +3 -10
  204. package/dist/rendering/render/Renderer.js.map +1 -1
  205. package/dist/rendering/render/SurfaceBackend.d.ts +5 -7
  206. package/dist/rendering/render/SurfaceBackend.d.ts.map +1 -1
  207. package/dist/rendering/render/SurfaceBackend.js +5 -7
  208. package/dist/rendering/render/SurfaceBackend.js.map +1 -1
  209. package/dist/rendering/render/createCanvas2DRenderer.d.ts +3 -0
  210. package/dist/rendering/render/createCanvas2DRenderer.d.ts.map +1 -0
  211. package/dist/rendering/render/createCanvas2DRenderer.js +74 -0
  212. package/dist/rendering/render/createCanvas2DRenderer.js.map +1 -0
  213. package/dist/rendering/render/createDefaultRendererHost.d.ts +4 -0
  214. package/dist/rendering/render/createDefaultRendererHost.d.ts.map +1 -0
  215. package/dist/rendering/render/createDefaultRendererHost.js +54 -0
  216. package/dist/rendering/render/createDefaultRendererHost.js.map +1 -0
  217. package/dist/rendering/render/createWebGLRenderer.d.ts.map +1 -1
  218. package/dist/rendering/render/createWebGLRenderer.js +42 -80
  219. package/dist/rendering/render/createWebGLRenderer.js.map +1 -1
  220. package/dist/rendering/render/createWebGPURenderer.d.ts +11 -0
  221. package/dist/rendering/render/createWebGPURenderer.d.ts.map +1 -0
  222. package/dist/rendering/render/createWebGPURenderer.js +619 -0
  223. package/dist/rendering/render/createWebGPURenderer.js.map +1 -0
  224. package/dist/rendering/render/createWebGPUSurfaceBackend.d.ts +15 -0
  225. package/dist/rendering/render/createWebGPUSurfaceBackend.d.ts.map +1 -0
  226. package/dist/rendering/render/createWebGPUSurfaceBackend.js +92 -0
  227. package/dist/rendering/render/createWebGPUSurfaceBackend.js.map +1 -0
  228. package/dist/rendering/render/frameMetrics.d.ts +19 -0
  229. package/dist/rendering/render/frameMetrics.d.ts.map +1 -0
  230. package/dist/rendering/render/frameMetrics.js +43 -0
  231. package/dist/rendering/render/frameMetrics.js.map +1 -0
  232. package/dist/rendering/render/index.d.ts +10 -0
  233. package/dist/rendering/render/index.d.ts.map +1 -1
  234. package/dist/rendering/render/index.js +6 -0
  235. package/dist/rendering/render/index.js.map +1 -1
  236. package/dist/rendering/render/rendererHost.d.ts +34 -0
  237. package/dist/rendering/render/rendererHost.d.ts.map +1 -0
  238. package/dist/rendering/render/rendererHost.js +144 -0
  239. package/dist/rendering/render/rendererHost.js.map +1 -0
  240. package/dist/rendering/render/webgpuResourceTable.d.ts +25 -0
  241. package/dist/rendering/render/webgpuResourceTable.d.ts.map +1 -0
  242. package/dist/rendering/render/webgpuResourceTable.js +54 -0
  243. package/dist/rendering/render/webgpuResourceTable.js.map +1 -0
  244. package/dist/rendering/scene/createLayerFromPlugin.d.ts.map +1 -1
  245. package/dist/rendering/scene/createLayerFromPlugin.js +10 -2
  246. package/dist/rendering/scene/createLayerFromPlugin.js.map +1 -1
  247. package/dist/rendering/scene/layerRegistry.d.ts +3 -5
  248. package/dist/rendering/scene/layerRegistry.d.ts.map +1 -1
  249. package/dist/rendering/scene/layerRegistry.js +1 -1
  250. package/dist/rendering/scene/layerRegistry.js.map +1 -1
  251. package/dist/rendering/scene/retainedScene.d.ts +60 -0
  252. package/dist/rendering/scene/retainedScene.d.ts.map +1 -0
  253. package/dist/rendering/scene/retainedScene.js +42 -0
  254. package/dist/rendering/scene/retainedScene.js.map +1 -0
  255. package/dist/version.d.ts +1 -1
  256. package/dist/version.js +1 -1
  257. package/package.json +2 -1
  258. package/src/__tests__/signal.test.ts +47 -1
  259. package/src/controllers/__tests__/createChartController.theme.test.ts +102 -0
  260. package/src/controllers/createChartController.ts +36 -12
  261. package/src/controllers/index.ts +5 -0
  262. package/src/controllers/types.ts +14 -0
  263. package/src/engine/__tests__/chart.dpr.test.ts +64 -0
  264. package/src/engine/__tests__/paneRenderer.resize.test.ts +0 -7
  265. package/src/engine/__tests__/renderSinglePath.test.ts +121 -0
  266. package/src/engine/chart.ts +172 -25
  267. package/src/engine/controller/__tests__/interaction.dpr.test.ts +45 -41
  268. package/src/engine/controller/interaction.ts +81 -30
  269. package/src/engine/data/__tests__/chartDataManager.incrementalLoad.test.ts +1 -0
  270. package/src/engine/data/incrementalLoadHint.ts +6 -1
  271. package/src/engine/drawing/DrawingState.ts +107 -89
  272. package/src/engine/drawing/__tests__/drawingState.workCopy.test.ts +95 -36
  273. package/src/engine/drawing/__tests__/drawingStore.projection.test.ts +22 -0
  274. package/src/engine/drawing/index.ts +12 -4
  275. package/src/engine/drawing/interaction.ts +25 -40
  276. package/src/engine/indicators/chartIndicatorManager.ts +6 -16
  277. package/src/engine/layout/chartPaneLayout.ts +6 -11
  278. package/src/engine/layout/pane.ts +0 -44
  279. package/src/engine/paneRenderer.ts +3 -34
  280. package/src/engine/render/chartRenderer.ts +181 -81
  281. package/src/engine/renderers/Indicator/atr.ts +5 -17
  282. package/src/engine/renderers/Indicator/boll.ts +15 -26
  283. package/src/engine/renderers/Indicator/cci.ts +7 -17
  284. package/src/engine/renderers/Indicator/chaikinVol.ts +3 -15
  285. package/src/engine/renderers/Indicator/cmf.ts +3 -15
  286. package/src/engine/renderers/Indicator/dema.ts +3 -15
  287. package/src/engine/renderers/Indicator/donchian.ts +3 -12
  288. package/src/engine/renderers/Indicator/ene.ts +15 -25
  289. package/src/engine/renderers/Indicator/expma.ts +4 -13
  290. package/src/engine/renderers/Indicator/fastk.ts +7 -17
  291. package/src/engine/renderers/Indicator/fib.ts +3 -12
  292. package/src/engine/renderers/Indicator/hma.ts +3 -15
  293. package/src/engine/renderers/Indicator/hv.ts +3 -15
  294. package/src/engine/renderers/Indicator/ichimoku.ts +21 -24
  295. package/src/engine/renderers/Indicator/kama.ts +3 -15
  296. package/src/engine/renderers/Indicator/keltner.ts +3 -12
  297. package/src/engine/renderers/Indicator/kst.ts +9 -20
  298. package/src/engine/renderers/Indicator/ma.ts +9 -21
  299. package/src/engine/renderers/Indicator/macd.ts +27 -102
  300. package/src/engine/renderers/Indicator/mfi.ts +3 -15
  301. package/src/engine/renderers/Indicator/mom.ts +7 -17
  302. package/src/engine/renderers/Indicator/obv.ts +3 -15
  303. package/src/engine/renderers/Indicator/parkinson.ts +3 -15
  304. package/src/engine/renderers/Indicator/pvt.ts +3 -15
  305. package/src/engine/renderers/Indicator/roc.ts +3 -15
  306. package/src/engine/renderers/Indicator/rsi.ts +12 -23
  307. package/src/engine/renderers/Indicator/shared/webglBand.ts +0 -13
  308. package/src/engine/renderers/Indicator/stoch.ts +9 -20
  309. package/src/engine/renderers/Indicator/tema.ts +3 -15
  310. package/src/engine/renderers/Indicator/trix.ts +3 -12
  311. package/src/engine/renderers/Indicator/vma.ts +3 -15
  312. package/src/engine/renderers/Indicator/vwap.ts +3 -15
  313. package/src/engine/renderers/Indicator/wma.ts +3 -15
  314. package/src/engine/renderers/Indicator/wmsr.ts +7 -17
  315. package/src/engine/renderers/__tests__/candle.sceneRenderer.test.ts +208 -0
  316. package/src/engine/renderers/__tests__/candleViaRenderer.test.ts +100 -0
  317. package/src/engine/renderers/__tests__/linesViaRenderer.test.ts +263 -0
  318. package/src/engine/renderers/__tests__/rectsViaRenderer.test.ts +87 -0
  319. package/src/engine/renderers/candle.ts +26 -69
  320. package/src/engine/renderers/candleViaRenderer.ts +38 -0
  321. package/src/engine/renderers/linesViaRenderer.ts +226 -0
  322. package/src/engine/renderers/rectsViaRenderer.ts +83 -0
  323. package/src/engine/renderers/subVolume.ts +9 -61
  324. package/src/engine/renderers/timeAxis.ts +1 -1
  325. package/src/engine/state/__tests__/effectiveTheme.test.ts +49 -0
  326. package/src/engine/state/__tests__/rendererState.test.ts +36 -0
  327. package/src/engine/state/__tests__/settingsState.test.ts +1 -1
  328. package/src/engine/state/chartStateKernel.ts +38 -17
  329. package/src/engine/state/index.ts +6 -1
  330. package/src/engine/state/interactionState.ts +73 -102
  331. package/src/engine/state/rendererState.ts +32 -0
  332. package/src/engine/state/themeState.ts +15 -8
  333. package/src/engine/state/viewportState.ts +6 -3
  334. package/src/engine/subPaneManager.ts +2 -16
  335. package/src/foundation/config/__tests__/chartSettings.test.ts +25 -0
  336. package/src/foundation/config/chartSettings.ts +24 -7
  337. package/src/foundation/plugin/index.ts +0 -1
  338. package/src/foundation/plugin/rendererPluginManager.ts +0 -99
  339. package/src/foundation/plugin/types.ts +11 -18
  340. package/src/foundation/reactivity/__tests__/frameTransaction.test.ts +306 -0
  341. package/src/foundation/reactivity/frameTransaction.ts +239 -0
  342. package/src/foundation/reactivity/index.ts +8 -0
  343. package/src/foundation/reactivity/signal.ts +39 -0
  344. package/src/foundation/tokens/__tests__/__snapshots__/baseline.test.ts.snap +2 -2
  345. package/src/foundation/tokens/theme-dark.ts +2 -1
  346. package/src/foundation/tokens/theme-light.ts +2 -1
  347. package/src/rendering/render/Renderer.ts +27 -14
  348. package/src/rendering/render/SurfaceBackend.ts +5 -7
  349. package/src/rendering/render/__tests__/canvas2DRenderer.test.ts +34 -0
  350. package/src/rendering/render/__tests__/contract.test.ts +2 -2
  351. package/src/rendering/render/__tests__/frameMetrics.test.ts +24 -0
  352. package/src/rendering/render/__tests__/rendererHost.test.ts +157 -0
  353. package/src/rendering/render/__tests__/webglRenderer.fallback.test.ts +14 -74
  354. package/src/rendering/render/__tests__/webglRenderer.test.ts +64 -6
  355. package/src/rendering/render/__tests__/webgpuRenderer.test.ts +370 -0
  356. package/src/rendering/render/__tests__/webgpuResourceTable.test.ts +62 -0
  357. package/src/rendering/render/__tests__/webgpuSurfaceBackend.test.ts +120 -0
  358. package/src/rendering/render/createCanvas2DRenderer.ts +83 -0
  359. package/src/rendering/render/createDefaultRendererHost.ts +74 -0
  360. package/src/rendering/render/createWebGLRenderer.ts +41 -87
  361. package/src/rendering/render/createWebGPURenderer.ts +677 -0
  362. package/src/rendering/render/createWebGPUSurfaceBackend.ts +106 -0
  363. package/src/rendering/render/frameMetrics.ts +54 -0
  364. package/src/rendering/render/index.ts +30 -0
  365. package/src/rendering/render/rendererHost.ts +210 -0
  366. package/src/rendering/render/webgpuResourceTable.ts +87 -0
  367. package/src/rendering/scene/__tests__/layerFromPlugin.test.ts +29 -2
  368. package/src/rendering/scene/__tests__/retainedScene.test.ts +59 -0
  369. package/src/rendering/scene/createLayerFromPlugin.ts +9 -2
  370. package/src/rendering/scene/layerRegistry.ts +4 -6
  371. package/src/rendering/scene/retainedScene.ts +101 -0
  372. package/src/version.ts +1 -1
@@ -2,12 +2,9 @@
2
2
  * High-level renderer interface — the drawing primitives the chart's
3
3
  * scene layers (candles, volume, indicators, drawings) call into.
4
4
  *
5
- * Both the existing WebGL renderer (currently spread across
6
- * `src/core/renderers/{candle,subVolume,crosshair,...}.ts`) and the
7
- * P1 WebGPU renderer implement this contract. v0's WebGL path will
8
- * be refactored to fit behind it in a follow-up PR (this file ships
9
- * the contract first; the legacy code is unchanged until the next
10
- * step is approved).
5
+ * WebGL (`createWebGLRenderer`) implements this contract today; P1 WebGPU
6
+ * will implement the same surface. Call sites only use sceneRenderer
7
+ * (fail-closed boolean Canvas2D), never dual-path surfaces.
11
8
  *
12
9
  * Design notes:
13
10
  * - `drawInstances` is the workhorse: render N copies of a unit geometry
@@ -22,10 +19,6 @@
22
19
  * - `setUniforms` is intentionally schemaless on the interface — the
23
20
  * uniform layout is owned by each shader/program. Callers pass a
24
21
  * structured object that the backend translates into uniform writes.
25
- *
26
- * This file is **pure interface** in this PR; behaviour comes in the
27
- * next two PRs (Scale interfaces, then a Renderer impl that wraps
28
- * the existing WebGL primitives).
29
22
  */
30
23
 
31
24
  import type { SurfaceBackend, SurfaceRegion } from './SurfaceBackend'
@@ -65,12 +58,24 @@ export type DrawInstancesParams = {
65
58
  uniforms?: Record<string, unknown>
66
59
  }
67
60
 
61
+ export type DrawLineStrip = {
62
+ points: ReadonlyArray<{ x: number; y: number }>
63
+ color: string
64
+ width?: number
65
+ }
66
+
68
67
  export type DrawLinesParams = {
69
68
  pipeline: PipelineHandle
70
- vertices: BufferHandle
71
- vertexCount: number
69
+ /**
70
+ * 单 strip 路径:vertices 为交错 x,y Float32。
71
+ * 若提供 strips,则忽略 vertices/vertexCount,一次 GPU 提交多条(避免 MSAA 逐条 clear)。
72
+ */
73
+ vertices?: BufferHandle
74
+ vertexCount?: number
72
75
  /** Line strip vs disconnected segments. */
73
76
  topology?: 'strip' | 'list'
77
+ /** 多色折线批量(推荐 MA 等多周期) */
78
+ strips?: ReadonlyArray<DrawLineStrip>
74
79
  uniforms?: Record<string, unknown>
75
80
  }
76
81
 
@@ -117,8 +122,16 @@ export interface Renderer {
117
122
  // --- frame ---
118
123
 
119
124
  beginFrame(region: SurfaceRegion): void
120
- drawInstances(params: DrawInstancesParams): void
121
- drawLines(params: DrawLinesParams): void
125
+ /**
126
+ * 返回 true 表示本批已成功提交 GPU(或 instanceCount<=0 无需绘制)。
127
+ * 返回 false 表示未画上(无 surface / pipeline 不匹配 / 资源缺失)——调用方应 fail-closed 走 2D。
128
+ */
129
+ drawInstances(params: DrawInstancesParams): boolean
130
+ /**
131
+ * 返回 true 表示本批折线/填充已成功提交 GPU。
132
+ * 返回 false 表示未画上——调用方应 fail-closed 走 2D。
133
+ */
134
+ drawLines(params: DrawLinesParams): boolean
122
135
  /** WebGPU only — WebGL implementations MUST throw. Caller checks caps first. */
123
136
  dispatchCompute(params: DispatchComputeParams): void
124
137
  endFrame(): void
@@ -2,22 +2,20 @@
2
2
  * Low-level GPU surface backend.
3
3
  *
4
4
  * Abstracts canvas + context lifecycle, viewport / scissor regions, clear,
5
- * compositing into a 2D canvas overlay, and teardown. Both the existing
6
- * WebGL surface (`src/core/renderers/webgl/sharedWebGLSurface.ts`) and the
7
- * P1 WebGPU surface implement this contract.
5
+ * compositing into a 2D canvas overlay, and teardown. WebGL
6
+ * (`createWebGLSurfaceBackend` over SharedWebGLSurface) implements this today;
7
+ * P1 WebGPU will implement the same contract.
8
8
  *
9
9
  * This file is **pure interface** — no implementation. It exists so that:
10
10
  *
11
- * 1. Existing WebGL code can declare conformance via a one-line adapter
12
- * (see `createLegacyWebGLBackend` below) without changing any behaviour.
11
+ * 1. WebGL can adapt SharedWebGLSurface behind a stable contract.
13
12
  * 2. P1's WebGPU implementation has a fixed target to write against.
14
13
  * 3. The higher-level `Renderer` (`./Renderer.ts`) can compose a backend
15
14
  * without knowing which GPU API it sits on.
16
15
  *
17
16
  * Design notes:
18
17
  * - All region coordinates are in **logical pixels** with the surface
19
- * responsible for DPR scaling internally. This matches the existing
20
- * `WebGLRegion` semantics so the legacy adapter is a transparent pass-through.
18
+ * responsible for DPR scaling internally (same semantics as WebGLRegion).
21
19
  * - `compositeTo` takes a 2D `CanvasRenderingContext2D` because the chart's
22
20
  * final composition target is a 2D overlay canvas — WebGL/WebGPU pixels
23
21
  * are drawn into the overlay via `drawImage`. Future paths that bypass
@@ -0,0 +1,34 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { createCanvas2DRenderer } from '../createCanvas2DRenderer'
4
+
5
+ describe('createCanvas2DRenderer', () => {
6
+ it('fails closed so business renderers use Canvas2D', () => {
7
+ const renderer = createCanvas2DRenderer()
8
+ const buffer = renderer.createBuffer('instance', 16)
9
+ const pipeline = renderer.createPipeline({ type: 'candle' })
10
+
11
+ expect(renderer.caps.name).toBe('canvas2d')
12
+ expect(renderer.surface.isAvailable()).toBe(false)
13
+ expect(
14
+ renderer.drawInstances({
15
+ pipeline,
16
+ vertices: buffer,
17
+ instances: buffer,
18
+ instanceCount: 1,
19
+ vertexCount: 6,
20
+ }),
21
+ ).toBe(false)
22
+ expect(renderer.drawLines({ pipeline, strips: [] })).toBe(false)
23
+ })
24
+
25
+ it('has an idempotent disposed lifecycle', () => {
26
+ const renderer = createCanvas2DRenderer()
27
+
28
+ renderer.dispose()
29
+ renderer.dispose()
30
+
31
+ expect(() => renderer.createBuffer('vertex', 8)).toThrow('Renderer is disposed')
32
+ expect(() => renderer.createComputePipeline({})).toThrow('compute not supported')
33
+ })
34
+ })
@@ -109,8 +109,8 @@ function makeMockRenderer(surface: SurfaceBackend, computeSupported: boolean): R
109
109
  },
110
110
  destroyComputePipeline: () => {},
111
111
  beginFrame: () => {},
112
- drawInstances: () => {},
113
- drawLines: () => {},
112
+ drawInstances: () => true,
113
+ drawLines: () => true,
114
114
  dispatchCompute: () => {
115
115
  if (!computeSupported) {
116
116
  throw new Error('dispatchCompute requires a backend with caps.compute === true')
@@ -0,0 +1,24 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { createFrameMetrics, getFrameMetrics, resetFrameMetrics } from '../frameMetrics'
4
+
5
+ describe('frameMetrics', () => {
6
+ it('counts submits uploads and buffer creates per frame', () => {
7
+ resetFrameMetrics()
8
+ const m = createFrameMetrics()
9
+ m.beginFrame()
10
+ m.recordBufferCreate()
11
+ m.recordUpload(64)
12
+ m.recordDraw()
13
+ m.recordSubmit()
14
+ m.recordComposite()
15
+ m.endFrame()
16
+ expect(getFrameMetrics()).toMatchObject({
17
+ bufferCreateCount: 1,
18
+ bufferUploadBytes: 64,
19
+ drawCallCount: 1,
20
+ queueSubmitCount: 1,
21
+ compositeCount: 1,
22
+ })
23
+ })
24
+ })
@@ -0,0 +1,157 @@
1
+ import { describe, expect, it, vi } from 'vitest'
2
+
3
+ import type { Renderer } from '../Renderer'
4
+ import { createCanvas2DRenderer } from '../createCanvas2DRenderer'
5
+ import { createRendererHost, type RendererBackend } from '../rendererHost'
6
+
7
+ function deferred<T>() {
8
+ let resolve!: (value: T) => void
9
+ const promise = new Promise<T>((done) => {
10
+ resolve = done
11
+ })
12
+ return { promise, resolve }
13
+ }
14
+
15
+ function makeRenderer(backend: RendererBackend): Renderer {
16
+ const renderer = createCanvas2DRenderer()
17
+ return {
18
+ ...renderer,
19
+ caps: { ...renderer.caps, name: backend === 'webgl' ? 'webgl2' : backend },
20
+ }
21
+ }
22
+
23
+ describe('RendererHost', () => {
24
+ it('degrades WebGPU to WebGL without changing the requested preference', async () => {
25
+ const webgl = makeRenderer('webgl')
26
+ const onRuntimeChange = vi.fn()
27
+ const host = await createRendererHost('webgpu', {
28
+ createWebGPU: async () => {
29
+ throw new Error('adapter unavailable')
30
+ },
31
+ createWebGL: () => webgl,
32
+ createCanvas: createCanvas2DRenderer,
33
+ onRuntimeChange,
34
+ })
35
+
36
+ expect(host.renderer).toBe(webgl)
37
+ expect(host.runtime).toEqual({
38
+ effective: 'webgl',
39
+ status: 'degraded',
40
+ error: 'adapter unavailable',
41
+ })
42
+ expect(onRuntimeChange).toHaveBeenLastCalledWith(host.runtime)
43
+ })
44
+
45
+ it('degrades WebGL to Canvas when WebGL is unavailable', async () => {
46
+ const host = await createRendererHost('webgl', {
47
+ createWebGPU: () => makeRenderer('webgpu'),
48
+ createWebGL: () => {
49
+ throw new Error('WebGL2 unavailable')
50
+ },
51
+ createCanvas: createCanvas2DRenderer,
52
+ })
53
+
54
+ expect(host.renderer.caps.name).toBe('canvas2d')
55
+ expect(host.runtime).toEqual({
56
+ effective: 'canvas',
57
+ status: 'degraded',
58
+ error: 'WebGL2 unavailable',
59
+ })
60
+ })
61
+
62
+ it('keeps the active renderer while a replacement is prepared', async () => {
63
+ const pending = deferred<Renderer>()
64
+ const webgl = makeRenderer('webgl')
65
+ const webgpu = makeRenderer('webgpu')
66
+ const host = await createRendererHost('webgl', {
67
+ createWebGPU: () => pending.promise,
68
+ createWebGL: () => webgl,
69
+ createCanvas: createCanvas2DRenderer,
70
+ })
71
+
72
+ const switching = host.switchTo('webgpu')
73
+
74
+ expect(host.renderer).toBe(webgl)
75
+ expect(host.runtime.status).toBe('switching')
76
+
77
+ pending.resolve(webgpu)
78
+ await switching
79
+
80
+ expect(host.renderer).toBe(webgpu)
81
+ expect(host.runtime).toEqual({ effective: 'webgpu', status: 'ready', error: null })
82
+ })
83
+
84
+ it('preserves the surface size when switching renderers', async () => {
85
+ const webgl = makeRenderer('webgl')
86
+ const webgpu = makeRenderer('webgpu')
87
+ const resizeWebGL = vi.spyOn(webgl.surface, 'resize')
88
+ const resizeWebGPU = vi.spyOn(webgpu.surface, 'resize')
89
+ const host = await createRendererHost('webgl', {
90
+ createWebGPU: () => webgpu,
91
+ createWebGL: () => webgl,
92
+ createCanvas: createCanvas2DRenderer,
93
+ })
94
+
95
+ host.resize(960, 540, 1.5)
96
+ await host.switchTo('webgpu')
97
+
98
+ expect(resizeWebGL).toHaveBeenCalledWith(960, 540, 1.5)
99
+ expect(resizeWebGPU).toHaveBeenCalledWith(960, 540, 1.5)
100
+ })
101
+
102
+ it('disposes a stale renderer created by an older switch', async () => {
103
+ const pending = deferred<Renderer>()
104
+ const stale = makeRenderer('webgpu')
105
+ const staleDispose = vi.spyOn(stale, 'dispose')
106
+ const host = await createRendererHost('canvas', {
107
+ createWebGPU: () => pending.promise,
108
+ createWebGL: () => makeRenderer('webgl'),
109
+ createCanvas: createCanvas2DRenderer,
110
+ })
111
+
112
+ const oldSwitch = host.switchTo('webgpu')
113
+ await host.switchTo('canvas')
114
+ pending.resolve(stale)
115
+ await oldSwitch
116
+
117
+ expect(staleDispose).toHaveBeenCalledOnce()
118
+ expect(host.runtime.effective).toBe('canvas')
119
+ })
120
+
121
+ it('disposes a renderer that arrives after the host is disposed', async () => {
122
+ const pending = deferred<Renderer>()
123
+ const late = makeRenderer('webgpu')
124
+ const lateDispose = vi.spyOn(late, 'dispose')
125
+ const host = await createRendererHost('canvas', {
126
+ createWebGPU: () => pending.promise,
127
+ createWebGL: () => makeRenderer('webgl'),
128
+ createCanvas: createCanvas2DRenderer,
129
+ })
130
+
131
+ const switching = host.switchTo('webgpu')
132
+ host.dispose()
133
+ pending.resolve(late)
134
+ await switching
135
+
136
+ expect(lateDispose).toHaveBeenCalledOnce()
137
+ })
138
+
139
+ it('degrades the active WebGPU renderer after device loss', async () => {
140
+ const webgpu = makeRenderer('webgpu')
141
+ const webgl = makeRenderer('webgl')
142
+ const host = await createRendererHost('webgpu', {
143
+ createWebGPU: () => webgpu,
144
+ createWebGL: () => webgl,
145
+ createCanvas: createCanvas2DRenderer,
146
+ })
147
+
148
+ await host.handleDeviceLost('device reset')
149
+
150
+ expect(host.renderer).toBe(webgl)
151
+ expect(host.runtime).toEqual({
152
+ effective: 'webgl',
153
+ status: 'degraded',
154
+ error: 'device reset',
155
+ })
156
+ })
157
+ })
@@ -54,50 +54,16 @@ function createMockSharedWebGLSurface() {
54
54
  }
55
55
  }
56
56
 
57
- function makeMockFallbackCtx() {
58
- return {
59
- beginPath: vi.fn(),
60
- moveTo: vi.fn(),
61
- lineTo: vi.fn(),
62
- closePath: vi.fn(),
63
- fill: vi.fn(),
64
- stroke: vi.fn(),
65
- fillStyle: '',
66
- strokeStyle: '',
67
- lineWidth: 1,
68
- fillRect: vi.fn(),
69
- }
70
- }
71
-
72
57
  beforeEach(() => {
73
58
  vi.clearAllMocks()
74
59
  })
75
60
 
76
- describe('Canvas2D fallback', () => {
77
- it('setFallbackContext stores the context and dpr', () => {
78
- const surface = createMockSurfaceBackend()
79
- const glSurface = createMockSharedWebGLSurface()
80
- const renderer = createWebGLRenderer(surface, glSurface as any)
81
- const ctx = makeMockFallbackCtx()
82
-
83
- expect(() => (renderer as any).setFallbackContext(ctx, 2)).not.toThrow()
84
- })
85
-
86
- it('setFallbackContext with null clears the fallback', () => {
87
- const surface = createMockSurfaceBackend()
88
- const glSurface = createMockSharedWebGLSurface()
89
- const renderer = createWebGLRenderer(surface, glSurface as any)
90
-
91
- expect(() => (renderer as any).setFallbackContext(null, 1)).not.toThrow()
92
- })
93
-
61
+ describe('fail-closed when GPU surfaces unavailable', () => {
94
62
  describe('drawLines — line type', () => {
95
- it('draws a polyline on the fallback context when WebGL is unavailable', () => {
63
+ it('returns false when lineSurface unavailable', () => {
96
64
  const surface = createMockSurfaceBackend()
97
65
  const glSurface = createMockSharedWebGLSurface()
98
66
  const renderer = createWebGLRenderer(surface, glSurface as any)
99
- const ctx = makeMockFallbackCtx()
100
- ;(renderer as any).setFallbackContext(ctx, 1)
101
67
 
102
68
  renderer.beginFrame({ x: 0, y: 0, width: 100, height: 100, dpr: 1 })
103
69
 
@@ -106,23 +72,17 @@ describe('Canvas2D fallback', () => {
106
72
  const verts = new Float32Array([10, 20, 30, 40, 50, 60])
107
73
  renderer.writeBuffer(vertexBuf, verts)
108
74
 
109
- renderer.drawLines({
75
+ const ok = renderer.drawLines({
110
76
  pipeline,
111
77
  vertices: vertexBuf,
112
78
  vertexCount: 3,
113
79
  uniforms: { color: '#00ff00', scrollLeft: 5, lineWidth: 2 },
114
80
  })
115
81
 
116
- expect(ctx.beginPath).toHaveBeenCalledTimes(1)
117
- expect(ctx.moveTo).toHaveBeenCalledWith(5, 20)
118
- expect(ctx.lineTo).toHaveBeenCalledWith(25, 40)
119
- expect(ctx.lineTo).toHaveBeenCalledWith(45, 60)
120
- expect(ctx.strokeStyle).toBe('#00ff00')
121
- expect(ctx.lineWidth).toBe(2)
122
- expect(ctx.stroke).toHaveBeenCalledTimes(1)
82
+ expect(ok).toBe(false)
123
83
  })
124
84
 
125
- it('no-ops on drawLines with no fallback context', () => {
85
+ it('returns false on drawLines with no lineSurface', () => {
126
86
  const surface = createMockSurfaceBackend()
127
87
  const glSurface = createMockSharedWebGLSurface()
128
88
  const renderer = createWebGLRenderer(surface, glSurface as any)
@@ -132,19 +92,15 @@ describe('Canvas2D fallback', () => {
132
92
  const vertexBuf = renderer.createBuffer('vertex', 256)
133
93
  renderer.writeBuffer(vertexBuf, new Float32Array([0, 0, 100, 100]))
134
94
 
135
- expect(() =>
136
- renderer.drawLines({ pipeline, vertices: vertexBuf, vertexCount: 2 }),
137
- ).not.toThrow()
95
+ expect(renderer.drawLines({ pipeline, vertices: vertexBuf, vertexCount: 2 })).toBe(false)
138
96
  })
139
97
  })
140
98
 
141
99
  describe('drawLines — fill type', () => {
142
- it('draws a filled band on the fallback context', () => {
100
+ it('returns false when lineSurface unavailable', () => {
143
101
  const surface = createMockSurfaceBackend()
144
102
  const glSurface = createMockSharedWebGLSurface()
145
103
  const renderer = createWebGLRenderer(surface, glSurface as any)
146
- const ctx = makeMockFallbackCtx()
147
- ;(renderer as any).setFallbackContext(ctx, 1)
148
104
 
149
105
  renderer.beginFrame({ x: 0, y: 0, width: 100, height: 100, dpr: 1 })
150
106
 
@@ -153,33 +109,22 @@ describe('Canvas2D fallback', () => {
153
109
  const verts = new Float32Array([0, 100, 0, 50, 100, 100, 100, 50, 200, 100, 200, 50])
154
110
  renderer.writeBuffer(vertexBuf, verts)
155
111
 
156
- renderer.drawLines({
112
+ const ok = renderer.drawLines({
157
113
  pipeline,
158
114
  vertices: vertexBuf,
159
115
  vertexCount: 6,
160
116
  uniforms: { color: '#0000ff', scrollLeft: 5 },
161
117
  })
162
118
 
163
- expect(ctx.beginPath).toHaveBeenCalledTimes(1)
164
- expect(ctx.moveTo).toHaveBeenCalledWith(-5, 100)
165
- expect(ctx.lineTo).toHaveBeenCalledWith(95, 100)
166
- expect(ctx.lineTo).toHaveBeenCalledWith(195, 100)
167
- expect(ctx.lineTo).toHaveBeenCalledWith(195, 50)
168
- expect(ctx.lineTo).toHaveBeenCalledWith(95, 50)
169
- expect(ctx.lineTo).toHaveBeenCalledWith(-5, 50)
170
- expect(ctx.closePath).toHaveBeenCalledTimes(1)
171
- expect(ctx.fillStyle).toBe('#0000ff')
172
- expect(ctx.fill).toHaveBeenCalledTimes(1)
119
+ expect(ok).toBe(false)
173
120
  })
174
121
  })
175
122
 
176
123
  describe('drawInstances', () => {
177
- it('draws rectangles on the fallback context', () => {
124
+ it('returns false when candleSurface unavailable', () => {
178
125
  const surface = createMockSurfaceBackend()
179
126
  const glSurface = createMockSharedWebGLSurface()
180
127
  const renderer = createWebGLRenderer(surface, glSurface as any)
181
- const ctx = makeMockFallbackCtx()
182
- ;(renderer as any).setFallbackContext(ctx, 1)
183
128
 
184
129
  renderer.beginFrame({ x: 0, y: 0, width: 100, height: 100, dpr: 1 })
185
130
 
@@ -188,7 +133,7 @@ describe('Canvas2D fallback', () => {
188
133
  const rects = new Float32Array([10, 20, 30, 40, 60, 70, 20, 10])
189
134
  renderer.writeBuffer(instanceBuf, rects)
190
135
 
191
- renderer.drawInstances({
136
+ const ok = renderer.drawInstances({
192
137
  pipeline,
193
138
  vertices: instanceBuf,
194
139
  instances: instanceBuf,
@@ -197,20 +142,15 @@ describe('Canvas2D fallback', () => {
197
142
  uniforms: { color: '#ff0000', scrollLeft: 5 },
198
143
  })
199
144
 
200
- expect(ctx.fillRect).toHaveBeenCalledTimes(2)
201
- expect(ctx.fillRect).toHaveBeenNthCalledWith(1, 5, 20, 30, 40)
202
- expect(ctx.fillRect).toHaveBeenNthCalledWith(2, 55, 70, 20, 10)
203
- expect(ctx.fillStyle).toBe('#ff0000')
145
+ expect(ok).toBe(false)
204
146
  })
205
147
  })
206
148
 
207
149
  describe('dispose behaviour', () => {
208
- it('after dispose, fallback draw calls are no-ops', () => {
150
+ it('after dispose, draw calls are no-ops / false', () => {
209
151
  const surface = createMockSurfaceBackend()
210
152
  const glSurface = createMockSharedWebGLSurface()
211
153
  const renderer = createWebGLRenderer(surface, glSurface as any)
212
- const ctx = makeMockFallbackCtx()
213
- ;(renderer as any).setFallbackContext(ctx, 1)
214
154
 
215
155
  const pipeline = renderer.createPipeline({ type: 'line' })
216
156
  const vertexBuf = renderer.createBuffer('vertex', 256)
@@ -221,7 +161,7 @@ describe('Canvas2D fallback', () => {
221
161
  expect(() =>
222
162
  renderer.drawLines({ pipeline, vertices: vertexBuf, vertexCount: 2 }),
223
163
  ).not.toThrow()
224
- expect(ctx.stroke).not.toHaveBeenCalled()
164
+ expect(renderer.drawLines({ pipeline, vertices: vertexBuf, vertexCount: 2 })).toBe(false)
225
165
  })
226
166
  })
227
167
  })
@@ -198,7 +198,7 @@ describe('createWebGLRenderer', () => {
198
198
  renderer.writeBuffer(instanceBuf, rects)
199
199
 
200
200
  const vertices = renderer.createBuffer('vertex', 48)
201
- renderer.drawInstances({
201
+ const ok = renderer.drawInstances({
202
202
  pipeline,
203
203
  vertices,
204
204
  instances: instanceBuf,
@@ -207,6 +207,7 @@ describe('createWebGLRenderer', () => {
207
207
  uniforms: { color: '#ff0000', scrollLeft: 0 },
208
208
  })
209
209
 
210
+ expect(ok).toBe(true)
210
211
  expect(mocks.mockDrawRectBuffer).toHaveBeenCalledTimes(1)
211
212
  const args = mocks.mockDrawRectBuffer.mock.calls[0]
212
213
  expect(args[0]).toBeInstanceOf(Float32Array)
@@ -215,37 +216,56 @@ describe('createWebGLRenderer', () => {
215
216
  expect(args[3]).toBe(0)
216
217
  })
217
218
 
218
- it('no-ops when instanceCount is zero', () => {
219
+ it('returns true without GPU when instanceCount is zero', () => {
219
220
  const { renderer } = makeRenderer()
220
221
  renderer.beginFrame({ x: 0, y: 0, width: 100, height: 100, dpr: 1 })
221
222
  const pipeline = renderer.createPipeline({ type: 'candle' })
222
223
  const buf = renderer.createBuffer('instance', 64)
223
224
  renderer.writeBuffer(buf, new Float32Array([0, 0, 50, 50]))
224
- renderer.drawInstances({
225
+ const ok = renderer.drawInstances({
225
226
  pipeline,
226
227
  vertices: buf,
227
228
  instances: buf,
228
229
  instanceCount: 0,
229
230
  vertexCount: 6,
230
231
  })
232
+ expect(ok).toBe(true)
231
233
  expect(mocks.mockDrawRectBuffer).not.toHaveBeenCalled()
232
234
  })
233
235
 
234
- it('no-ops when pipeline type mismatch (line -> instances)', () => {
236
+ it('returns false when pipeline type mismatch (line -> instances)', () => {
235
237
  const { renderer } = makeRenderer()
236
238
  renderer.beginFrame({ x: 0, y: 0, width: 100, height: 100, dpr: 1 })
237
239
  const pipeline = renderer.createPipeline({ type: 'line' })
238
240
  const buf = renderer.createBuffer('instance', 64)
239
241
  renderer.writeBuffer(buf, new Float32Array([0, 0, 50, 50]))
240
- renderer.drawInstances({
242
+ const ok = renderer.drawInstances({
241
243
  pipeline,
242
244
  vertices: buf,
243
245
  instances: buf,
244
246
  instanceCount: 1,
245
247
  vertexCount: 6,
246
248
  })
249
+ expect(ok).toBe(false)
247
250
  expect(mocks.mockDrawRectBuffer).not.toHaveBeenCalled()
248
251
  })
252
+
253
+ it('returns false when drawRectBuffer fails', () => {
254
+ mocks.mockDrawRectBuffer.mockReturnValueOnce(false)
255
+ const { renderer } = makeRenderer()
256
+ renderer.beginFrame({ x: 0, y: 0, width: 100, height: 100, dpr: 1 })
257
+ const pipeline = renderer.createPipeline({ type: 'candle' })
258
+ const buf = renderer.createBuffer('instance', 64)
259
+ renderer.writeBuffer(buf, new Float32Array([0, 0, 50, 50]))
260
+ const ok = renderer.drawInstances({
261
+ pipeline,
262
+ vertices: buf,
263
+ instances: buf,
264
+ instanceCount: 1,
265
+ vertexCount: 6,
266
+ })
267
+ expect(ok).toBe(false)
268
+ })
249
269
  })
250
270
 
251
271
  describe('drawLines', () => {
@@ -258,7 +278,7 @@ describe('createWebGLRenderer', () => {
258
278
  const verts = new Float32Array([10, 20, 30, 40, 50, 60])
259
279
  renderer.writeBuffer(vertexBuf, verts)
260
280
 
261
- renderer.drawLines({
281
+ const ok = renderer.drawLines({
262
282
  pipeline,
263
283
  vertices: vertexBuf,
264
284
  vertexCount: 3,
@@ -266,6 +286,7 @@ describe('createWebGLRenderer', () => {
266
286
  uniforms: { color: '#00ff00', scrollLeft: 10, lineWidth: 1 },
267
287
  })
268
288
 
289
+ expect(ok).toBe(true)
269
290
  expect(mocks.mockDrawLineStrips).toHaveBeenCalledTimes(1)
270
291
  const args = mocks.mockDrawLineStrips.mock.calls[0]
271
292
  expect(args[0]).toHaveLength(1)
@@ -275,6 +296,43 @@ describe('createWebGLRenderer', () => {
275
296
  expect(args[1]).toBe(10)
276
297
  })
277
298
 
299
+ it('batches multiple strips in one drawLineStrips call', () => {
300
+ const { renderer } = makeRenderer()
301
+ renderer.beginFrame({ x: 0, y: 0, width: 800, height: 600, dpr: 1 })
302
+ const pipeline = renderer.createPipeline({ type: 'line' })
303
+ const ok = renderer.drawLines({
304
+ pipeline,
305
+ strips: [
306
+ {
307
+ points: [
308
+ { x: 0, y: 0 },
309
+ { x: 1, y: 1 },
310
+ ],
311
+ color: '#f00',
312
+ width: 1,
313
+ },
314
+ {
315
+ points: [
316
+ { x: 0, y: 2 },
317
+ { x: 1, y: 3 },
318
+ { x: 2, y: 2 },
319
+ ],
320
+ color: '#0f0',
321
+ width: 2,
322
+ },
323
+ ],
324
+ uniforms: { scrollLeft: 5 },
325
+ })
326
+ expect(ok).toBe(true)
327
+ expect(mocks.mockDrawLineStrips).toHaveBeenCalledTimes(1)
328
+ const lines = mocks.mockDrawLineStrips.mock.calls[0]![0]
329
+ expect(lines).toHaveLength(2)
330
+ expect(lines[0].color).toBe('#f00')
331
+ expect(lines[1].color).toBe('#0f0')
332
+ expect(lines[1].width).toBe(2)
333
+ expect(mocks.mockDrawLineStrips.mock.calls[0]![1]).toBe(5)
334
+ })
335
+
278
336
  it('delegates to line surface drawFilledBand for fill pipeline', () => {
279
337
  const { renderer } = makeRenderer()
280
338
  renderer.beginFrame({ x: 0, y: 0, width: 800, height: 600, dpr: 2 })