xbintsc 0.5.45 → 0.5.52

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 (286) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +5 -4
  3. package/README.zh-CN.md +4 -3
  4. package/dist/src/cli/extensions.d.ts +24 -0
  5. package/dist/src/cli/extensions.js +62 -0
  6. package/dist/src/cli/extensions.js.map +1 -0
  7. package/dist/src/cli/help.d.ts +1 -1
  8. package/dist/src/cli/help.js +3 -1
  9. package/dist/src/cli/help.js.map +1 -1
  10. package/dist/src/cli/hints.js +8 -3
  11. package/dist/src/cli/hints.js.map +1 -1
  12. package/dist/src/cli/main.js +21 -28
  13. package/dist/src/cli/main.js.map +1 -1
  14. package/dist/src/diagnostics/diagnostic.d.ts +3 -1
  15. package/dist/src/diagnostics/diagnostic.js +2 -0
  16. package/dist/src/diagnostics/diagnostic.js.map +1 -1
  17. package/dist/src/driver/build-key.d.ts +2 -0
  18. package/dist/src/driver/build-key.js +1 -0
  19. package/dist/src/driver/build-key.js.map +1 -1
  20. package/dist/src/driver/compiler.js +61 -8
  21. package/dist/src/driver/compiler.js.map +1 -1
  22. package/dist/src/driver/config.d.ts +4 -0
  23. package/dist/src/driver/config.js +2 -0
  24. package/dist/src/driver/config.js.map +1 -1
  25. package/dist/src/driver/link-features.d.ts +37 -0
  26. package/dist/src/driver/link-features.js +123 -0
  27. package/dist/src/driver/link-features.js.map +1 -0
  28. package/dist/src/driver/options.d.ts +10 -0
  29. package/dist/src/driver/runtime-objects.d.ts +5 -0
  30. package/dist/src/driver/runtime-objects.js +20 -1
  31. package/dist/src/driver/runtime-objects.js.map +1 -1
  32. package/dist/src/driver/usage.d.ts +23 -0
  33. package/dist/src/driver/usage.js +353 -0
  34. package/dist/src/driver/usage.js.map +1 -0
  35. package/dist/src/driver/version.d.ts +1 -1
  36. package/dist/src/driver/version.js +1 -1
  37. package/dist/src/extensions/catalog.js +1 -2
  38. package/dist/src/extensions/catalog.js.map +1 -1
  39. package/dist/src/extensions/gui/features.d.ts +34 -0
  40. package/dist/src/extensions/gui/features.js +165 -0
  41. package/dist/src/extensions/gui/features.js.map +1 -0
  42. package/dist/src/extensions/gui/index.js +10 -4
  43. package/dist/src/extensions/gui/index.js.map +1 -1
  44. package/dist/src/extensions/link-feature.d.ts +70 -0
  45. package/dist/src/extensions/link-feature.js +18 -0
  46. package/dist/src/extensions/link-feature.js.map +1 -0
  47. package/dist/src/extensions/registry.d.ts +24 -4
  48. package/dist/src/extensions/registry.js +26 -3
  49. package/dist/src/extensions/registry.js.map +1 -1
  50. package/dist/src/index.d.ts +3 -2
  51. package/dist/src/index.js +0 -1
  52. package/dist/src/index.js.map +1 -1
  53. package/dist/tests/cli/hints.test.js +5 -7
  54. package/dist/tests/cli/hints.test.js.map +1 -1
  55. package/dist/tests/driver/link-features.test.js +105 -0
  56. package/dist/tests/driver/link-features.test.js.map +1 -0
  57. package/dist/tests/driver/usage.test.d.ts +1 -0
  58. package/dist/tests/driver/usage.test.js +88 -0
  59. package/dist/tests/driver/usage.test.js.map +1 -0
  60. package/dist/tests/e2e/gui-audio.test.js +4 -4
  61. package/dist/tests/e2e/gui-audio.test.js.map +1 -1
  62. package/dist/tests/e2e/gui-css-coverage.test.d.ts +9 -0
  63. package/dist/tests/e2e/gui-css-coverage.test.js +333 -0
  64. package/dist/tests/e2e/gui-css-coverage.test.js.map +1 -0
  65. package/dist/tests/e2e/gui-css-features.test.d.ts +6 -0
  66. package/dist/tests/e2e/gui-css-features.test.js +270 -0
  67. package/dist/tests/e2e/gui-css-features.test.js.map +1 -0
  68. package/dist/tests/e2e/gui-effects.test.js +92 -11
  69. package/dist/tests/e2e/gui-effects.test.js.map +1 -1
  70. package/dist/tests/e2e/gui-feature-linking.test.d.ts +9 -0
  71. package/dist/tests/e2e/gui-feature-linking.test.js +68 -0
  72. package/dist/tests/e2e/gui-feature-linking.test.js.map +1 -0
  73. package/dist/tests/e2e/gui-font-face.test.d.ts +10 -0
  74. package/dist/tests/e2e/gui-font-face.test.js +142 -0
  75. package/dist/tests/e2e/gui-font-face.test.js.map +1 -0
  76. package/dist/tests/e2e/gui-fonts.test.d.ts +10 -0
  77. package/dist/tests/e2e/gui-fonts.test.js +150 -0
  78. package/dist/tests/e2e/gui-fonts.test.js.map +1 -0
  79. package/dist/tests/e2e/gui-helpers.d.ts +10 -1
  80. package/dist/tests/e2e/gui-helpers.js +6 -0
  81. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  82. package/dist/tests/e2e/gui-layout.test.js +7 -3
  83. package/dist/tests/e2e/gui-layout.test.js.map +1 -1
  84. package/dist/tests/e2e/gui-media-disabled.test.d.ts +5 -4
  85. package/dist/tests/e2e/gui-media-disabled.test.js +7 -6
  86. package/dist/tests/e2e/gui-media-disabled.test.js.map +1 -1
  87. package/dist/tests/e2e/gui-media.test.d.ts +5 -4
  88. package/dist/tests/e2e/gui-media.test.js +9 -9
  89. package/dist/tests/e2e/gui-media.test.js.map +1 -1
  90. package/dist/tests/e2e/gui-paint.test.js +5 -2
  91. package/dist/tests/e2e/gui-paint.test.js.map +1 -1
  92. package/dist/tests/e2e/gui-shaping.test.d.ts +5 -0
  93. package/dist/tests/e2e/gui-shaping.test.js +47 -0
  94. package/dist/tests/e2e/gui-shaping.test.js.map +1 -0
  95. package/dist/tests/e2e/gui-track-sizing.test.d.ts +6 -0
  96. package/dist/tests/e2e/gui-track-sizing.test.js +173 -0
  97. package/dist/tests/e2e/gui-track-sizing.test.js.map +1 -0
  98. package/dist/tests/e2e/gui-transform.test.js +3 -2
  99. package/dist/tests/e2e/gui-transform.test.js.map +1 -1
  100. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.d.ts +18 -0
  101. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js +120 -0
  102. package/dist/tests/e2e/gui-webgl-pipeline-cache.test.js.map +1 -0
  103. package/dist/tests/extensions/gui-features.test.d.ts +1 -0
  104. package/dist/tests/extensions/gui-features.test.js +175 -0
  105. package/dist/tests/extensions/gui-features.test.js.map +1 -0
  106. package/doc/ai/cli.md +5 -1
  107. package/doc/ai/extensions.md +56 -26
  108. package/doc/ai/troubleshooting.md +10 -7
  109. package/doc/ai/zh-CN/cli.md +5 -1
  110. package/doc/ai/zh-CN/extensions.md +48 -22
  111. package/doc/ai/zh-CN/troubleshooting.md +6 -5
  112. package/doc/gui/animation.md +39 -9
  113. package/doc/gui/api.md +2 -1
  114. package/doc/gui/build.md +137 -19
  115. package/doc/gui/canvas.md +1 -1
  116. package/doc/gui/dom.md +20 -4
  117. package/doc/gui/known-issues.md +63 -26
  118. package/doc/gui/layout.md +31 -8
  119. package/doc/gui/media.md +13 -7
  120. package/doc/gui/rendering.md +114 -39
  121. package/doc/gui/runtime.md +5 -0
  122. package/doc/gui/webgl.md +24 -6
  123. package/doc/gui.md +110 -47
  124. package/doc/implemented/cli.md +2 -0
  125. package/doc/implemented/extensions.md +9 -3
  126. package/doc/npm-package.md +4 -4
  127. package/doc/xbintsc.config.schema.json +10 -0
  128. package/doc/zh-CN/gui/animation.md +33 -10
  129. package/doc/zh-CN/gui/api.md +2 -1
  130. package/doc/zh-CN/gui/build.md +110 -16
  131. package/doc/zh-CN/gui/canvas.md +1 -1
  132. package/doc/zh-CN/gui/dom.md +16 -3
  133. package/doc/zh-CN/gui/known-issues.md +45 -15
  134. package/doc/zh-CN/gui/layout.md +23 -6
  135. package/doc/zh-CN/gui/media.md +11 -6
  136. package/doc/zh-CN/gui/rendering.md +84 -26
  137. package/doc/zh-CN/gui/runtime.md +3 -0
  138. package/doc/zh-CN/gui/webgl.md +3 -3
  139. package/doc/zh-CN/gui.md +92 -41
  140. package/doc/zh-CN/implemented/cli.md +2 -0
  141. package/doc/zh-CN/implemented/extensions.md +9 -3
  142. package/doc/zh-CN/npm-package.md +4 -3
  143. package/package.json +7 -7
  144. package/runtime/ext_gui/animation_values.cpp +425 -0
  145. package/runtime/ext_gui/animation_values.h +64 -0
  146. package/runtime/ext_gui/browser.cpp +22 -15
  147. package/runtime/ext_gui/browser_loop.cpp +13 -1
  148. package/runtime/ext_gui/canvas.h +1 -1
  149. package/runtime/ext_gui/canvas_raster.cpp +1 -1
  150. package/runtime/ext_gui/canvas_text.cpp +1 -1
  151. package/runtime/ext_gui/css.cpp +172 -21
  152. package/runtime/ext_gui/css.h +75 -4
  153. package/runtime/ext_gui/css_match.cpp +4 -0
  154. package/runtime/ext_gui/css_sheet.cpp +218 -2
  155. package/runtime/ext_gui/document.cpp +42 -192
  156. package/runtime/ext_gui/document.h +25 -12
  157. package/runtime/ext_gui/document_animation.cpp +428 -0
  158. package/runtime/ext_gui/dom_api_canvas.cpp +15 -8
  159. package/runtime/ext_gui/dom_api_canvas_extra.cpp +44 -20
  160. package/runtime/ext_gui/dom_api_canvas_image.cpp +3 -1
  161. package/runtime/ext_gui/dom_api_node_read.cpp +10 -0
  162. package/runtime/ext_gui/dom_api_offscreen.cpp +3 -1
  163. package/runtime/ext_gui/dom_api_style.cpp +7 -0
  164. package/runtime/ext_gui/dom_api_webgl_internal.h +4 -0
  165. package/runtime/ext_gui/dom_api_webgl_source.cpp +5 -2
  166. package/runtime/ext_gui/effect_values.cpp +245 -21
  167. package/runtime/ext_gui/gpu_bridge.c +28 -0
  168. package/runtime/ext_gui/gpu_feature.cpp +30 -0
  169. package/runtime/ext_gui/gpu_shader.cpp +22 -0
  170. package/runtime/ext_gui/gpu_shader_api.h +50 -0
  171. package/runtime/ext_gui/gradient_values.cpp +24 -0
  172. package/runtime/ext_gui/grid_values.cpp +23 -0
  173. package/runtime/ext_gui/gui.cpp +6 -37
  174. package/runtime/ext_gui/gui_canvas.cpp +14 -6
  175. package/runtime/ext_gui/gui_cursor.cpp +50 -0
  176. package/runtime/ext_gui/gui_engine.h +1 -18
  177. package/runtime/ext_gui/gui_events.cpp +19 -6
  178. package/runtime/ext_gui/gui_internal.h +3 -0
  179. package/runtime/ext_gui/gui_webgl.cpp +25 -10
  180. package/runtime/ext_gui/image_api.cpp +18 -0
  181. package/runtime/ext_gui/image_api.h +43 -0
  182. package/runtime/ext_gui/image_bitmap_browser.cpp +20 -11
  183. package/runtime/ext_gui/image_bridge.c +22 -0
  184. package/runtime/ext_gui/image_feature.cpp +107 -0
  185. package/runtime/ext_gui/inline_svg.cpp +95 -8
  186. package/runtime/ext_gui/inline_svg.h +5 -1
  187. package/runtime/ext_gui/layout.cpp +15 -9
  188. package/runtime/ext_gui/layout.h +8 -2
  189. package/runtime/ext_gui/layout_flex.cpp +134 -29
  190. package/runtime/ext_gui/layout_flow.cpp +58 -7
  191. package/runtime/ext_gui/layout_flow_internal.h +39 -4
  192. package/runtime/ext_gui/layout_grid.cpp +31 -0
  193. package/runtime/ext_gui/layout_inline.cpp +26 -2
  194. package/runtime/ext_gui/layout_vertical.cpp +196 -0
  195. package/runtime/ext_gui/media.cpp +4 -2
  196. package/runtime/ext_gui/media.h +3 -3
  197. package/runtime/ext_gui/media_api.h +7 -6
  198. package/runtime/ext_gui/paint.cpp +67 -9
  199. package/runtime/ext_gui/paint.h +61 -8
  200. package/runtime/ext_gui/paint_backdrop.cpp +211 -0
  201. package/runtime/ext_gui/paint_background.cpp +175 -97
  202. package/runtime/ext_gui/paint_effects.cpp +12 -6
  203. package/runtime/ext_gui/paint_internal.h +27 -0
  204. package/runtime/ext_gui/paint_outline.cpp +44 -0
  205. package/runtime/ext_gui/paint_shadow.cpp +368 -0
  206. package/runtime/ext_gui/renderer.cpp +15 -7
  207. package/runtime/ext_gui/renderer.h +11 -2
  208. package/runtime/ext_gui/renderer_backdrop.cpp +374 -0
  209. package/runtime/ext_gui/renderer_bitmap.cpp +82 -0
  210. package/runtime/ext_gui/renderer_gradient.cpp +51 -17
  211. package/runtime/ext_gui/renderer_image.cpp +19 -7
  212. package/runtime/ext_gui/renderer_internal.h +21 -0
  213. package/runtime/ext_gui/renderer_layer.cpp +37 -6
  214. package/runtime/ext_gui/renderer_text.cpp +2 -1
  215. package/runtime/ext_gui/stb_truetype.h +5077 -0
  216. package/runtime/ext_gui/style.cpp +62 -8
  217. package/runtime/ext_gui/style.h +111 -4
  218. package/runtime/ext_gui/style_cascade.cpp +83 -0
  219. package/runtime/ext_gui/style_internal.h +13 -1
  220. package/runtime/ext_gui/style_length.cpp +92 -0
  221. package/runtime/ext_gui/style_print.cpp +83 -2
  222. package/runtime/ext_gui/style_svg.cpp +40 -0
  223. package/runtime/ext_gui/style_text.cpp +302 -0
  224. package/runtime/ext_gui/style_ui.cpp +150 -0
  225. package/runtime/ext_gui/style_values.cpp +76 -62
  226. package/runtime/ext_gui/style_values_internal.h +23 -0
  227. package/runtime/ext_gui/text.cpp +27 -499
  228. package/runtime/ext_gui/text.h +18 -9
  229. package/runtime/ext_gui/text_api.h +45 -0
  230. package/runtime/ext_gui/text_bridge.c +23 -0
  231. package/runtime/ext_gui/text_cache.cpp +12 -3
  232. package/runtime/ext_gui/text_font_db.cpp +427 -0
  233. package/runtime/ext_gui/text_font_paths.cpp +250 -0
  234. package/runtime/ext_gui/text_font_paths.h +39 -0
  235. package/runtime/ext_gui/text_font_sfnt.cpp +396 -0
  236. package/runtime/ext_gui/text_font_sfnt.h +44 -0
  237. package/runtime/ext_gui/text_fonts.cpp +348 -0
  238. package/runtime/ext_gui/text_fonts.h +78 -0
  239. package/runtime/ext_gui/text_fonts_internal.h +77 -0
  240. package/runtime/ext_gui/text_glyph_cache.cpp +2 -2
  241. package/runtime/ext_gui/text_hb.cpp +438 -0
  242. package/runtime/ext_gui/text_stb.cpp +369 -0
  243. package/runtime/ext_gui/transform.cpp +107 -9
  244. package/runtime/ext_gui/transform.h +5 -0
  245. package/runtime/ext_gui/webgl_api.cpp +19 -0
  246. package/runtime/ext_gui/webgl_api.h +53 -0
  247. package/runtime/ext_gui/webgl_bridge.c +25 -0
  248. package/runtime/ext_gui/webgl_draw_setup.cpp +42 -1
  249. package/runtime/ext_gui/webgl_feature.cpp +82 -0
  250. package/runtime/ext_gui/webgl_feature.h +28 -0
  251. package/runtime/ext_gui/webgl_msl.cpp +41 -0
  252. package/runtime/ext_gui/webgl_msl.h +6 -0
  253. package/runtime/ext_gui/webgl_shader.cpp +40 -32
  254. package/runtime/ext_gui/window.cpp +7 -7
  255. package/runtime/ext_media/decode_audio.cpp +1 -1
  256. package/runtime/ext_media/media.cpp +1 -1
  257. package/runtime/ext_media/media_bridge.c +7 -7
  258. package/scripts/build-gui.ts +97 -98
  259. package/scripts/check-file-length.ts +1 -0
  260. package/scripts/gui-build-options.ts +122 -0
  261. package/src/cli/extensions.ts +72 -0
  262. package/src/cli/help.ts +3 -1
  263. package/src/cli/hints.ts +9 -3
  264. package/src/cli/main.ts +21 -27
  265. package/src/diagnostics/diagnostic.ts +2 -0
  266. package/src/driver/build-key.ts +3 -0
  267. package/src/driver/compiler.ts +63 -8
  268. package/src/driver/config.ts +6 -0
  269. package/src/driver/link-features.ts +163 -0
  270. package/src/driver/options.ts +10 -0
  271. package/src/driver/runtime-objects.ts +26 -1
  272. package/src/driver/usage.ts +376 -0
  273. package/src/driver/version.ts +1 -1
  274. package/src/extensions/catalog.ts +1 -2
  275. package/src/extensions/gui/features.ts +166 -0
  276. package/src/extensions/gui/index.ts +10 -4
  277. package/src/extensions/link-feature.ts +72 -0
  278. package/src/extensions/registry.ts +45 -4
  279. package/src/index.ts +3 -2
  280. package/dist/src/extensions/media/index.d.ts +0 -28
  281. package/dist/src/extensions/media/index.js +0 -59
  282. package/dist/src/extensions/media/index.js.map +0 -1
  283. package/dist/tests/extensions/media.test.js +0 -63
  284. package/dist/tests/extensions/media.test.js.map +0 -1
  285. package/src/extensions/media/index.ts +0 -65
  286. /package/dist/tests/{extensions/media.test.d.ts → driver/link-features.test.d.ts} +0 -0
