xbintsc 0.5.32 → 0.5.37

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 (222) hide show
  1. package/README.md +10 -7
  2. package/README.zh-CN.md +10 -8
  3. package/dist/scripts/check-version.d.ts +27 -0
  4. package/dist/scripts/check-version.js +71 -0
  5. package/dist/scripts/check-version.js.map +1 -0
  6. package/dist/scripts/package-platform.d.ts +58 -0
  7. package/dist/scripts/package-platform.js +142 -0
  8. package/dist/scripts/package-platform.js.map +1 -0
  9. package/dist/scripts/verify-platform-package.d.ts +34 -0
  10. package/dist/scripts/verify-platform-package.js +161 -0
  11. package/dist/scripts/verify-platform-package.js.map +1 -0
  12. package/dist/src/cli/help.d.ts +1 -1
  13. package/dist/src/cli/main.js +3 -1
  14. package/dist/src/cli/main.js.map +1 -1
  15. package/dist/src/driver/runtime-lib.d.ts +29 -4
  16. package/dist/src/driver/runtime-lib.js +68 -10
  17. package/dist/src/driver/runtime-lib.js.map +1 -1
  18. package/dist/src/driver/version.d.ts +1 -1
  19. package/dist/src/driver/version.js +1 -1
  20. package/dist/src/extensions/gui/index.js +18 -5
  21. package/dist/src/extensions/gui/index.js.map +1 -1
  22. package/dist/src/extensions/media/index.js +4 -3
  23. package/dist/src/extensions/media/index.js.map +1 -1
  24. package/dist/src/parser/context.d.ts +41 -0
  25. package/dist/src/parser/context.js +89 -4
  26. package/dist/src/parser/context.js.map +1 -1
  27. package/dist/src/parser/literals.js +6 -12
  28. package/dist/src/parser/literals.js.map +1 -1
  29. package/dist/tests/driver/platform-package.test.d.ts +1 -0
  30. package/dist/tests/driver/platform-package.test.js +49 -0
  31. package/dist/tests/driver/platform-package.test.js.map +1 -0
  32. package/dist/tests/driver/runtime-lib.test.js +51 -4
  33. package/dist/tests/driver/runtime-lib.test.js.map +1 -1
  34. package/dist/tests/e2e/gui-audio.test.d.ts +14 -0
  35. package/dist/tests/e2e/gui-audio.test.js +287 -0
  36. package/dist/tests/e2e/gui-audio.test.js.map +1 -0
  37. package/dist/tests/e2e/gui-browser.test.js +65 -0
  38. package/dist/tests/e2e/gui-browser.test.js.map +1 -1
  39. package/dist/tests/e2e/gui-canvas-args.test.d.ts +6 -0
  40. package/dist/tests/e2e/gui-canvas-args.test.js +217 -0
  41. package/dist/tests/e2e/gui-canvas-args.test.js.map +1 -0
  42. package/dist/tests/e2e/gui-canvas.test.js.map +1 -1
  43. package/dist/tests/e2e/gui-dom.test.js +215 -0
  44. package/dist/tests/e2e/gui-dom.test.js.map +1 -1
  45. package/dist/tests/e2e/gui-events.test.js +112 -0
  46. package/dist/tests/e2e/gui-events.test.js.map +1 -1
  47. package/dist/tests/e2e/gui-helpers.d.ts +7 -0
  48. package/dist/tests/e2e/gui-helpers.js +1 -0
  49. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  50. package/dist/tests/e2e/gui-media.test.js +78 -0
  51. package/dist/tests/e2e/gui-media.test.js.map +1 -1
  52. package/dist/tests/e2e/gui-style.test.js +55 -0
  53. package/dist/tests/e2e/gui-style.test.js.map +1 -1
  54. package/dist/tests/e2e/gui-webgl.test.js +258 -0
  55. package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
  56. package/dist/tests/e2e/gui-webgl2-3d.test.d.ts +4 -0
  57. package/dist/tests/e2e/gui-webgl2-3d.test.js +264 -0
  58. package/dist/tests/e2e/gui-webgl2-3d.test.js.map +1 -0
  59. package/dist/tests/e2e/gui-webgl2-blit.test.d.ts +10 -0
  60. package/dist/tests/e2e/gui-webgl2-blit.test.js +262 -0
  61. package/dist/tests/e2e/gui-webgl2-blit.test.js.map +1 -0
  62. package/dist/tests/e2e/gui-webgl2-formats.test.d.ts +6 -0
  63. package/dist/tests/e2e/gui-webgl2-formats.test.js +395 -0
  64. package/dist/tests/e2e/gui-webgl2-formats.test.js.map +1 -0
  65. package/dist/tests/e2e/gui-webgl2-mrt.test.d.ts +4 -0
  66. package/dist/tests/e2e/gui-webgl2-mrt.test.js +105 -0
  67. package/dist/tests/e2e/gui-webgl2-mrt.test.js.map +1 -0
  68. package/dist/tests/e2e/gui-webgl2-ubo.test.d.ts +8 -0
  69. package/dist/tests/e2e/gui-webgl2-ubo.test.js +112 -0
  70. package/dist/tests/e2e/gui-webgl2-ubo.test.js.map +1 -0
  71. package/dist/tests/e2e/gui-webgl2.test.js +45 -1
  72. package/dist/tests/e2e/gui-webgl2.test.js.map +1 -1
  73. package/dist/tests/extensions/gui-index.test.js +1 -0
  74. package/dist/tests/extensions/gui-index.test.js.map +1 -1
  75. package/dist/tests/extensions/media.test.js +1 -0
  76. package/dist/tests/extensions/media.test.js.map +1 -1
  77. package/dist/tests/parser/expressions.test.js +30 -0
  78. package/dist/tests/parser/expressions.test.js.map +1 -1
  79. package/dist/tests/platform-tarball.test.d.ts +1 -0
  80. package/dist/tests/platform-tarball.test.js +111 -0
  81. package/dist/tests/platform-tarball.test.js.map +1 -0
  82. package/dist/tests/version.test.js +6 -0
  83. package/dist/tests/version.test.js.map +1 -1
  84. package/doc/ai/contributing.md +11 -0
  85. package/doc/ai/extensions.md +7 -4
  86. package/doc/ai/troubleshooting.md +7 -5
  87. package/doc/ai/zh-CN/contributing.md +9 -0
  88. package/doc/ai/zh-CN/extensions.md +4 -2
  89. package/doc/ai/zh-CN/troubleshooting.md +5 -3
  90. package/doc/gui/api.md +99 -22
  91. package/doc/gui/build.md +11 -4
  92. package/doc/gui/canvas.md +49 -18
  93. package/doc/gui/dom.md +57 -7
  94. package/doc/gui/media.md +4 -0
  95. package/doc/gui/webgl.md +105 -46
  96. package/doc/gui.md +7 -3
  97. package/doc/npm-package.md +55 -27
  98. package/doc/requirements.md +6 -2
  99. package/doc/zh-CN/gui/api.md +84 -14
  100. package/doc/zh-CN/gui/build.md +7 -2
  101. package/doc/zh-CN/gui/canvas.md +44 -19
  102. package/doc/zh-CN/gui/dom.md +48 -4
  103. package/doc/zh-CN/gui/media.md +3 -0
  104. package/doc/zh-CN/gui/webgl.md +22 -16
  105. package/doc/zh-CN/gui.md +3 -2
  106. package/doc/zh-CN/npm-package.md +44 -23
  107. package/doc/zh-CN/requirements.md +4 -1
  108. package/package.json +17 -1
  109. package/runtime/ext_gui/audio.cpp +365 -153
  110. package/runtime/ext_gui/audio_buffer.cpp +166 -0
  111. package/runtime/ext_gui/audio_buffer.h +37 -0
  112. package/runtime/ext_gui/audio_decode.cpp +219 -0
  113. package/runtime/ext_gui/audio_decode.h +39 -0
  114. package/runtime/ext_gui/audio_device.cpp +87 -0
  115. package/runtime/ext_gui/audio_effects.cpp +163 -0
  116. package/runtime/ext_gui/audio_effects.h +25 -0
  117. package/runtime/ext_gui/audio_internal.h +30 -0
  118. package/runtime/ext_gui/audio_mixer.cpp +494 -0
  119. package/runtime/ext_gui/audio_mixer.h +238 -0
  120. package/runtime/ext_gui/audio_param.cpp +104 -0
  121. package/runtime/ext_gui/audio_param_object.cpp +155 -0
  122. package/runtime/ext_gui/browser.cpp +45 -50
  123. package/runtime/ext_gui/browser_abort.cpp +249 -0
  124. package/runtime/ext_gui/browser_dialogs.cpp +76 -0
  125. package/runtime/ext_gui/browser_media.cpp +176 -0
  126. package/runtime/ext_gui/browser_navigation.cpp +130 -0
  127. package/runtime/ext_gui/canvas.cpp +88 -30
  128. package/runtime/ext_gui/canvas.h +74 -9
  129. package/runtime/ext_gui/canvas_image_data.cpp +143 -0
  130. package/runtime/ext_gui/canvas_internal.h +8 -5
  131. package/runtime/ext_gui/canvas_pattern.cpp +90 -0
  132. package/runtime/ext_gui/canvas_raster.cpp +31 -9
  133. package/runtime/ext_gui/canvas_style.cpp +4 -2
  134. package/runtime/ext_gui/css_selector.cpp +21 -3
  135. package/runtime/ext_gui/dom.h +4 -0
  136. package/runtime/ext_gui/dom_api.cpp +1 -0
  137. package/runtime/ext_gui/dom_api_canvas.cpp +120 -114
  138. package/runtime/ext_gui/dom_api_canvas_extra.cpp +57 -2
  139. package/runtime/ext_gui/dom_api_canvas_image.cpp +239 -0
  140. package/runtime/ext_gui/dom_api_canvas_paths.cpp +179 -0
  141. package/runtime/ext_gui/dom_api_computed_style.cpp +124 -0
  142. package/runtime/ext_gui/dom_api_dataset.cpp +154 -0
  143. package/runtime/ext_gui/dom_api_document.cpp +58 -5
  144. package/runtime/ext_gui/dom_api_events.cpp +93 -17
  145. package/runtime/ext_gui/dom_api_internal.h +71 -2
  146. package/runtime/ext_gui/dom_api_mutation.cpp +159 -0
  147. package/runtime/ext_gui/dom_api_node.cpp +40 -19
  148. package/runtime/ext_gui/dom_api_node_read.cpp +37 -1
  149. package/runtime/ext_gui/dom_api_path2d.cpp +358 -0
  150. package/runtime/ext_gui/dom_api_proto.cpp +35 -0
  151. package/runtime/ext_gui/dom_api_style.cpp +223 -13
  152. package/runtime/ext_gui/dom_api_webgl.cpp +7 -0
  153. package/runtime/ext_gui/dom_api_webgl2.cpp +151 -36
  154. package/runtime/ext_gui/dom_api_webgl2_draw.cpp +3 -3
  155. package/runtime/ext_gui/dom_api_webgl_buffer.cpp +6 -2
  156. package/runtime/ext_gui/dom_api_webgl_compressed.cpp +167 -0
  157. package/runtime/ext_gui/dom_api_webgl_framebuffer.cpp +160 -0
  158. package/runtime/ext_gui/dom_api_webgl_shader.cpp +4 -4
  159. package/runtime/ext_gui/dom_api_webgl_source.cpp +161 -0
  160. package/runtime/ext_gui/dom_api_webgl_state.cpp +169 -23
  161. package/runtime/ext_gui/dom_api_webgl_texture.cpp +276 -236
  162. package/runtime/ext_gui/dom_api_webgl_ubo.cpp +81 -0
  163. package/runtime/ext_gui/gui.cpp +103 -14
  164. package/runtime/ext_gui/gui_engine.h +56 -4
  165. package/runtime/ext_gui/gui_events.cpp +80 -9
  166. package/runtime/ext_gui/image_bitmap.cpp +28 -0
  167. package/runtime/ext_gui/image_bitmap.h +26 -0
  168. package/runtime/ext_gui/image_bitmap_browser.cpp +136 -0
  169. package/runtime/ext_gui/listener_options.h +43 -0
  170. package/runtime/ext_gui/media.cpp +32 -0
  171. package/runtime/ext_gui/media.h +13 -0
  172. package/runtime/ext_gui/media_api.h +23 -0
  173. package/runtime/ext_gui/paint.cpp +28 -0
  174. package/runtime/ext_gui/paint.h +4 -0
  175. package/runtime/ext_gui/webgl.h +152 -18
  176. package/runtime/ext_gui/webgl_blit.cpp +95 -0
  177. package/runtime/ext_gui/webgl_buffer.cpp +178 -263
  178. package/runtime/ext_gui/webgl_buffer_objects.cpp +270 -0
  179. package/runtime/ext_gui/webgl_clear.cpp +142 -0
  180. package/runtime/ext_gui/webgl_compressed.cpp +180 -0
  181. package/runtime/ext_gui/webgl_context.cpp +241 -156
  182. package/runtime/ext_gui/webgl_draw.cpp +68 -3
  183. package/runtime/ext_gui/webgl_draw_buffers.cpp +75 -0
  184. package/runtime/ext_gui/webgl_glsl.h +40 -0
  185. package/runtime/ext_gui/webgl_glsl_compile.cpp +6 -0
  186. package/runtime/ext_gui/webgl_glsl_rewrite.cpp +18 -0
  187. package/runtime/ext_gui/webgl_glsl_scan.cpp +20 -11
  188. package/runtime/ext_gui/webgl_glsl_scan_block.cpp +126 -0
  189. package/runtime/ext_gui/webgl_glsl_tokens.h +28 -0
  190. package/runtime/ext_gui/webgl_pipeline.cpp +71 -19
  191. package/runtime/ext_gui/webgl_program_blocks.cpp +58 -0
  192. package/runtime/ext_gui/webgl_program_internal.h +6 -0
  193. package/runtime/ext_gui/webgl_shader.cpp +13 -1
  194. package/runtime/ext_gui/webgl_state.cpp +228 -0
  195. package/runtime/ext_gui/webgl_texture.cpp +144 -17
  196. package/runtime/ext_gui/webgl_texture_3d.cpp +215 -0
  197. package/runtime/ext_gui/webgl_uniform_block.h +31 -0
  198. package/runtime/ext_gui/webgl_webgl2.cpp +58 -5
  199. package/runtime/ext_gui/window.cpp +14 -65
  200. package/runtime/ext_gui/window_events.cpp +125 -0
  201. package/runtime/ext_media/decode_audio.cpp +308 -0
  202. package/runtime/ext_media/media_bridge.c +2 -0
  203. package/runtime/rt.h +43 -0
  204. package/runtime/rt_internal.h +4 -0
  205. package/runtime/xt_array_buffer.c +30 -0
  206. package/runtime/xt_builtins.c +0 -21
  207. package/runtime/xt_containers/access.inc +94 -2
  208. package/runtime/xt_containers/objects.inc +29 -31
  209. package/runtime/xt_typed_array/elements.inc +13 -0
  210. package/scripts/build-media.ts +14 -6
  211. package/scripts/build-runtime.ts +21 -16
  212. package/scripts/check-version.ts +39 -9
  213. package/scripts/package-platform.ts +183 -0
  214. package/scripts/set-platform-version.ts +62 -0
  215. package/scripts/verify-platform-package.ts +182 -0
  216. package/src/cli/main.ts +5 -1
  217. package/src/driver/runtime-lib.ts +73 -9
  218. package/src/driver/version.ts +1 -1
  219. package/src/extensions/gui/index.ts +18 -5
  220. package/src/extensions/media/index.ts +4 -3
  221. package/src/parser/context.ts +99 -4
  222. package/src/parser/literals.ts +6 -12
