xbintsc 0.5.37 → 0.5.41

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 (166) hide show
  1. package/dist/src/cli/help.d.ts +1 -1
  2. package/dist/src/codegen/generator/module.js +23 -7
  3. package/dist/src/codegen/generator/module.js.map +1 -1
  4. package/dist/src/codegen/generator/state.js +1 -0
  5. package/dist/src/codegen/generator/state.js.map +1 -1
  6. package/dist/src/codegen/generator/tables.js +1 -0
  7. package/dist/src/codegen/generator/tables.js.map +1 -1
  8. package/dist/src/driver/bundler/deferred.d.ts +63 -0
  9. package/dist/src/driver/bundler/deferred.js +279 -0
  10. package/dist/src/driver/bundler/deferred.js.map +1 -0
  11. package/dist/src/driver/bundler/merge.js +214 -2
  12. package/dist/src/driver/bundler/merge.js.map +1 -1
  13. package/dist/src/driver/runtime-objects.d.ts +1 -1
  14. package/dist/src/driver/runtime-objects.js +1 -0
  15. package/dist/src/driver/runtime-objects.js.map +1 -1
  16. package/dist/src/driver/version.d.ts +1 -1
  17. package/dist/src/driver/version.js +1 -1
  18. package/dist/src/extensions/gui/html.js +98 -15
  19. package/dist/src/extensions/gui/html.js.map +1 -1
  20. package/dist/src/extensions/gui/index.js +1 -0
  21. package/dist/src/extensions/gui/index.js.map +1 -1
  22. package/dist/src/extensions/gui/three.d.ts +4 -3
  23. package/dist/src/extensions/gui/three.js +4 -3
  24. package/dist/src/extensions/gui/three.js.map +1 -1
  25. package/dist/tests/codegen/closures.test.js +7 -0
  26. package/dist/tests/codegen/closures.test.js.map +1 -1
  27. package/dist/tests/e2e/compile-objects-classes.test.js +31 -0
  28. package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
  29. package/dist/tests/e2e/gui-canvas-state.test.d.ts +9 -0
  30. package/dist/tests/e2e/gui-canvas-state.test.js +297 -0
  31. package/dist/tests/e2e/gui-canvas-state.test.js.map +1 -0
  32. package/dist/tests/e2e/gui-draw-perf.test.d.ts +22 -0
  33. package/dist/tests/e2e/gui-draw-perf.test.js +405 -0
  34. package/dist/tests/e2e/gui-draw-perf.test.js.map +1 -0
  35. package/dist/tests/e2e/gui-perf.test.d.ts +23 -0
  36. package/dist/tests/e2e/gui-perf.test.js +283 -0
  37. package/dist/tests/e2e/gui-perf.test.js.map +1 -0
  38. package/dist/tests/e2e/gui-script-import-order.test.d.ts +10 -0
  39. package/dist/tests/e2e/gui-script-import-order.test.js +169 -0
  40. package/dist/tests/e2e/gui-script-import-order.test.js.map +1 -0
  41. package/dist/tests/e2e/gui-webgl.test.js +4 -4
  42. package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
  43. package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.d.ts +8 -0
  44. package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.js +336 -0
  45. package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.js.map +1 -0
  46. package/dist/tests/e2e/gui-webgl2-shadow.test.d.ts +10 -0
  47. package/dist/tests/e2e/gui-webgl2-shadow.test.js +108 -0
  48. package/dist/tests/e2e/gui-webgl2-shadow.test.js.map +1 -0
  49. package/dist/tests/e2e/url.test.d.ts +6 -0
  50. package/dist/tests/e2e/url.test.js +114 -0
  51. package/dist/tests/e2e/url.test.js.map +1 -0
  52. package/dist/tests/extensions/gui.test.js +45 -10
  53. package/dist/tests/extensions/gui.test.js.map +1 -1
  54. package/doc/gui/api.md +28 -2
  55. package/doc/gui/canvas.md +110 -27
  56. package/doc/gui/known-issues.md +32 -10
  57. package/doc/gui/rendering.md +77 -0
  58. package/doc/gui/runtime.md +18 -3
  59. package/doc/gui/webgl.md +58 -29
  60. package/doc/gui-scripts.md +19 -19
  61. package/doc/gui.md +43 -9
  62. package/doc/implemented/fetch.md +5 -0
  63. package/doc/implemented/modules.md +13 -0
  64. package/doc/implemented/stdlib.md +17 -0
  65. package/doc/unimplemented/deviations.md +1 -0
  66. package/doc/zh-CN/gui/api.md +25 -2
  67. package/doc/zh-CN/gui/canvas.md +95 -26
  68. package/doc/zh-CN/gui/known-issues.md +21 -4
  69. package/doc/zh-CN/gui/rendering.md +59 -0
  70. package/doc/zh-CN/gui/runtime.md +13 -3
  71. package/doc/zh-CN/gui/webgl.md +30 -8
  72. package/doc/zh-CN/gui-scripts.md +14 -13
  73. package/doc/zh-CN/gui.md +32 -8
  74. package/doc/zh-CN/implemented/fetch.md +4 -0
  75. package/doc/zh-CN/implemented/modules.md +10 -0
  76. package/doc/zh-CN/implemented/stdlib.md +15 -0
  77. package/doc/zh-CN/unimplemented/deviations.md +1 -0
  78. package/package.json +7 -7
  79. package/runtime/ext_gui/browser.cpp +3 -1
  80. package/runtime/ext_gui/canvas.cpp +135 -89
  81. package/runtime/ext_gui/canvas.h +116 -0
  82. package/runtime/ext_gui/canvas_clip.cpp +143 -0
  83. package/runtime/ext_gui/canvas_composite.cpp +241 -0
  84. package/runtime/ext_gui/canvas_coverage.cpp +241 -0
  85. package/runtime/ext_gui/canvas_gradient.cpp +41 -4
  86. package/runtime/ext_gui/canvas_image_data.cpp +4 -0
  87. package/runtime/ext_gui/canvas_internal.h +34 -9
  88. package/runtime/ext_gui/canvas_raster.cpp +207 -178
  89. package/runtime/ext_gui/canvas_shadow.cpp +161 -0
  90. package/runtime/ext_gui/canvas_stroke.cpp +37 -9
  91. package/runtime/ext_gui/canvas_style.cpp +59 -0
  92. package/runtime/ext_gui/canvas_text.cpp +140 -0
  93. package/runtime/ext_gui/css.h +7 -0
  94. package/runtime/ext_gui/css_sheet.cpp +22 -0
  95. package/runtime/ext_gui/document.cpp +25 -1
  96. package/runtime/ext_gui/document.h +8 -0
  97. package/runtime/ext_gui/dom_api_canvas.cpp +16 -1
  98. package/runtime/ext_gui/dom_api_canvas_image.cpp +9 -0
  99. package/runtime/ext_gui/dom_api_canvas_state.cpp +217 -0
  100. package/runtime/ext_gui/dom_api_internal.h +36 -37
  101. package/runtime/ext_gui/dom_api_offscreen.cpp +213 -0
  102. package/runtime/ext_gui/dom_api_proto.cpp +18 -0
  103. package/runtime/ext_gui/dom_api_webgl.cpp +29 -9
  104. package/runtime/ext_gui/dom_api_webgl2.cpp +33 -1
  105. package/runtime/ext_gui/dom_api_webgl_internal.h +58 -0
  106. package/runtime/ext_gui/dom_api_webgl_source.cpp +18 -0
  107. package/runtime/ext_gui/gradient_values.cpp +12 -1
  108. package/runtime/ext_gui/gui.cpp +5 -0
  109. package/runtime/ext_gui/gui_canvas.cpp +4 -0
  110. package/runtime/ext_gui/gui_engine.h +32 -0
  111. package/runtime/ext_gui/gui_events.cpp +182 -58
  112. package/runtime/ext_gui/gui_media.cpp +3 -0
  113. package/runtime/ext_gui/image.cpp +69 -0
  114. package/runtime/ext_gui/image_bitmap_browser.cpp +11 -0
  115. package/runtime/ext_gui/layout.cpp +4 -0
  116. package/runtime/ext_gui/paint.cpp +19 -0
  117. package/runtime/ext_gui/paint_background.cpp +18 -1
  118. package/runtime/ext_gui/perf.cpp +171 -0
  119. package/runtime/ext_gui/perf.h +127 -0
  120. package/runtime/ext_gui/renderer.cpp +109 -41
  121. package/runtime/ext_gui/renderer.h +33 -9
  122. package/runtime/ext_gui/renderer_image.cpp +135 -65
  123. package/runtime/ext_gui/renderer_internal.h +15 -3
  124. package/runtime/ext_gui/renderer_text.cpp +24 -12
  125. package/runtime/ext_gui/script.cpp +31 -0
  126. package/runtime/ext_gui/style.cpp +101 -30
  127. package/runtime/ext_gui/style.h +6 -3
  128. package/runtime/ext_gui/style_print.cpp +7 -1
  129. package/runtime/ext_gui/text.cpp +23 -4
  130. package/runtime/ext_gui/text.h +29 -0
  131. package/runtime/ext_gui/text_cache.cpp +118 -0
  132. package/runtime/ext_gui/text_glyph_cache.cpp +100 -0
  133. package/runtime/ext_gui/webgl.h +48 -146
  134. package/runtime/ext_gui/webgl_blit.cpp +12 -1
  135. package/runtime/ext_gui/webgl_blit_depth.cpp +270 -0
  136. package/runtime/ext_gui/webgl_buffer.cpp +59 -1
  137. package/runtime/ext_gui/webgl_buffer_objects.cpp +106 -9
  138. package/runtime/ext_gui/webgl_clear.cpp +3 -0
  139. package/runtime/ext_gui/webgl_context.cpp +29 -3
  140. package/runtime/ext_gui/webgl_draw.cpp +75 -270
  141. package/runtime/ext_gui/webgl_draw_buffers.cpp +1 -0
  142. package/runtime/ext_gui/webgl_draw_setup.cpp +321 -0
  143. package/runtime/ext_gui/webgl_draw_state.h +130 -0
  144. package/runtime/ext_gui/webgl_glsl.h +3 -0
  145. package/runtime/ext_gui/webgl_glsl_scan.cpp +3 -8
  146. package/runtime/ext_gui/webgl_glsl_unroll.cpp +283 -0
  147. package/runtime/ext_gui/webgl_msl.cpp +4 -0
  148. package/runtime/ext_gui/webgl_objects.h +148 -0
  149. package/runtime/ext_gui/webgl_pipeline.cpp +8 -8
  150. package/runtime/ext_gui/webgl_program_internal.h +35 -0
  151. package/runtime/ext_gui/webgl_shader.cpp +34 -36
  152. package/runtime/ext_gui/webgl_webgl2.cpp +36 -0
  153. package/runtime/ext_gui/window.cpp +56 -1
  154. package/runtime/rt.h +3 -0
  155. package/runtime/xt_fetch/async.inc +60 -0
  156. package/runtime/xt_url_object.c +463 -0
  157. package/src/codegen/generator/module.ts +22 -7
  158. package/src/codegen/generator/state.ts +1 -0
  159. package/src/codegen/generator/tables.ts +1 -0
  160. package/src/driver/bundler/deferred.ts +351 -0
  161. package/src/driver/bundler/merge.ts +219 -2
  162. package/src/driver/runtime-objects.ts +1 -0
  163. package/src/driver/version.ts +1 -1
  164. package/src/extensions/gui/html.ts +100 -17
  165. package/src/extensions/gui/index.ts +1 -0
  166. package/src/extensions/gui/three.ts +4 -3