@@ -142,6 +142,42 @@ int type_size(int type) {
142
142
  }
143
143
  }
144
144
 
145
+ /** Content hash of the vertex input state a pipeline bakes in.
146
+ *
147
+ * `WebGLDrawKey` carries `attribs_version_`/`framebuffer_version_`, which are
148
+ * *monotonic counters*: every VAO bind and every framebuffer switch bumps them.
149
+ * A page that binds several VAOs per frame (three.js does, once per geometry)
150
+ * therefore produced a new pipeline key for every draw on every frame, and the
151
+ * per-program pipeline cache never hit — measured at ~250
152
+ * `SDL_CreateGPUGraphicsPipeline` calls per frame, several milliseconds of the
153
+ * frame on its own, with a cache that grew by that many entries every frame.
154
+ * The pipeline only depends on the layout itself, so key it by the layout's
155
+ * content: the same layout stays cached across frames and is shared by every
156
+ * object that uses it. */
157
+ uint64_t vertex_layout_hash(const std::vector<SDL_GPUVertexAttribute> &attributes,
158
+ const std::vector<SDL_GPUVertexBufferDescription> &buffers) {
159
+ uint64_t hash = 1469598103934665603ull;
160
+ auto mix = [&hash](uint64_t value) {
161
+ hash ^= value;
162
+ hash *= 1099511628211ull;
163
+ hash ^= hash >> 29;
164
+ };
165
+ mix(attributes.size());
166
+ for (const SDL_GPUVertexAttribute &attribute : attributes) {
167
+ mix(attribute.location);
168
+ mix(attribute.buffer_slot);
169
+ mix((uint64_t)(uint32_t)attribute.format);
170
+ mix(attribute.offset);
171
+ }
172
+ mix(buffers.size());
173
+ for (const SDL_GPUVertexBufferDescription &buffer : buffers) {
174
+ mix(buffer.slot);
175
+ mix(buffer.pitch);
176
+ mix((uint64_t)(uint32_t)buffer.input_rate);
177
+ }
178
+ return hash;
179
+ }
180
+
145
181
  } // namespace