@@ -0,0 +1,75 @@
1
+ /*
2
+ * xbintsc GUI engine — WebGL 2 draw/read buffers and multiple render targets.
3
+ *
4
+ * The framebuffer stores up to four colour attachments and the draw-buffer
5
+ * table that maps pipeline slot N to one of them. `begin_pass` and
6
+ * `create_pipeline` consume the same slots, so a fragment shader's `out`
7
+ * variables land in the attachments the page selected with `drawBuffers`.
8
+ */
9
+
10
+ #include "webgl.h"
11
+
12
+ #include "gui_internal.h"
13
+
14
+ namespace xtgui {
15
+
16
+ SDL_GPUTexture *WebGLContext::draw_color_texture(int slot) {
17
+ if (slot < 0 || slot >= kWebGLMaxDrawBuffers) return nullptr;
18
+ if (bound_framebuffer_ == 0) {
19
+ ensure_default_target();
20
+ return slot == 0 && default_draw_buffers_[0] == GL_BACK ? color_ : nullptr;
21
+ }
22
+ WebGLFramebuffer *framebuffer = this->framebuffer(bound_framebuffer_);
23
+ if (framebuffer == nullptr) return nullptr;
24
+ const int buffer = framebuffer->draw_buffers[slot];
25
+ const int index = buffer - GL_COLOR_ATTACHMENT0;
26
+ if (index < 0 || index >= kWebGLMaxDrawBuffers) return nullptr;
27
+ if (framebuffer->color_textures[index] == 0) return nullptr;
28
+ WebGLTexture *texture = this->texture(framebuffer->color_textures[index]);
29
+ return texture != nullptr ? ensure_texture_gpu(texture, true) : nullptr;
30
+ }
31
+
32
+ int WebGLContext::draw_color_format(int slot) {
33
+ if (bound_framebuffer_ == 0) return SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
34
+ WebGLFramebuffer *framebuffer = this->framebuffer(bound_framebuffer_);
35
+ if (framebuffer == nullptr) return SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
36
+ const int index = framebuffer->draw_buffers[slot] - GL_COLOR_ATTACHMENT0;
37
+ if (index < 0 || index >= kWebGLMaxDrawBuffers) {
38
+ return SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
39
+ }
40
+ WebGLTexture *texture = this->texture(framebuffer->color_textures[index]);
41
+ return texture != nullptr ? (int)texture->format : SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
42
+ }
43
+
44
+ int WebGLContext::draw_color_layer(int slot) {
45
+ if (slot < 0 || slot >= kWebGLMaxDrawBuffers || bound_framebuffer_ == 0) return 0;
46
+ WebGLFramebuffer *framebuffer = this->framebuffer(bound_framebuffer_);
47
+ if (framebuffer == nullptr) return 0;
48
+ const int index = framebuffer->draw_buffers[slot] - GL_COLOR_ATTACHMENT0;
49
+ if (index < 0 || index >= kWebGLMaxDrawBuffers) return 0;
50
+ return framebuffer->color_layers[index];
51
+ }
52
+
53
+ void WebGLContext::draw_buffers(int count, const int *buffers) {
54
+ if (count < 0) count = 0;
55
+ if (count > kWebGLMaxDrawBuffers) count = kWebGLMaxDrawBuffers;
56
+ int *target = default_draw_buffers_;
57
+ if (bound_framebuffer_ != 0) {
58
+ WebGLFramebuffer *framebuffer = this->framebuffer(bound_framebuffer_);
59
+ if (framebuffer == nullptr) return;
60
+ target = framebuffer->draw_buffers;
61
+ }
62
+ for (int i = 0; i < kWebGLMaxDrawBuffers; i++) {
63
+ target[i] = (i < count && buffers != nullptr) ? buffers[i] : GL_NONE;
64
+ }
65
+ end_pass();
66
+ }
67
+
68
+ int WebGLContext::draw_buffer_value(int index) {
69
+ if (index < 0 || index >= kWebGLMaxDrawBuffers) return GL_NONE;
70
+ if (bound_framebuffer_ == 0) return default_draw_buffers_[index];
71
+ WebGLFramebuffer *framebuffer = this->framebuffer(bound_framebuffer_);
72
+ return framebuffer != nullptr ? framebuffer->draw_buffers[index] : GL_NONE;
73
+ }
74
+
75
+ } // namespace xtgui
@@ -22,6 +22,8 @@
22
22
 