@@ -0,0 +1,321 @@
1
+ /*
2
+ * xbintsc GUI engine — prepared WebGL draws.
3
+ *
4
+ * Every draw needs the same three things: the pipeline for the current
5
+ * fixed-function state, the vertex layout of the program's active attributes,
6
+ * and the GPU buffer handles behind them. Building that per draw was the
7
+ * dominant CPU cost of a multi-element scene, so it is built once per
8
+ * (program, layout version, state) and cached in `draw_setups_`.
9
+ *
10
+ * A setup is invalidated by bumping one of the context's state versions:
11
+ * `attribs_version_` for anything that changes the vertex layout (attribute
12
+ * pointers, enables, VAO loads), `gpu_version_` for buffer handle changes, and
13
+ * `framebuffer_version_` for attachment-format changes.
14
+ */
15
+
16
+ #include "webgl.h"
17
+
18
+ #include <cstring>
19
+ #include <utility>
20
+ #include <vector>
21
+
22
+ #include "gui_internal.h"
23
+
24
+ namespace xtgui {
25
+ namespace {
26
+
27
+ int components_of_type(const std::string &type) {
28
+ if (!type.empty() && type.back() >= '2' && type.back() <= '4') return type.back() - '0';
29
+ return 1;
30
+ }
31
+
32
+ int matrix_slots(const std::string &type) {
33
+ if (type == "mat2") return 2;
34
+ if (type == "mat3") return 3;
35
+ if (type == "mat4") return 4;
36
+ return 1;
37
+ }
38
+
39
+ SDL_GPUVertexElementFormat float_format(int size) {
40
+ switch (size) {
41
+ case 1:
42
+ return SDL_GPU_VERTEXELEMENTFORMAT_FLOAT;
43
+ case 2:
44
+ return SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2;
45
+ case 3:
46
+ return SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3;
47
+ default:
48
+ return SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4;
49
+ }
50
+ }
51
+
52
+ SDL_GPUVertexElementFormat format_of(int size, int type, bool normalized) {
53
+ if (size < 1 || size > 4) return SDL_GPU_VERTEXELEMENTFORMAT_INVALID;
54
+ if (type == GL_FLOAT) return float_format(size);
55
+ if (type == GL_UNSIGNED_BYTE || type == GL_BYTE) {
56
+ const bool is_byte = type == GL_BYTE;
57
+ if (size == 2) {
58
+ return normalized ? (is_byte ? SDL_GPU_VERTEXELEMENTFORMAT_BYTE2_NORM
59
+ : SDL_GPU_VERTEXELEMENTFORMAT_UBYTE2_NORM)
60
+ : (is_byte ? SDL_GPU_VERTEXELEMENTFORMAT_BYTE2
61
+ : SDL_GPU_VERTEXELEMENTFORMAT_UBYTE2);
62
+ }
63
+ if (size == 4) {
64
+ return normalized ? (is_byte ? SDL_GPU_VERTEXELEMENTFORMAT_BYTE4_NORM
65
+ : SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4_NORM)
66
+ : (is_byte ? SDL_GPU_VERTEXELEMENTFORMAT_BYTE4
67
+ : SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4);
68
+ }
69
+ return SDL_GPU_VERTEXELEMENTFORMAT_INVALID;
70
+ }
71
+ if (type == GL_UNSIGNED_SHORT || type == GL_SHORT) {
72
+ const bool is_short = type == GL_SHORT;
73
+ if (size == 2) {
74
+ return normalized ? (is_short ? SDL_GPU_VERTEXELEMENTFORMAT_SHORT2_NORM
75
+ : SDL_GPU_VERTEXELEMENTFORMAT_USHORT2_NORM)
76
+ : (is_short ? SDL_GPU_VERTEXELEMENTFORMAT_SHORT2
77
+ : SDL_GPU_VERTEXELEMENTFORMAT_USHORT2);
78
+ }
79
+ if (size == 4) {
80
+ return normalized ? (is_short ? SDL_GPU_VERTEXELEMENTFORMAT_SHORT4_NORM
81
+ : SDL_GPU_VERTEXELEMENTFORMAT_USHORT4_NORM)
82
+ : (is_short ? SDL_GPU_VERTEXELEMENTFORMAT_SHORT4
83
+ : SDL_GPU_VERTEXELEMENTFORMAT_USHORT4);
84
+ }
85
+ return SDL_GPU_VERTEXELEMENTFORMAT_INVALID;
86
+ }
87
+ return SDL_GPU_VERTEXELEMENTFORMAT_INVALID;
88
+ }
89
+
90
+ SDL_GPUVertexElementFormat int_format_of(int size, int type) {
91
+ if (size < 1 || size > 4) return SDL_GPU_VERTEXELEMENTFORMAT_INVALID;
92
+ if (type == GL_INT) {
93
+ switch (size) {
94
+ case 1:
95
+ return SDL_GPU_VERTEXELEMENTFORMAT_INT;
96
+ case 2:
97
+ return SDL_GPU_VERTEXELEMENTFORMAT_INT2;
98
+ case 3:
99
+ return SDL_GPU_VERTEXELEMENTFORMAT_INT3;
100
+ default:
101
+ return SDL_GPU_VERTEXELEMENTFORMAT_INT4;
102
+ }
103
+ }
104
+ if (type == GL_UNSIGNED_INT) {
105
+ switch (size) {
106
+ case 1:
107
+ return SDL_GPU_VERTEXELEMENTFORMAT_UINT;
108
+ case 2:
109
+ return SDL_GPU_VERTEXELEMENTFORMAT_UINT2;
110
+ case 3:
111
+ return SDL_GPU_VERTEXELEMENTFORMAT_UINT3;
112
+ default:
113
+ return SDL_GPU_VERTEXELEMENTFORMAT_UINT4;
114
+ }
115
+ }
116
+ if (type == GL_SHORT && (size == 2 || size == 4)) {
117
+ return size == 2 ? SDL_GPU_VERTEXELEMENTFORMAT_SHORT2 : SDL_GPU_VERTEXELEMENTFORMAT_SHORT4;
118
+ }
119
+ if (type == GL_UNSIGNED_SHORT && (size == 2 || size == 4)) {
120
+ return size == 2 ? SDL_GPU_VERTEXELEMENTFORMAT_USHORT2
121
+ : SDL_GPU_VERTEXELEMENTFORMAT_USHORT4;
122
+ }
123
+ if (type == GL_BYTE && (size == 2 || size == 4)) {
124
+ return size == 2 ? SDL_GPU_VERTEXELEMENTFORMAT_BYTE2 : SDL_GPU_VERTEXELEMENTFORMAT_BYTE4;
125
+ }
126
+ if (type == GL_UNSIGNED_BYTE && (size == 2 || size == 4)) {
127
+ return size == 2 ? SDL_GPU_VERTEXELEMENTFORMAT_UBYTE2 : SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4;
128
+ }
129
+ return SDL_GPU_VERTEXELEMENTFORMAT_INVALID;
130
+ }
131
+
132
+ int type_size(int type) {
133
+ switch (type) {
134
+ case GL_BYTE:
135
+ case GL_UNSIGNED_BYTE:
136
+ return 1;
137
+ case GL_SHORT:
138
+ case GL_UNSIGNED_SHORT:
139
+ return 2;
140
+ default:
141
+ return 4;
142
+ }
143
+ }
144
+
145
+ } // namespace
146
+
147
+ void WebGLContext::draw_state_hash(uint64_t *first, uint64_t *second) const {
148
+ uint64_t h1 = 1469598103934665603ull;
149
+ uint64_t h2 = 0x9e3779b97f4a7c15ull;
150
+ auto mix = [&h1, &h2](uint64_t value) {
151
+ h1 ^= value;
152
+ h1 *= 1099511628211ull;
153
+ h1 ^= h1 >> 32;
154
+ h2 += value + 0x9e3779b97f4a7c15ull;
155
+ h2 = (h2 << 27) | (h2 >> 37);
156
+ h2 *= 0xbf58476d1ce4e5b9ull;
157
+ };
158
+ auto mix_face = [&mix](const WebGLStencilFace &face) {
159
+ /* `ref` is a per-pass dynamic value, not part of the pipeline. */
160
+ mix((uint64_t)(uint32_t)face.func);
161
+ mix(face.value_mask);
162
+ mix(face.write_mask);
163
+ mix((uint64_t)(uint32_t)face.fail);
164
+ mix((uint64_t)(uint32_t)face.zfail);
165
+ mix((uint64_t)(uint32_t)face.zpass);
166
+ };
167
+
168
+ mix(state_.blend ? 1u : 0u);
169
+ mix((uint64_t)(uint32_t)state_.blend_src_rgb);
170
+ mix((uint64_t)(uint32_t)state_.blend_dst_rgb);
171
+ mix((uint64_t)(uint32_t)state_.blend_src_alpha);
172
+ mix((uint64_t)(uint32_t)state_.blend_dst_alpha);
173
+ mix((uint64_t)(uint32_t)state_.blend_eq_rgb);
174
+ mix((uint64_t)(uint32_t)state_.blend_eq_alpha);
175
+ mix(state_.depth_test ? 1u : 0u);
176
+ mix(state_.depth_write ? 1u : 0u);
177
+ mix((uint64_t)(uint32_t)state_.depth_func);
178
+ mix(state_.stencil ? 1u : 0u);
179
+ mix_face(state_.stencil_front);
180
+ mix_face(state_.stencil_back);
181
+ mix(state_.cull ? 1u : 0u);
182
+ mix((uint64_t)(uint32_t)state_.cull_face);
183
+ mix((uint64_t)(uint32_t)state_.front_face);
184
+ for (bool channel : state_.color_mask) mix(channel ? 1u : 0u);
185
+ mix((uint64_t)(uint32_t)state_.color_target_count);
186
+ for (int i = 0; i < state_.color_target_count; i++) {
187
+ mix((uint64_t)(uint32_t)state_.color_formats[i]);
188
+ }
189
+ mix((uint64_t)(uint32_t)state_.depth_format);
190
+
191
+ *first = h1;
192
+ *second = h2;
193
+ }
194
+
195
+ void WebGLContext::clear_draw_cache() {
196
+ draw_setups_.clear();
197
+ last_setup_ = nullptr;
198
+ last_pipeline_ = nullptr;
199
+ last_setup_pass_ = 0;
200
+ }
201
+
202
+ const WebGLDrawSetup *WebGLContext::prepare_draw(const WebGLDrawKey &key) {
203
+ auto found = draw_setups_.find(key);
204
+ if (found != draw_setups_.end()) return &found->second;
205
+ WebGLProgram *program_ = program(key.program);
206
+ if (program_ == nullptr) {
207
+ set_error(GL_INVALID_OPERATION);
208
+ return nullptr;
209
+ }
210
+ /* A page that cycles through many states would otherwise grow the table
211
+ * without bound; clearing only costs one rebuild per live state. */
212
+ if (draw_setups_.size() >= 256) clear_draw_cache();
213
+
214
+ WebGLDrawSetup setup;
215
+ setup.attributes.reserve(program_->draw_attribs.size());
216
+ setup.buffers.reserve(program_->draw_attribs.size());
217
+ setup.bindings.reserve(program_->draw_attribs.size());
218
+
219
+ for (const WebGLDrawAttribPlan &plan : program_->draw_attribs) {
220
+ if (plan.location < 0 || plan.location >= kWebGLMaxAttribs) continue;
221
+ WebGLAttribState &attrib = attribs_[plan.location];
222
+ const int slots = matrix_slots(plan.type);
223
+ const int type_components = components_of_type(plan.type);
224
+ const int components = slots > 1 ? slots : (type_components > 4 ? 4 : type_components);
225
+ const uint32_t slot = (uint32_t)setup.buffers.size();
226
+
227
+ SDL_GPUVertexBufferDescription description;
228
+ memset(&description, 0, sizeof(description));
229
+ description.slot = slot;
230
+ description.input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX;
231
+ SDL_GPUBufferBinding binding;
232
+ memset(&binding, 0, sizeof(binding));
233
+
234
+ if (!attrib.enabled) {
235
+ /* A disabled attribute is a constant read from a persistent 16-byte
236
+ * buffer, not a fresh GPU allocation per draw. */
237
+ SDL_GPUBuffer *constant = constant_attrib_buffer(plan.location);
238
+ if (constant == nullptr) return nullptr;
239
+ binding.buffer = constant;
240
+ description.pitch = 0;
241
+ for (int column = 0; column < slots; column++) {
242
+ SDL_GPUVertexAttribute attribute;
243
+ memset(&attribute, 0, sizeof(attribute));
244
+ attribute.location = (Uint32)(plan.location + column);
245
+ attribute.buffer_slot = slot;
246
+ attribute.format = float_format(components);
247
+ attribute.offset = 0;
248
+ setup.attributes.push_back(attribute);
249
+ }
250
+ setup.buffers.push_back(description);
251
+ setup.bindings.push_back(binding);
252
+ continue;
253
+ }
254
+
255
+ WebGLBuffer *buffer_ = buffer(attrib.buffer);
256
+ if (buffer_ == nullptr || buffer_->data.empty()) {
257
+ set_error(GL_INVALID_OPERATION);
258
+ return nullptr;
259
+ }
260
+ SDL_GPUVertexElementFormat format = attrib.integer
261
+ ? int_format_of(attrib.size, attrib.type)
262
+ : format_of(attrib.size, attrib.type, attrib.normalized);
263
+ if (format == SDL_GPU_VERTEXELEMENTFORMAT_INVALID) {
264
+ set_error(GL_INVALID_OPERATION);
265
+ return nullptr;
266
+ }
267
+ description.input_rate = attrib.divisor > 0 ? SDL_GPU_VERTEXINPUTRATE_INSTANCE
268
+ : SDL_GPU_VERTEXINPUTRATE_VERTEX;
269
+ const int stride =
270
+ attrib.stride > 0 ? attrib.stride : components * type_size(attrib.type) * slots;
271
+ description.pitch = (Uint32)stride;
272
+ SDL_GPUBuffer *gpu = ensure_buffer_gpu(buffer_);
273
+ if (gpu == nullptr) return nullptr;
274
+ binding.buffer = gpu;
275
+ for (int column = 0; column < slots; column++) {
276
+ SDL_GPUVertexAttribute attribute;
277
+ memset(&attribute, 0, sizeof(attribute));
278
+ attribute.location = (Uint32)(plan.location + column);
279
+ attribute.buffer_slot = slot;
280
+ attribute.format = format;
281
+ attribute.offset = (Uint32)(attrib.offset + column * components * type_size(attrib.type));
282
+ setup.attributes.push_back(attribute);
283
+ }
284
+ setup.buffers.push_back(description);
285
+ setup.bindings.push_back(binding);
286
+ }
287
+
288
+ if (key.index_size > 0 && key.element_buffer != 0) {
289
+ WebGLBuffer *element = buffer(key.element_buffer);
290
+ if (element == nullptr || element->data.empty()) {
291
+ set_error(GL_INVALID_OPERATION);
292
+ return nullptr;
293
+ }
294
+ setup.index_buffer = ensure_buffer_gpu(element);
295
+ if (setup.index_buffer == nullptr) return nullptr;
296
+ }
297
+
298
+ /* The pipeline depends on the vertex layout and fixed-function state, not on
299
+ * which GPU buffer happens to hold the data (a buffer can be recreated after
300
+ * a grow) nor on the element-buffer binding. Normalise those fields out so a
301
+ * dynamic buffer does not create a fresh pipeline. */
302
+ WebGLDrawKey pipeline_key = key;
303
+ pipeline_key.gpu_version = 0;
304
+ pipeline_key.element_buffer = 0;
305
+ auto cached = program_->pipeline_cache.find(pipeline_key);
306
+ if (cached != program_->pipeline_cache.end()) {
307
+ setup.pipeline = cached->second;
308
+ } else {
309
+ setup.pipeline = create_pipeline(program_, key.mode, (int)setup.attributes.size(),
310
+ (int)setup.buffers.size(), setup.attributes.data(),
311
+ setup.buffers.data());
312
+ if (setup.pipeline == nullptr) return nullptr;
313
+ program_->pipeline_cache[pipeline_key] = setup.pipeline;
314
+ program_->pipelines.push_back(setup.pipeline);
315
+ }
316
+
317
+ auto inserted = draw_setups_.emplace(key, std::move(setup));
318
+ return &inserted.first->second;
319
+ }
320
+
321
+ } // namespace xtgui
@@ -0,0 +1,130 @@
1
+ /*
2
+ * xbintsc GUI engine — the fixed-function WebGL state a pipeline depends on.
3
+ *
4
+ * Split out of `webgl.h` to keep that header under the repository's
5
+ * file-length budget.
6
+ */
7
+ #ifndef XT_GUI_WEBGL_DRAW_STATE_H
8
+ #define XT_GUI_WEBGL_DRAW_STATE_H
9
+
10
+ #include <cstddef>
11
+ #include <cstdint>
12
+ #include <string>
13
+ #include <vector>
14
+
15
+ #include <SDL3/SDL.h>
16
+
17
+ #include "webgl_constants.h"
18
+
19
+ namespace xtgui {
20
+
21
+ /** One face's stencil test and update state. */
22
+ struct WebGLStencilFace {
23
+ int func = GL_ALWAYS;
24
+ int ref = 0;
25
+ uint32_t value_mask = 0xFFFFFFFFu; // compare mask
26
+ uint32_t write_mask = 0xFFFFFFFFu;
27
+ int fail = GL_KEEP;
28
+ int zfail = GL_KEEP;
29
+ int zpass = GL_KEEP;
30
+ };
31
+
32
+ /** The fixed-function state a pipeline depends on. */
33
+ struct WebGLDrawState {
34
+ bool blend = false;
35
+ int blend_src_rgb = GL_ONE;
36
+ int blend_dst_rgb = GL_ZERO;
37
+ int blend_src_alpha = GL_ONE;
38
+ int blend_dst_alpha = GL_ZERO;
39
+ int blend_eq_rgb = GL_FUNC_ADD;
40
+ int blend_eq_alpha = GL_FUNC_ADD;
41
+ bool depth_test = false;
42
+ bool depth_write = true;
43
+ int depth_func = GL_LESS;
44
+ bool stencil = false;
45
+ WebGLStencilFace stencil_front;
46
+ WebGLStencilFace stencil_back;
47
+ bool cull = false;
48
+ int cull_face = GL_BACK;
49
+ int front_face = GL_CCW;
50
+ bool color_mask[4] = {true, true, true, true};
51
+ /** Attachment formats of the bound target; pipelines are format-specific. */
52
+ int color_formats[4] = {
53
+ SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM,
54
+ SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM};
55
+ int color_target_count = 1;
56
+ int depth_format = SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT;
57
+ };
58
+
59
+ /**
60
+ * Identity of one prepared draw. Everything a draw's pipeline, vertex layout
61
+ * and GPU buffer handles depend on is either an exact integer here or folds
62
+ * into `state_hash`/`state_hash2` (a 128-bit hash of `WebGLDrawState`). Two
63
+ * independent hashes keep a collision in the same place a 128-bit hash would
64
+ * have it — the state struct has no padding-sensitive fields to compare.
65
+ */
66
+ struct WebGLDrawKey {
67
+ uint32_t program = 0;
68
+ int mode = 0; // GL primitive mode
69
+ int index_size = 0; // 0 = drawArrays, otherwise index element bytes
70
+ uint32_t element_buffer = 0; // bound GL_ELEMENT_ARRAY_BUFFER, 0 = none
71
+ uint64_t attribs_version = 0;
72
+ uint64_t gpu_version = 0;
73
+ uint64_t framebuffer_version = 0;
74
+ uint64_t state_hash = 0;
75
+ uint64_t state_hash2 = 0;
76
+
77
+ bool operator==(const WebGLDrawKey &other) const {
78
+ return program == other.program && mode == other.mode && index_size == other.index_size &&
79
+ element_buffer == other.element_buffer && attribs_version == other.attribs_version &&
80
+ gpu_version == other.gpu_version &&
81
+ framebuffer_version == other.framebuffer_version && state_hash == other.state_hash &&
82
+ state_hash2 == other.state_hash2;
83
+ }
84
+ };
85
+
86
+ struct WebGLDrawKeyHash {
87
+ size_t operator()(const WebGLDrawKey &key) const {
88
+ uint64_t hash = 1469598103934665603ull;
89
+ auto mix = [&hash](uint64_t value) {
90
+ hash ^= value;
91
+ hash *= 1099511628211ull;
92
+ hash ^= hash >> 29;
93
+ };
94
+ mix(key.program);
95
+ mix((uint64_t)(uint32_t)key.mode);
96
+ mix((uint64_t)(uint32_t)key.index_size);
97
+ mix(key.element_buffer);
98
+ mix(key.attribs_version);
99
+ mix(key.gpu_version);
100
+ mix(key.framebuffer_version);
101
+ mix(key.state_hash);
102
+ mix(key.state_hash2);
103
+ return (size_t)hash;
104
+ }
105
+ };
106
+
107
+ /**
108
+ * One active vertex attribute, resolved once at link time: `location` is the
109
+ * base attribute location and `type` the GLSL type name the draw needs to size
110
+ * the vertex layout. Keeping this out of the draw path removes the per-draw
111
+ * `attribs` scan and the `name -> location` string hash lookup.
112
+ */
113
+ struct WebGLDrawAttribPlan {
114
+ int location = -1;
115
+ std::string type;
116
+ };
117
+
118
+ /** A pipeline and the vertex-layout/binding set the draw prepared for it. A
119
+ * cache hit is a bind (when it changed) and a draw. */
120
+ struct WebGLDrawSetup {
121
+ SDL_GPUGraphicsPipeline *pipeline = nullptr;
122
+ std::vector<SDL_GPUVertexAttribute> attributes;
123
+ std::vector<SDL_GPUVertexBufferDescription> buffers;
124
+ std::vector<SDL_GPUBufferBinding> bindings;
125
+ SDL_GPUBuffer *index_buffer = nullptr;
126
+ };
127
+
128
+ } // namespace xtgui
129
+
130
+ #endif /* XT_GUI_WEBGL_DRAW_STATE_H */
@@ -126,6 +126,9 @@ struct GlslLinkLayout {
126
126
  std::vector<GlslUniformDecl> block_members;
127
127
  };
128
128
 
129
+ /** Expand three.js's `#pragma unroll_loop_start` loops into constant indices. */
130
+ std::string unroll_glsl_pragmas(const std::string &source);
131
+
129
132
  /** Scan `source` for the declarations WebGL needs to know about. */
130
133
  void scan_glsl(const std::string &source, bool fragment, GlslShaderInfo *info);
131
134
 
@@ -425,14 +425,9 @@ void scan_glsl(const std::string &source, bool fragment, GlslShaderInfo *info) {
425
425
  info->legacy_fragment_output = true;
426
426
  continue;
427
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
- }
428
+ /* `gl_FragDepth`/`gl_FragDepthEXT`: the Vulkan SPIR-V path emits
429
+ * `DepthReplacing`, and SPIRV-Cross turns that into `[[depth(any)]]` for
430
+ * Metal, so a fragment shader may write the depth attachment directly. */
436
431
  /* Declarations inside a definitively false preprocessor branch never reach
437
432
  * the compiler; rewriting them would move their types/macros (and array
438
433
  * sizes such as `MORPHTARGETS_COUNT`) outside that branch. */