146
182
 
147
183
  void WebGLContext::draw_state_hash(uint64_t *first, uint64_t *second) const {
@@ -298,10 +334,15 @@ const WebGLDrawSetup *WebGLContext::prepare_draw(const WebGLDrawKey &key) {
298
334
  /* The pipeline depends on the vertex layout and fixed-function state, not on
299
335
  * which GPU buffer happens to hold the data (a buffer can be recreated after
300
336
  * a grow) nor on the element-buffer binding. Normalise those fields out so a
301
- * dynamic buffer does not create a fresh pipeline. */
337
+ * dynamic buffer does not create a fresh pipeline. The monotonic version
338
+ * counters are replaced by the layout's content hash (and zero): they change
339
+ * on every VAO bind / framebuffer switch without changing the pipeline, so
340
+ * keying on them re-created every pipeline once per frame. */
302
341
  WebGLDrawKey pipeline_key = key;
303
342
  pipeline_key.gpu_version = 0;
304
343
  pipeline_key.element_buffer = 0;
344
+ pipeline_key.attribs_version = vertex_layout_hash(setup.attributes, setup.buffers);
345
+ pipeline_key.framebuffer_version = 0;
305
346
  auto cached = program_->pipeline_cache.find(pipeline_key);
306
347
  if (cached != program_->pipeline_cache.end()) {
307
348
  setup.pipeline = cached->second;
@@ -0,0 +1,82 @@
1
+ /*
2
+ * xbintsc GUI engine — `webgl` link feature implementation.
3
+ *
4
+ * This translation unit is the only core-side place that references the WebGL
5
+ * context and DOM bindings. It is pulled out of `gui.a` only when
6
+ * `webgl_bridge.c` (the feature's C registration object) is linked, and installs
7
+ * `XtWebglApi` for the rest of the engine. Without it the table is null, so a
8
+ * page that never asks for WebGL does not link any `webgl_*`/`dom_api_webgl*`
9
+ * member.
10
+ */
11
+
12
+ #include "webgl_feature.h"
13
+
14
+ #include "webgl_api.h"
15
+ #include "dom_api_internal.h"
16
+
17
+ namespace {
18
+
19
+ void *get(XtGuiWindow *win, const void *node, bool webgl2) {
20
+ return xt_gui_webgl_get(win, static_cast<const xtgui::Node *>(node), webgl2);
21
+ }
22
+
23
+ void *find(XtGuiWindow *win, const void *node) {
24
+ return xt_gui_webgl_find(win, static_cast<const xtgui::Node *>(node));
25
+ }
26
+
27
+ void forget(XtGuiWindow *win, const void *node) {
28
+ xt_gui_webgl_forget(win, static_cast<const xtgui::Node *>(node));
29
+ }
30
+
31
+ void shutdown(XtGuiWindow *win) { xt_gui_webgl_shutdown(win); }
32
+
33
+ bool submit_all() { return xt_gui_webgl_submit_all(); }
34
+
35
+ void info(void *context, float *width, float *height, uint64_t *id) {
36
+ auto *webgl = static_cast<xtgui::WebGLContext *>(context);
37
+ *width = (float)webgl->width();
38
+ *height = (float)webgl->height();
39
+ *id = webgl->id();
40
+ }
41
+
42
+ void resize(void *context, int width, int height) {
43
+ static_cast<xtgui::WebGLContext *>(context)->resize(width, height);
44
+ }
45
+
46
+ bool read_pixels(void *context, int x, int y, int width, int height, unsigned char *out) {
47
+ return static_cast<xtgui::WebGLContext *>(context)->read_pixels(x, y, width, height, out);
48
+ }
49
+
50
+ xt_value handle(xt_value self, void *context) {
51
+ return domapi::webgl_context_handle(self, static_cast<xtgui::WebGLContext *>(context));
52
+ }
53
+
54
+ xt_value proto(bool webgl2) {
55
+ return webgl2 ? domapi::webgl2_proto() : domapi::webgl_proto();
56
+ }
57
+
58
+ xt_value object_proto(const char *kind) { return domapi::webgl_object_proto(kind); }
59
+
60
+ void *color_texture(uint64_t id) {
61
+ xtgui::WebGLContext *context = xtgui::xt_webgl_by_id(id);
62
+ return context != nullptr ? context->color_texture() : nullptr;
63
+ }
64
+
65
+ const XtWebglApi kWebglApi = {
66
+ get,
67
+ find,
68
+ forget,
69
+ shutdown,
70
+ submit_all,
71
+ info,
72
+ resize,
73
+ read_pixels,
74
+ handle,
75
+ proto,
76
+ object_proto,
77
+ color_texture,
78
+ };
79
+
80
+ } // namespace
81
+
82
+ extern "C" void xt_webgl_feature_register(void) { xt_webgl_api_install(&kWebglApi); }
@@ -0,0 +1,28 @@
1
+ /*
2
+ * xbintsc GUI engine — WebGL context lifetime, implemented by the optional
3
+ * `webgl` feature.
4
+ *
5
+ * These functions are only referenced by `webgl_feature.cpp`, which installs
6
+ * them in `XtWebglApi`; no core translation unit calls them directly. The header
7
+ * is therefore only included by the feature's own translation units.
8
+ */
9
+ #ifndef XT_GUI_WEBGL_FEATURE_H
10
+ #define XT_GUI_WEBGL_FEATURE_H
11
+
12
+ #include "gui_engine.h"
13
+ #include "webgl.h"
14
+
15
+ /** The WebGL context of a `<canvas>`, creating it on first use. `webgl2`
16
+ * selects the WebGL 2 API/prototype; a canvas keeps the first context kind it
17
+ * asked for. */
18
+ xtgui::WebGLContext *xt_gui_webgl_get(XtGuiWindow *win, const xtgui::Node *node, bool webgl2);
19
+ /** The existing WebGL context of `node`, or null (no side effects). */
20
+ xtgui::WebGLContext *xt_gui_webgl_find(XtGuiWindow *win, const xtgui::Node *node);
21
+ /** Drop a node's WebGL context (called when its handle is forgotten). */
22
+ void xt_gui_webgl_forget(XtGuiWindow *win, const xtgui::Node *node);
23
+ /** Release every WebGL context of `win`. */
24
+ void xt_gui_webgl_shutdown(XtGuiWindow *win);
25
+ /** Submit every live context's queued commands; true when any exists. */
26
+ bool xt_gui_webgl_submit_all();
27
+
28
+ #endif /* XT_GUI_WEBGL_FEATURE_H */
@@ -6,10 +6,19 @@
6
6
  * share one GLSL front end: the Vulkan path hands the SPIR-V to SDL_GPU
7
7
  * directly, the Metal path cross-compiles it to MSL text that SDL_GPU's Metal
8
8
  * backend compiles at run time.
9
+ *
10
+ * Metal is the only SDL_GPU backend that consumes MSL, so `scripts/build-gui.ts`
11
+ * merges SPIRV-Cross into `gui.a` on macOS only; the rest of this file is a stub
12
+ * (the `#else` branch) so no caller needs an `#if`. Including the archive
13
+ * everywhere would instead add ~2.5 MiB of code no driver can ever ask for.
9
14
  */
10
15
 
11
16
  #include "webgl_msl.h"
12
17
 
18
+ #if defined(__APPLE__)
19
+
20
+ #include <cstdio>
21
+ #include <cstdlib>
13
22
  #include <exception>
14
23
 
15
24
  #include <spirv_msl.hpp>
@@ -42,6 +51,13 @@ bool cross_compile_msl(const std::vector<uint32_t> &spirv, bool fragment,
42
51
  options.msl_version = spirv_cross::CompilerMSL::Options::make_msl_version(2, 0);
43
52
  compiler.set_msl_options(options);
44
53
  *source = compiler.compile();
54
+ /* The GLSL dump (`XT_WEBGL_DUMP_GLSL`) stops at the SPIR-V front end; this
55
+ * one shows what SPIRV-Cross handed the Metal backend, which is where a
56
+ * backend-only sampling difference (depth/comparison samplers) shows up. */
57
+ if (getenv("XT_WEBGL_DUMP_MSL") != nullptr) {
58
+ fprintf(stderr, "--- MSL %s ---\n%s\n---\n", fragment ? "fragment" : "vertex",
59
+ source->c_str());
60
+ }
45
61
  *entry = compiler.get_cleansed_entry_point_name(
46
62
  "main", fragment ? spv::ExecutionModelFragment : spv::ExecutionModelVertex);
47
63
  if (entry->empty()) {
@@ -64,3 +80,28 @@ bool cross_compile_msl(const std::vector<uint32_t> &spirv, bool fragment,
64
80
  }
65
81
 
66
82
  } // namespace xtgui
83
+
84
+ #else /* !__APPLE__ */
85
+
86
+ namespace xtgui {
87
+
88
+ /**
89
+ * Non-Metal builds carry no SPIRV-Cross, so there is nothing to translate with.
90
+ *
91
+ * No SDL_GPU backend outside Apple platforms reports MSL, so no driver can reach
92
+ * this; it exists so the callers keep one unconditional branch and a device that
93
+ * ever did advertise MSL fails with an actionable log instead of a wrong shader.
94
+ */
95
+ bool cross_compile_msl(const std::vector<uint32_t> &spirv, bool fragment,
96
+ std::string *source, std::string *entry, std::string *log) {
97
+ (void)spirv;
98
+ (void)fragment;
99
+ source->clear();
100
+ entry->clear();
101
+ *log = "this build has no MSL backend (SPIRV-Cross is macOS-only)";
102
+ return false;
103
+ }
104
+
105
+ } // namespace xtgui
106
+
107
+ #endif /* __APPLE__ */
@@ -8,6 +8,12 @@
8
8
  * cross-compiles it to MSL while keeping the binding indices the rewriter
9
9
  * assigned — one std140 block at buffer 0 and stage-local samplers at
10
10
  * binding N, which is exactly the slot layout SDL_GPU's Metal backend binds.
11
+ *
12
+ * Only a macOS build links SPIRV-Cross (`scripts/build-gui.ts` skips the
13
+ * archive elsewhere); on every other platform the same function is a stub that
14
+ * reports the missing backend. No driver outside Apple platforms consumes MSL,
15
+ * so the stub is unreachable — keeping the declaration unconditional just means
16
+ * the callers need no `#if`.
11
17
  */
12
18
  #ifndef XT_GUI_WEBGL_MSL_H
13
19
  #define XT_GUI_WEBGL_MSL_H
@@ -1,12 +1,9 @@
1
1
  /*
2
2
  * xbintsc GUI engine — WebGL shaders, programs, linking and uniforms.
3
3
  *
4
- * `compileShader` performs a standalone translation so `COMPILE_STATUS` and the
5
- * info log work before linking. `linkProgram` then translates both stages again
6
- * with one shared attribute/varying location map, compiles them with glslang
7
- * and reflects the std140 block out of the SPIR-V. The SDL_GPU shader then
8
- * takes whichever form the device accepts: the SPIR-V module itself on Vulkan,
9
- * or the same module cross-compiled to MSL by SPIRV-Cross on Metal.
4
+ * Translation/compilation goes through the optional `gpu` table (glslang);
5
+ * `linkProgram` shares attribute/varying locations between the stages and
6
+ * reflects the std140 block from the SPIR-V.
10
7
  */
11
8
 
12
9
  #include "webgl.h"
@@ -16,15 +13,13 @@
16
13
  #include <cstring>
17
14
 
18
15
  #include "gui_internal.h"
19
- #include "webgl_msl.h"
16
+ #include "gpu_shader_api.h"
20
17
  #include "webgl_program_internal.h"
21
18
 
22
19
  namespace xtgui {
23
20
  namespace {
24
-
25
21
  using webgl_detail::gl_type_of;
26
22
  using webgl_detail::location_slots;
27
-
28
23
  /** Merge uniforms declared by both stages, in declaration order. */
29
24
  bool merge_uniforms(const GlslShaderInfo &vs, const GlslShaderInfo &fs,
30
25
  std::vector<GlslUniformDecl> *out, std::string *error) {
@@ -80,17 +75,17 @@ struct StageCode {
80
75
  std::string text;
81
76
  };
82
77
 
83
- /**
84
- * Turn a compiled SPIR-V stage into the form the device consumes.
85
- *
86
- * Metal has no SPIR-V loader, so the module is cross-compiled to MSL; Vulkan
87
- * takes the SPIR-V directly. `code` points into `text` for MSL, so the caller
88
- * must keep the `StageCode` alive until `SDL_CreateGPUShader` has run.
89
- */
78
+ /** Turn a SPIR-V stage into what the device consumes (SPIR-V or MSL text);
79
+ * `code` points into `text` for MSL, so `stage` must outlive the SDL shader. */
90
80
  bool prepare_stage(const std::vector<uint32_t> &spirv, bool fragment,
91
81
  SDL_GPUShaderFormat formats, StageCode *stage, std::string *log) {
92
82
  if ((formats & SDL_GPU_SHADERFORMAT_MSL) != 0) {
93
- if (!cross_compile_msl(spirv, fragment, &stage->text, &stage->entry, log)) return false;
83
+ const GpuShaderApi *api = gpu_shader_api();
84
+ if (api == nullptr) {
85
+ *log = "shader translation was not linked (the 'gpu' link feature is inactive)";
86
+ return false;
87
+ }
88
+ if (!api->cross_compile_msl(spirv, fragment, &stage->text, &stage->entry, log)) return false;
94
89
  stage->format = SDL_GPU_SHADERFORMAT_MSL;
95
90
  stage->code = (const Uint8 *)stage->text.data();
96
91
  stage->code_size = stage->text.size();
@@ -146,12 +141,18 @@ bool WebGLContext::compile_shader(uint32_t id) {
146
141
  WebGLShader *record = shader(id);
147
142
  if (record == nullptr) return false;
148
143
  const bool fragment = record->type == GL_FRAGMENT_SHADER;
144
+ const GpuShaderApi *api = gpu_shader_api();
145
+ if (api == nullptr) {
146
+ record->log = "shader translation was not linked (the 'gpu' link feature is inactive)";
147
+ record->compiled = false;
148
+ return false;
149
+ }
149
150
  /* Expand three.js's pragma-marked light loops first: a sampler-array index
150
151
  * must be constant in Vulkan GLSL, and the expansion is the only place that
151
152
  * knows the loop bounds. Scanning and rewriting then share one text. */
152
- const std::string prepared = unroll_glsl_pragmas(record->source);
153
+ const std::string prepared = api->unroll_glsl_pragmas(record->source);
153
154
  GlslShaderInfo info;
154
- scan_glsl(prepared, fragment, &info);
155
+ api->scan_glsl(prepared, fragment, &info);
155
156
  if (!info.unsupported.empty()) {
156
157
  record->log = info.unsupported;
157
158
  record->compiled = false;
@@ -162,9 +163,9 @@ bool WebGLContext::compile_shader(uint32_t id) {
162
163
  assign_locations(info.varyings, {}, &layout.varyings);
163
164
  assign_locations(info.samplers, {}, &layout.samplers);
164
165
  layout.block_members = info.uniforms;
165
- const std::string source = rewrite_glsl(prepared, fragment, layout, info);
166
+ const std::string source = api->rewrite_glsl(prepared, fragment, layout, info);
166
167
  std::vector<uint32_t> spirv;
167
- record->compiled = compile_glsl(source, fragment, &spirv, &record->log);
168
+ record->compiled = api->compile_glsl(source, fragment, &spirv, &record->log);
168
169
  return record->compiled;
169
170
  }
170
171
 
@@ -268,12 +269,19 @@ bool WebGLContext::link_program(uint32_t id) {
268
269
  return false;
269
270
  }
270
271
 
271
- const std::string prepared_vertex = unroll_glsl_pragmas(vertex->source);
272
- const std::string prepared_fragment = unroll_glsl_pragmas(fragment->source);
272
+ const GpuShaderApi *api = gpu_shader_api();
273
+ if (api == nullptr) {
274
+ record->log = "shader translation was not linked (the 'gpu' link feature is inactive)";
275
+ record->linked = false;
276
+ return false;
277
+ }
278
+
279
+ const std::string prepared_vertex = api->unroll_glsl_pragmas(vertex->source);
280
+ const std::string prepared_fragment = api->unroll_glsl_pragmas(fragment->source);
273
281
  GlslShaderInfo vs_info;
274
282
  GlslShaderInfo fs_info;
275
- scan_glsl(prepared_vertex, false, &vs_info);
276
- scan_glsl(prepared_fragment, true, &fs_info);
283
+ api->scan_glsl(prepared_vertex, false, &vs_info);
284
+ api->scan_glsl(prepared_fragment, true, &fs_info);
277
285
  if (!vs_info.unsupported.empty() || !fs_info.unsupported.empty()) {
278
286
  record->log = vs_info.unsupported.empty() ? fs_info.unsupported : vs_info.unsupported;
279
287
  record->linked = false;
@@ -342,17 +350,17 @@ bool WebGLContext::link_program(uint32_t id) {
342
350
  }
343
351
  fs_layout.varyings = vs_layout.varyings;
344
352
 
345
- const std::string vs_source = rewrite_glsl(prepared_vertex, false, vs_layout, vs_info);
346
- const std::string fs_source = rewrite_glsl(prepared_fragment, true, fs_layout, fs_info);
353
+ const std::string vs_source = api->rewrite_glsl(prepared_vertex, false, vs_layout, vs_info);
354
+ const std::string fs_source = api->rewrite_glsl(prepared_fragment, true, fs_layout, fs_info);
347
355
  std::vector<uint32_t> vs_spirv;
348
356
  std::vector<uint32_t> fs_spirv;
349
357
  std::string log;
350
- if (!compile_glsl(vs_source, false, &vs_spirv, &log)) {
358
+ if (!api->compile_glsl(vs_source, false, &vs_spirv, &log)) {
351
359
  record->log = "vertex shader:\n" + log;
352
360
  record->linked = false;
353
361
  return false;
354
362
  }
355
- if (!compile_glsl(fs_source, true, &fs_spirv, &log)) {
363
+ if (!api->compile_glsl(fs_source, true, &fs_spirv, &log)) {
356
364
  record->log = "fragment shader:\n" + log;
357
365
  record->linked = false;
358
366
  return false;
@@ -360,9 +368,9 @@ bool WebGLContext::link_program(uint32_t id) {
360
368
 
361
369
  /* Reflect the std140 block (members survive even when unused). */
362
370
  std::vector<GlslBlockMember> members;
363
- reflect_uniform_block(vs_spirv, &members);
371
+ api->reflect_uniform_block(vs_spirv, &members);
364
372
  std::vector<GlslBlockMember> fragment_members;
365
- reflect_uniform_block(fs_spirv, &fragment_members);
373
+ api->reflect_uniform_block(fs_spirv, &fragment_members);
366
374
  /* Keep members declared by either stage: a uniform block may only be read by
367
375
  * the fragment shader while the vertex stage contributes other uniforms. */
368
376
  for (const GlslBlockMember &member : fragment_members) {
@@ -380,7 +388,7 @@ bool WebGLContext::link_program(uint32_t id) {
380
388
  * declarations, so the source scan over-reports; use the reflection and keep
381
389
  * the declaration order from the source for stable `getActiveAttrib` order. */
382
390
  std::unordered_map<std::string, int> active_attributes;
383
- reflect_attributes(vs_spirv, &active_attributes);
391
+ api->reflect_attributes(vs_spirv, &active_attributes);
384
392
  if (!active_attributes.empty()) {
385
393
  std::vector<std::pair<int, GlslUniformDecl>> ordered;
386
394
  for (const GlslUniformDecl &decl : vs_info.attributes) {
@@ -13,7 +13,7 @@
13
13
 
14
14
  #include <algorithm>
15
15
 
16
- #include "inline_svg.h"
16
+ #include "image_api.h"
17
17
  #include "paint.h"
18
18
  #include "perf.h"
19
19
  #include "text.h"
@@ -252,17 +252,17 @@ static xt_value win_inline_svg(xt_value self, xt_value env, int32_t argc, xt_val
252
252
  const char *selector = xt_string_data(selectorValue);
253
253
  if (selector == nullptr) return XT_UNDEFINED;
254
254
  const xtgui::Node *node = win->document->querySelector(selector);
255
- if (!xtgui::xt_inline_svg_is_root(node)) return XT_UNDEFINED;
256
- std::string markup = xtgui::xt_inline_svg_serialize(node);
257
- int width = 0;
258
- int height = 0;
259
- xtgui::xt_svg_markup_size(markup, &width, &height);
255
+ const XtImageApi *images = xt_image_api();
256
+ if (images == nullptr || !images->inline_is_root(node)) return XT_UNDEFINED;
257
+ std::string markup = images->inline_serialize(node, &win->document->styles());
258
+ int width = 0, height = 0;
259
+ images->svg_markup_size(markup, &width, &height);
260
260
  xt_value object = xt_object_new();
261
261
  xt_object_set(object, xt_string_from_cstr("markup"), xt_string_from_cstr(markup.c_str()));
262
262
  xt_object_set(object, xt_string_from_cstr("width"), xt_number(width));
263
263
  xt_object_set(object, xt_string_from_cstr("height"), xt_number(height));
264
264
  xt_object_set(object, xt_string_from_cstr("shapes"),
265
- xt_number(xtgui::xt_svg_markup_shapes(markup)));
265
+ xt_number(images->svg_markup_shapes(markup)));
266
266
  return object;
267
267
  }
268
268
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * xbintsc media extension — in-memory audio decoding.
2
+ * xbintsc media link feature — in-memory audio decoding.
3
3
  *
4
4
  * `AudioContext.decodeAudioData` hands over bytes, not a path, so the file
5
5
  * oriented `MediaPlayer` cannot be reused. This unit opens the bytes through a
@@ -1,5 +1,5 @@
1
1
  /*
2
- * xbintsc media extension — `<video>`/`<audio>` decoding.
2
+ * xbintsc media link feature — `<video>`/`<audio>` decoding.
3
3
  *
4
4
  * This translation unit is the only place that knows about FFmpeg. It is
5
5
  * compiled into the prebuilt `media.a`/`media.lib` by `scripts/build-media.ts` and linked
@@ -1,19 +1,19 @@
1
1
  /*
2
- * xbintsc media extension — registration bridge.
2
+ * xbintsc media link feature — registration bridge.
3
3
  *
4
4
  * The decoders live in `media.a` (`media.lib` with the MSVC ABI on Windows), a
5
5
  * prebuilt C++ archive. A static archive is
6
6
  * only searched for members that satisfy an undefined symbol, so a backend that
7
7
  * merely had a constructor would never be extracted. This tiny C translation
8
- * unit is compiled and linked *directly* (it is the media extension's
9
- * `runtimeSources()` entry): its constructor references every backend entry
8
+ * unit is compiled and linked *directly* — but only when the compiler selects
9
+ * the `media` feature (it is the feature's `runtimeSources()` entry): its
10
+ * constructor references every backend entry
10
11
  * point, which pulls the archive member in, and installs the table into the GUI
11
12
  * engine before `main` runs.
12
13
  *
13
- * The bridge is intentionally C-only and has no FFmpeg (or SDL) dependency. If
14
- * the `gui` extension is not enabled, `xt_media_install` is unresolved; the
15
- * registry rejects that combination first with a "media requires gui"
16
- * diagnostic (see `src/extensions/registry.ts`).
14
+ * The bridge is intentionally C-only and has no FFmpeg (or SDL) dependency. The
15
+ * feature belongs to `gui`, so it is only ever compiled with `gui` enabled;
16
+ * `xt_media_install` therefore always resolves.
17
17
  */
18
18
 
19
19
  #include "../ext_gui/media_api.h"