23
23
  #include <cstdint>
24
24
  #include <string>
25
+
26
+ #include "webgl_glsl_tokens.h"
25
27
  #include <unordered_map>
26
28
  #include <utility>
27
29
  #include <vector>
@@ -40,6 +42,32 @@ struct GlslUniformDecl {
40
42
  std::string extra;
41
43
  };
42
44
 
45
+ /** One member of a user-declared uniform block. */
46
+ struct GlslBlockMemberDecl {
47
+ std::string type;
48
+ std::string name;
49
+ std::string array;
50
+ };
51
+
52
+ /** A user-declared `uniform Block { … } instance;`. The rewriter merges its
53
+ * members into the engine block under `__ub_<instance>_<member>` names and
54
+ * rewrites references, because SDL_GPU has no bindable uniform buffers. */
55
+ struct GlslUniformBlock {
56
+ std::string name;
57
+ std::string instance;
58
+ std::vector<GlslBlockMemberDecl> members;
59
+ size_t begin = 0;
60
+ size_t end = 0;
61
+ bool conditional = false;
62
+ };
63
+
64
+ /** One `instance.member` reference to rename. */
65
+ struct GlslRename {
66
+ size_t begin = 0;
67
+ size_t end = 0;
68
+ std::string text;
69
+ };
70
+
43
71
  /** Where one declaration sits in the source text, and what it declares. */
44
72
  struct GlslDeclSite {
45
73
  enum Kind { Uniform, Sampler, Attribute, Varying, FragmentOut };
@@ -53,6 +81,14 @@ struct GlslDeclSite {
53
81
  };
54
82
 
55
83
  /** The declarations a shader contributes to a program. */
84
+ struct GlslShaderInfo;
85
+
86
+ /** Parse a user uniform block at `uniform_index`, merging its members. Returns
87
+ * true when a block declaration was consumed; `after` is the next token. */
88
+ bool scan_uniform_block(const std::vector<GlslToken> &tokens, size_t uniform_index,
89
+ size_t pending_layout, bool conditional, GlslShaderInfo *info,
90
+ size_t *after, size_t *first_uniform_any);
91
+
56
92
  struct GlslShaderInfo {
57
93
  std::vector<GlslUniformDecl> uniforms; // non-sampler
58
94
  std::vector<GlslUniformDecl> samplers;
@@ -65,6 +101,10 @@ struct GlslShaderInfo {
65
101
  std::vector<std::pair<size_t, size_t>> structs;
66
102
  /** The text of those definitions, shared across stages at link time. */
67
103
  std::vector<std::string> struct_definitions;
104
+ /** User uniform blocks, merged into the single engine block at link time. */
105
+ std::vector<GlslUniformBlock> blocks;
106
+ /** References the rewriter must spell with the prefixed member name. */
107
+ std::vector<GlslRename> renames;
68
108
  /** True when the source writes `gl_FragColor` / `gl_FragData[0]`. */
69
109
  bool legacy_fragment_output = false;
70
110
  /** True when the source declared its own `out` variable (ES 3.00). */
@@ -10,6 +10,8 @@
10
10
 
11
11
  #include "webgl_glsl.h"
12
12
 
13
+ #include <cstdio>
14
+ #include <cstdlib>
13
15
  #include <cstring>
14
16
  #include <string>
15
17
  #include <unordered_map>
@@ -347,6 +349,10 @@ bool compile_glsl(const std::string &source, bool fragment, std::vector<uint32_t
347
349
  }
348
350
  const EShLanguage stage = fragment ? EShLangFragment : EShLangVertex;
349
351
  glslang::TShader shader(stage);
352
+ if (getenv("XT_WEBGL_DUMP_GLSL") != nullptr) {
353
+ fprintf(stderr, "--- GLSL %s ---\n%s\n---\n", fragment ? "fragment" : "vertex",
354
+ source.c_str());
355
+ }
350
356
  const char *text = source.c_str();
351
357
  shader.setStrings(&text, 1);
352
358
  shader.setEntryPoint("main");
@@ -213,6 +213,24 @@ std::string rewrite_glsl(const std::string &source, bool fragment, const GlslLin
213
213
  edits.push_back(block_edit);
214
214
  }
215
215
 
216
+ /* Drop user uniform-block declarations (their members are already part of
217
+ * the generated block) and spell `instance.member` with the prefixed name. */
218
+ for (const GlslUniformBlock &block_decl : info.blocks) {
219
+ Edit remove;
220
+ remove.begin = block_decl.begin;
221
+ remove.end = block_decl.end;
222
+ remove.order = 1;
223
+ edits.push_back(remove);
224
+ }
225
+ for (const GlslRename &rename : info.renames) {
226
+ Edit edit;
227
+ edit.begin = rename.begin;
228
+ edit.end = rename.end;
229
+ edit.text = rename.text;
230
+ edit.order = 1;
231
+ edits.push_back(edit);
232
+ }
233
+
216
234
  int fragment_output = 0;
217
235
  for (const GlslDeclSite &site : info.sites) {
218
236
  Edit edit;
@@ -17,12 +17,7 @@
17
17
  namespace xtgui {
18
18
  namespace {
19
19
 
20
- struct Token {
21
- enum Kind { Ident, Number, Punct, Directive } kind = Punct;
22
- std::string text;
23
- size_t begin = 0;
24
- size_t end = 0;
25
- };
20
+ using Token = GlslToken;
26
21
 
27
22
  bool is_ident_start(char c) {
28
23
  return std::isalpha((unsigned char)c) != 0 || c == '_';
@@ -100,11 +95,6 @@ std::vector<Token> tokenize(const std::string &source) {
100
95
  return tokens;
101
96
  }
102
97
 
103
- bool is_qualifier(const std::string &text) {
104
- return text == "highp" || text == "mediump" || text == "lowp" || text == "flat" ||
105
- text == "smooth" || text == "centroid" || text == "invariant" || text == "precise" ||
106
- text == "noperspective" || text == "sample" || text == "const";
107
- }
108
98
 
109
99
  bool is_sampler_type(const std::string &type) {
110
100
  return type.rfind("sampler", 0) == 0 || type == "atomic_uint";
@@ -435,6 +425,14 @@ void scan_glsl(const std::string &source, bool fragment, GlslShaderInfo *info) {
435
425
  info->legacy_fragment_output = true;
436
426
  continue;
437
427
  }
428
+ /* SDL_GPU render targets carry no writable depth attachment, so there is
429
+ * no place for a shader-written depth value; reject it with a clear log
430
+ * instead of failing later inside glslang. */
431
+ if (inactive == 0 && fragment && token.kind == Token::Ident &&
432
+ (token.text == "gl_FragDepth" || token.text == "gl_FragDepthEXT")) {
433
+ info->unsupported = "gl_FragDepth is not supported";
434
+ continue;
435
+ }
438
436
  /* Declarations inside a definitively false preprocessor branch never reach
439
437
  * the compiler; rewriting them would move their types/macros (and array
440
438
  * sizes such as `MORPHTARGETS_COUNT`) outside that branch. */
@@ -503,6 +501,17 @@ void scan_glsl(const std::string &source, bool fragment, GlslShaderInfo *info) {
503
501
  continue;
504
502
  }
505
503
 
504
+ if (kind == GlslDeclSite::Uniform) {
505
+ size_t after = i;
506
+ if (scan_uniform_block(tokens, i, pending_layout, !condition_stack.empty(), info, &after,
507
+ &first_uniform_any)) {
508
+ pending_layout = std::string::npos;
509
+ pending_location = -1;
510
+ i = after;
511
+ continue;
512
+ }
513
+ }
514
+
506
515
  /* Find the terminating semicolon at bracket/paren depth 0. */
507
516
  size_t semicolon = i + 1;
508
517
  int brackets = 0;
@@ -0,0 +1,126 @@
1
+ /*
2
+ * xbintsc GUI engine — GLSL user uniform-block scanner.
3
+ *
4
+ * The block's members are merged into the engine's single std140 block under
5
+ * `xt_ub_<instance>_<member>` names; `instance.member` references are renamed
6
+ * by the rewriter. SDL_GPU has no bindable uniform buffers, so the draw path
7
+ * copies the bound buffer into these members instead.
8
+ */
9
+
10
+ #include "webgl_glsl.h"
11
+
12
+ #include <string>
13
+ #include <vector>
14
+
15
+ namespace xtgui {
16
+
17
+ bool scan_uniform_block(const std::vector<GlslToken> &tokens, size_t uniform_index,
18
+ size_t pending_layout, bool conditional, GlslShaderInfo *info,
19
+ size_t *after, size_t *first_uniform_any) {
20
+ size_t brace = uniform_index + 1;
21
+ while (brace < tokens.size() && tokens[brace].text != "{" && tokens[brace].text != ";") {
22
+ brace++;
23
+ }
24
+ if (brace >= tokens.size() || tokens[brace].text != "{") return false;
25
+
26
+ std::string block_name;
27
+ for (size_t t = uniform_index + 1; t < brace; t++) {
28
+ if (tokens[t].kind == GlslToken::Ident) block_name = tokens[t].text;
29
+ }
30
+ size_t close = brace;
31
+ int braces = 0;
32
+ for (; close < tokens.size(); close++) {
33
+ if (tokens[close].text == "{") braces++;
34
+ if (tokens[close].text == "}") {
35
+ braces--;
36
+ if (braces == 0) break;
37
+ }
38
+ if (tokens[close].kind == GlslToken::Directive) {
39
+ info->unsupported = "preprocessor directives inside uniform blocks are not supported";
40
+ *after = close;
41
+ return true;
42
+ }
43
+ }
44
+ if (close + 2 >= tokens.size() || tokens[close + 1].kind != GlslToken::Ident ||
45
+ tokens[close + 2].text != ";") {
46
+ info->unsupported = "uniform blocks without an instance name are not supported";
47
+ *after = close;
48
+ return true;
49
+ }
50
+
51
+ GlslUniformBlock block;
52
+ block.name = block_name;
53
+ block.instance = tokens[close + 1].text;
54
+ block.begin = pending_layout != std::string::npos ? tokens[pending_layout].begin
55
+ : tokens[uniform_index].begin;
56
+ block.end = tokens[close + 2].end;
57
+ block.conditional = conditional;
58
+ bool valid = !block.instance.empty();
59
+ size_t member_start = brace + 1;
60
+ int inner_braces = 0;
61
+ for (size_t t = brace + 1; t < close && valid; t++) {
62
+ if (tokens[t].text == "{") inner_braces++;
63
+ if (tokens[t].text == "}") inner_braces--;
64
+ if (!(tokens[t].text == ";" && inner_braces == 0)) continue;
65
+ size_t at = member_start;
66
+ while (at < t && tokens[at].kind == GlslToken::Ident && is_qualifier(tokens[at].text)) at++;
67
+ if (at + 1 >= t || tokens[at].kind != GlslToken::Ident ||
68
+ tokens[at + 1].kind != GlslToken::Ident) {
69
+ valid = false;
70
+ break;
71
+ }
72
+ GlslBlockMemberDecl member;
73
+ member.type = tokens[at].text;
74
+ member.name = tokens[at + 1].text;
75
+ size_t array_at = at + 2;
76
+ if (array_at < t && tokens[array_at].text == "[") {
77
+ std::string array;
78
+ for (size_t k = array_at; k < t; k++) array += tokens[k].text;
79
+ member.array = array;
80
+ }
81
+ if (member.type.rfind("sampler", 0) == 0 || member.type == "atomic_uint" ||
82
+ member.type == "struct") {
83
+ valid = false;
84
+ break;
85
+ }
86
+ block.members.push_back(member);
87
+ member_start = t + 1;
88
+ }
89
+ if (!valid || block.members.empty()) {
90
+ info->unsupported = "unsupported uniform block declaration";
91
+ *after = close;
92
+ return true;
93
+ }
94
+
95
+ for (size_t t = 0; t + 2 < tokens.size(); t++) {
96
+ if (tokens[t].kind != GlslToken::Ident || tokens[t].text != block.instance) continue;
97
+ if (tokens[t + 1].text != "." || tokens[t + 2].kind != GlslToken::Ident) continue;
98
+ if (tokens[t].begin >= block.begin && tokens[t].begin < block.end) continue;
99
+ bool is_member = false;
100
+ for (const GlslBlockMemberDecl &member : block.members) {
101
+ if (member.name == tokens[t + 2].text) is_member = true;
102
+ }
103
+ if (!is_member) continue;
104
+ GlslRename rename;
105
+ rename.begin = tokens[t].begin;
106
+ rename.end = tokens[t + 2].end;
107
+ rename.text = "xt_ub_" + block.instance + "_" + tokens[t + 2].text;
108
+ info->renames.push_back(rename);
109
+ }
110
+ for (const GlslBlockMemberDecl &member : block.members) {
111
+ GlslUniformDecl decl;
112
+ decl.type = member.type;
113
+ decl.name = "xt_ub_" + block.instance + "_" + member.name;
114
+ decl.array = member.array;
115
+ info->uniforms.push_back(decl);
116
+ }
117
+ if (*first_uniform_any == std::string::npos) *first_uniform_any = block.begin;
118
+ if (!block.conditional && info->first_uniform_offset == std::string::npos) {
119
+ info->first_uniform_offset = block.begin;
120
+ }
121
+ info->blocks.push_back(block);
122
+ *after = close + 2;
123
+ return true;
124
+ }
125
+
126
+ } // namespace xtgui
@@ -0,0 +1,28 @@
1
+ /*
2
+ * xbintsc GUI engine — shared GLSL token type for the scanner and block parser.
3
+ */
4
+ #ifndef XT_GUI_WEBGL_GLSL_TOKENS_H
5
+ #define XT_GUI_WEBGL_GLSL_TOKENS_H
6
+
7
+ #include <cstddef>
8
+ #include <string>
9
+
10
+ namespace xtgui {
11
+
12
+ struct GlslToken {
13
+ enum Kind { Ident, Number, Punct, Directive } kind = Punct;
14
+ std::string text;
15
+ size_t begin = 0;
16
+ size_t end = 0;
17
+ };
18
+
19
+ inline bool is_qualifier(const std::string &text) {
20
+ return text == "const" || text == "uniform" || text == "attribute" || text == "varying" ||
21
+ text == "in" || text == "out" || text == "inout" || text == "highp" ||
22
+ text == "mediump" || text == "lowp" || text == "flat" || text == "smooth" ||
23
+ text == "centroid" || text == "invariant" || text == "precision";
24
+ }
25
+
26
+ } // namespace xtgui
27
+
28
+ #endif /* XT_GUI_WEBGL_GLSL_TOKENS_H */
@@ -87,6 +87,46 @@ SDL_GPUCompareOp compare_op(int func) {
87
87
  }
88
88
  }
89
89
 
90
+ SDL_GPUStencilOp stencil_op(int op) {
91
+ switch (op) {
92
+ case GL_ZERO:
93
+ return SDL_GPU_STENCILOP_ZERO;
94
+ case GL_REPLACE:
95
+ return SDL_GPU_STENCILOP_REPLACE;
96
+ case GL_INCR:
97
+ return SDL_GPU_STENCILOP_INCREMENT_AND_CLAMP;
98
+ case GL_INCR_WRAP:
99
+ return SDL_GPU_STENCILOP_INCREMENT_AND_WRAP;
100
+ case GL_DECR:
101
+ return SDL_GPU_STENCILOP_DECREMENT_AND_CLAMP;
102
+ case GL_DECR_WRAP:
103
+ return SDL_GPU_STENCILOP_DECREMENT_AND_WRAP;
104
+ case GL_INVERT:
105
+ return SDL_GPU_STENCILOP_INVERT;
106
+ default:
107
+ return SDL_GPU_STENCILOP_KEEP;
108
+ }
109
+ }
110
+
111
+ /** SDL 3.2 has no per-pipeline stencil write mask. A mask of 0 means "write
112
+ * nothing", which every op can express as KEEP; a partial mask cannot be
113
+ * represented, so the op applies to all bits. */
114
+ SDL_GPUStencilOpState stencil_face_state(const WebGLStencilFace &face) {
115
+ SDL_GPUStencilOpState state;
116
+ memset(&state, 0, sizeof(state));
117
+ if (face.write_mask == 0) {
118
+ state.fail_op = SDL_GPU_STENCILOP_KEEP;
119
+ state.pass_op = SDL_GPU_STENCILOP_KEEP;
120
+ state.depth_fail_op = SDL_GPU_STENCILOP_KEEP;
121
+ } else {
122
+ state.fail_op = stencil_op(face.fail);
123
+ state.pass_op = stencil_op(face.zpass);
124
+ state.depth_fail_op = stencil_op(face.zfail);
125
+ }
126
+ state.compare_op = compare_op(face.func);
127
+ return state;
128
+ }
129
+
90
130
  SDL_GPUPrimitiveType primitive_of(int mode) {
91
131
  switch (mode) {
92
132
  case GL_POINTS:
@@ -215,22 +255,27 @@ SDL_GPUGraphicsPipeline *WebGLContext::create_pipeline(
215
255
  const SDL_GPUVertexAttribute *attrs, const SDL_GPUVertexBufferDescription *buffers,
216
256
  const std::string &key) {
217
257
  (void)index_size;
218
- SDL_GPUColorTargetDescription color_target;
219
- memset(&color_target, 0, sizeof(color_target));
220
- color_target.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
221
- color_target.blend_state.enable_blend = state_.blend;
222
- color_target.blend_state.src_color_blendfactor = blend_factor(state_.blend_src_rgb);
223
- color_target.blend_state.dst_color_blendfactor = blend_factor(state_.blend_dst_rgb);
224
- color_target.blend_state.color_blend_op = blend_op(state_.blend_eq_rgb);
225
- color_target.blend_state.src_alpha_blendfactor = blend_factor(state_.blend_src_alpha);
226
- color_target.blend_state.dst_alpha_blendfactor = blend_factor(state_.blend_dst_alpha);
227
- color_target.blend_state.alpha_blend_op = blend_op(state_.blend_eq_alpha);
228
- color_target.blend_state.enable_color_write_mask = true;
229
- color_target.blend_state.color_write_mask =
230
- (state_.color_mask[0] ? SDL_GPU_COLORCOMPONENT_R : 0) |
231
- (state_.color_mask[1] ? SDL_GPU_COLORCOMPONENT_G : 0) |
232
- (state_.color_mask[2] ? SDL_GPU_COLORCOMPONENT_B : 0) |
233
- (state_.color_mask[3] ? SDL_GPU_COLORCOMPONENT_A : 0);
258
+ SDL_GPUColorTargetDescription color_targets[kWebGLMaxDrawBuffers];
259
+ const int color_target_count =
260
+ state_.color_target_count > 0 ? state_.color_target_count : 1;
261
+ for (int i = 0; i < color_target_count; i++) {
262
+ SDL_GPUColorTargetDescription &color_target = color_targets[i];
263
+ memset(&color_target, 0, sizeof(color_target));
264
+ color_target.format = (SDL_GPUTextureFormat)state_.color_formats[i];
265
+ color_target.blend_state.enable_blend = state_.blend;
266
+ color_target.blend_state.src_color_blendfactor = blend_factor(state_.blend_src_rgb);
267
+ color_target.blend_state.dst_color_blendfactor = blend_factor(state_.blend_dst_rgb);
268
+ color_target.blend_state.color_blend_op = blend_op(state_.blend_eq_rgb);
269
+ color_target.blend_state.src_alpha_blendfactor = blend_factor(state_.blend_src_alpha);
270
+ color_target.blend_state.dst_alpha_blendfactor = blend_factor(state_.blend_dst_alpha);
271
+ color_target.blend_state.alpha_blend_op = blend_op(state_.blend_eq_alpha);
272
+ color_target.blend_state.enable_color_write_mask = true;
273
+ color_target.blend_state.color_write_mask =
274
+ (state_.color_mask[0] ? SDL_GPU_COLORCOMPONENT_R : 0) |
275
+ (state_.color_mask[1] ? SDL_GPU_COLORCOMPONENT_G : 0) |
276
+ (state_.color_mask[2] ? SDL_GPU_COLORCOMPONENT_B : 0) |
277
+ (state_.color_mask[3] ? SDL_GPU_COLORCOMPONENT_A : 0);
278
+ }
234
279
 
235
280
  SDL_GPUGraphicsPipelineCreateInfo info;
236
281
  memset(&info, 0, sizeof(info));
@@ -255,9 +300,16 @@ SDL_GPUGraphicsPipeline *WebGLContext::create_pipeline(
255
300
  info.depth_stencil_state.enable_depth_test = state_.depth_test;
256
301
  info.depth_stencil_state.enable_depth_write = state_.depth_write;
257
302
  info.depth_stencil_state.compare_op = compare_op(state_.depth_func);
258
- info.target_info.color_target_descriptions = &color_target;
259
- info.target_info.num_color_targets = 1;
260
- info.target_info.depth_stencil_format = SDL_GPU_TEXTUREFORMAT_D32_FLOAT;
303
+ info.depth_stencil_state.enable_stencil_test = state_.stencil;
304
+ info.depth_stencil_state.front_stencil_state = stencil_face_state(state_.stencil_front);
305
+ info.depth_stencil_state.back_stencil_state = stencil_face_state(state_.stencil_back);
306
+ /* SDL_GPU has one compare/write mask for both faces; use the front-face
307
+ * values (WebGL's default sets both faces alike). */
308
+ info.depth_stencil_state.compare_mask = (Uint8)(state_.stencil_front.value_mask & 0xFF);
309
+ info.depth_stencil_state.write_mask = (Uint8)(state_.stencil_front.write_mask & 0xFF);
310
+ info.target_info.color_target_descriptions = color_targets;
311
+ info.target_info.num_color_targets = (Uint32)color_target_count;
312
+ info.target_info.depth_stencil_format = (SDL_GPUTextureFormat)state_.depth_format;
261
313
  info.target_info.has_depth_stencil_target = true;
262
314
 
263
315
  SDL_GPUGraphicsPipeline *pipeline = SDL_CreateGPUGraphicsPipeline(guidev::g_device, &info);
@@ -0,0 +1,58 @@
1
+ /*
2
+ * xbintsc GUI engine — user uniform blocks reflected from the merged block.
3
+ */
4
+
5
+ #include "webgl.h"
6
+
7
+ #include <algorithm>
8
+ #include <vector>
9
+
10
+ #include "webgl_program_internal.h"
11
+
12
+ namespace xtgui {
13
+ namespace webgl_detail {
14
+
15
+ void build_uniform_blocks(WebGLProgram *record, const std::vector<GlslUniformBlock> &vertex_blocks,
16
+ const std::vector<GlslUniformBlock> &fragment_blocks,
17
+ const std::vector<GlslBlockMember> &members) {
18
+ if (record == nullptr) return;
19
+ /* Build the user-facing uniform-block table. Members were merged into the
20
+ * engine block as `__ub_<instance>_<member>`, so their reflected offsets give
21
+ * the block's base and size without a second descriptor set. */
22
+ auto add_blocks = [&](const std::vector<GlslUniformBlock> &blocks) {
23
+ for (const GlslUniformBlock &decl : blocks) {
24
+ bool exists = false;
25
+ for (const WebGLUniformBlock &existing : record->blocks) {
26
+ if (existing.instance == decl.instance) exists = true;
27
+ }
28
+ if (exists) continue;
29
+ WebGLUniformBlock block;
30
+ block.name = decl.name;
31
+ block.instance = decl.instance;
32
+ int base = -1;
33
+ int end = 0;
34
+ int count = 0;
35
+ for (const GlslBlockMemberDecl &member : decl.members) {
36
+ const std::string name = "xt_ub_" + decl.instance + "_" + member.name;
37
+ for (const GlslBlockMember &reflected : members) {
38
+ if (reflected.name != name && reflected.name != name + "[0]") continue;
39
+ if (base < 0 || reflected.offset < base) base = reflected.offset;
40
+ end = std::max(end, reflected.offset + webgl_detail::member_bytes(reflected));
41
+ count++;
42
+ break;
43
+ }
44
+ }
45
+ if (base < 0 || count == 0) continue;
46
+ block.base_offset = base;
47
+ block.data_size = ((end - base) + 15) & ~15;
48
+ block.member_count = count;
49
+ record->blocks.push_back(block);
50
+ }
51
+ };
52
+ add_blocks(vertex_blocks);
53
+ add_blocks(fragment_blocks);
54
+
55
+ }
56
+
57
+ } // namespace webgl_detail
58
+ } // namespace xtgui
@@ -4,6 +4,7 @@
4
4
  #ifndef XT_GUI_WEBGL_PROGRAM_INTERNAL_H
5
5
  #define XT_GUI_WEBGL_PROGRAM_INTERNAL_H
6
6
 
7
+ #include "webgl.h"
7
8
  #include "webgl_glsl.h"
8
9
 
9
10
  namespace xtgui {
@@ -12,6 +13,11 @@ namespace webgl_detail {
12
13
  /** Bytes a std140 block member occupies (the whole array for an array). */
13
14
  int member_bytes(const GlslBlockMember &member);
14
15
 
16
+ /** Fill `record->blocks` from the user blocks merged into the engine block. */
17
+ void build_uniform_blocks(WebGLProgram *record, const std::vector<GlslUniformBlock> &vertex_blocks,
18
+ const std::vector<GlslUniformBlock> &fragment_blocks,
19
+ const std::vector<GlslBlockMember> &members);
20
+
15
21
  } // namespace webgl_detail
16
22
  } // namespace xtgui
17
23
 
@@ -40,6 +40,8 @@ int gl_type_of(const std::string &type) {
40
40
  if (type == "mat4") return GL_FLOAT_MAT4;
41
41
  if (type == "sampler2D") return GL_SAMPLER_2D;
42
42
  if (type == "samplerCube") return GL_SAMPLER_CUBE;
43
+ if (type == "sampler3D") return GL_SAMPLER_3D;
44
+ if (type == "sampler2DArray") return GL_SAMPLER_2D_ARRAY;
43
45
  return 0;
44
46
  }
45
47
 
@@ -380,7 +382,15 @@ bool WebGLContext::link_program(uint32_t id) {
380
382
  reflect_uniform_block(vs_spirv, &members);
381
383
  std::vector<GlslBlockMember> fragment_members;
382
384
  reflect_uniform_block(fs_spirv, &fragment_members);
383
- if (members.empty()) members = fragment_members;
385
+ /* Keep members declared by either stage: a uniform block may only be read by
386
+ * the fragment shader while the vertex stage contributes other uniforms. */
387
+ for (const GlslBlockMember &member : fragment_members) {
388
+ bool present = false;
389
+ for (const GlslBlockMember &existing : members) {
390
+ if (existing.name == member.name) present = true;
391
+ }
392
+ if (!present) members.push_back(member);
393
+ }
384
394
 
385
395
  record->uniforms.clear();
386
396
  record->uniform_index.clear();
@@ -445,6 +455,8 @@ bool WebGLContext::link_program(uint32_t id) {
445
455
  for (const GlslUniformDecl &decl : vs_info.samplers) add_sampler(record, &record->vs_sampler_uniforms, decl);
446
456
  for (const GlslUniformDecl &decl : fs_info.samplers) add_sampler(record, &record->fs_sampler_uniforms, decl);
447
457
 
458
+ webgl_detail::build_uniform_blocks(record, vs_info.blocks, fs_info.blocks, members);
459
+
448
460
  /* (Re)create the SDL shaders. */
449
461
  if (guidev::g_device == nullptr) {
450
462
  record->log = "no GPU device";