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
@@ -32,15 +32,13 @@
32
32
 
33
33
  #include "webgl_constants.h"
34
34
  #include "webgl2_constants.h"
35
+ #include "webgl_draw_state.h"
35
36
  #include "webgl_glsl.h"
37
+ #include "webgl_objects.h"
36
38
  #include "webgl_uniform_block.h"
37
39
 
38
40
  namespace xtgui {
39
41
 
40
- constexpr int kWebGLMaxAttribs = 16;
41
- constexpr int kWebGLMaxTextureUnits = 16;
42
- constexpr int kWebGLMaxDrawBuffers = 4;
43
-
44
42
  struct WebGLShader {
45
43
  uint32_t id = 0;
46
44
  int type = 0; // GL_VERTEX_SHADER / GL_FRAGMENT_SHADER
@@ -56,6 +54,10 @@ struct WebGLUniform {
56
54
  std::string name;
57
55
  GlslBlockMember layout;
58
56
  bool sampler = false;
57
+ /** `sampler2DShadow`/`samplerCubeShadow`/`sampler2DArrayShadow`: SDL_GPU
58
+ * binds a comparison sampler, and the GLSL `texture()` call performs the
59
+ * hardware depth compare. */
60
+ bool compare = false;
59
61
  int unit = -1;
60
62
  };
61
63
 
@@ -68,6 +70,7 @@ struct WebGLProgram {
68
70
  std::vector<WebGLUniform> uniforms;
69
71
  std::vector<WebGLUniformBlock> blocks;
70
72
  std::vector<GlslUniformDecl> attribs;
73
+ std::vector<WebGLDrawAttribPlan> draw_attribs;
71
74
  std::unordered_map<std::string, int> attrib_locations; // name -> location
72
75
  std::unordered_map<std::string, int> attrib_bindings; // bindAttribLocation overrides
73
76
  std::unordered_map<std::string, int> uniform_index; // name -> uniforms[] index
@@ -81,145 +84,7 @@ struct WebGLProgram {
81
84
  SDL_GPUShader *vs_shader = nullptr;
82
85
  SDL_GPUShader *fs_shader = nullptr;
83
86
  std::vector<SDL_GPUGraphicsPipeline *> pipelines;
84
- std::unordered_map<std::string, SDL_GPUGraphicsPipeline *> pipeline_cache;
85
- };
86
-
87
- struct WebGLBuffer {
88
- uint32_t id = 0;
89
- std::vector<uint8_t> data;
90
- SDL_GPUBuffer *gpu = nullptr;
91
- uint32_t capacity = 0;
92
- bool dirty = true;
93
- /** `bufferData` usage hint, reported by `getBufferParameter`. */
94
- int usage = GL_STATIC_DRAW;
95
- };
96
-
97
- struct WebGLTexture {
98
- uint32_t id = 0;
99
- int width = 0;
100
- int height = 0;
101
- int levels = 1;
102
- /** The binding target this texture was created for. */
103
- int target = GL_TEXTURE_2D;
104
- /** Layers for a cube/2D-array texture, depth for a 3D texture. */
105
- int layers = 1;
106
- SDL_GPUTexture *gpu = nullptr;
107
- SDL_GPUTextureFormat format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
108
- /** Depth(-stencil) formats can only be depth attachments. */
109
- bool depth = false;
110
- /** Block-compressed textures are sample-only, with one mip level. */
111
- bool compressed = false;
112
- /** Usage flags the current `gpu` texture was created with. */
113
- uint32_t usage = 0;
114
- bool as_target = false; // bound as a colour attachment
115
- bool as_depth_target = false; // bound as a depth attachment
116
- bool has_mipmaps = false;
117
- int min_filter = 0;
118
- int mag_filter = 0;
119
- int wrap_s = 0;
120
- int wrap_t = 0;
121
- SDL_GPUSampler *sampler = nullptr;
122
- bool dirty = false;
123
- bool defined = false;
124
- };
125
-
126
- struct WebGLRenderbuffer {
127
- uint32_t id = 0;
128
- int width = 0;
129
- int height = 0;
130
- bool stencil = false;
131
- SDL_GPUTexture *depth = nullptr;
132
- };
133
-
134
- struct WebGLFramebuffer {
135
- uint32_t id = 0;
136
- uint32_t color_textures[kWebGLMaxDrawBuffers] = {};
137
- /** Layer rendered to for an array/cube colour attachment. */
138
- int color_layers[kWebGLMaxDrawBuffers] = {};
139
- /** `COLOR_ATTACHMENTi` or `NONE` per draw-buffer slot. */
140
- int draw_buffers[kWebGLMaxDrawBuffers] = {GL_COLOR_ATTACHMENT0, GL_NONE, GL_NONE, GL_NONE};
141
- uint32_t depth_texture = 0;
142
- uint32_t depth_renderbuffer = 0;
143
- int width = 0;
144
- int height = 0;
145
- SDL_GPUTexture *depth = nullptr; // owned when no renderbuffer/texture supplies one
146
- };
147
-
148
- /** Per-attribute array state. */
149
- struct WebGLAttribState {
150
- bool enabled = false;
151
- bool integer = false; // `vertexAttribIPointer` rather than `vertexAttribPointer`
152
- uint32_t buffer = 0;
153
- int size = 4;
154
- int type = GL_FLOAT;
155
- bool normalized = false;
156
- int stride = 0;
157
- int offset = 0;
158
- int divisor = 0; // 0 = per vertex, 1 = per instance
159
- float current[4] = {0.0f, 0.0f, 0.0f, 1.0f};
160
- };
161
-
162
- /** A WebGL 2 vertex-array object; binding one restores this state. */
163
- struct WebGLVertexArray {
164
- uint32_t id = 0;
165
- WebGLAttribState attribs[kWebGLMaxAttribs];
166
- uint32_t element_buffer = 0;
167
- };
168
-
169
- /** A WebGL 2 sampler object, bound to a texture unit with `bindSampler`. */
170
- struct WebGLSampler {
171
- uint32_t id = 0;
172
- int min_filter = 0;
173
- int mag_filter = 0;
174
- int wrap_s = 0;
175
- int wrap_t = 0;
176
- SDL_GPUSampler *gpu = nullptr;
177
- };
178
-
179
- struct WebGLQuery {
180
- uint32_t id = 0;
181
- };
182
-
183
- struct WebGLSync {
184
- uint32_t id = 0;
185
- };
186
-
187
- /** One face's stencil test and update state. */
188
- struct WebGLStencilFace {
189
- int func = GL_ALWAYS;
190
- int ref = 0;
191
- uint32_t value_mask = 0xFFFFFFFFu; // compare mask
192
- uint32_t write_mask = 0xFFFFFFFFu;
193
- int fail = GL_KEEP;
194
- int zfail = GL_KEEP;
195
- int zpass = GL_KEEP;
196
- };
197
-
198
- /** The fixed-function state a pipeline depends on. */
199
- struct WebGLDrawState {
200
- bool blend = false;
201
- int blend_src_rgb = GL_ONE;
202
- int blend_dst_rgb = GL_ZERO;
203
- int blend_src_alpha = GL_ONE;
204
- int blend_dst_alpha = GL_ZERO;
205
- int blend_eq_rgb = GL_FUNC_ADD;
206
- int blend_eq_alpha = GL_FUNC_ADD;
207
- bool depth_test = false;
208
- bool depth_write = true;
209
- int depth_func = GL_LESS;
210
- bool stencil = false;
211
- WebGLStencilFace stencil_front;
212
- WebGLStencilFace stencil_back;
213
- bool cull = false;
214
- int cull_face = GL_BACK;
215
- int front_face = GL_CCW;
216
- bool color_mask[4] = {true, true, true, true};
217
- /** Attachment formats of the bound target; pipelines are format-specific. */
218
- int color_formats[kWebGLMaxDrawBuffers] = {
219
- SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM,
220
- SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM};
221
- int color_target_count = 1;
222
- int depth_format = SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT;
87
+ std::unordered_map<WebGLDrawKey, SDL_GPUGraphicsPipeline *, WebGLDrawKeyHash> pipeline_cache;
223
88
  };
224
89
 
225
90
  class WebGLContext {
@@ -416,6 +281,10 @@ class WebGLContext {
416
281
  void bind_framebuffer(int target, uint32_t id);
417
282
  uint32_t bound_read_framebuffer_id() const { return read_framebuffer_; }
418
283
  bool blit_framebuffer(const int source[4], const int destination[4], int mask, int filter);
284
+ /** Copy the depth attachment with a sampling pass (SDL_GPU's blit only copies
285
+ * colour); stencil bits are not copied. */
286
+ bool blit_depth(uint32_t source_framebuffer, uint32_t destination_framebuffer,
287
+ const int source[4], const int destination[4]);
419
288
  bool framebuffer_texture(uint32_t framebuffer, uint32_t texture,
420
289
  int attachment = GL_COLOR_ATTACHMENT0);
421
290
  bool framebuffer_texture_layer(uint32_t framebuffer, uint32_t texture, int attachment,
@@ -476,6 +345,7 @@ class WebGLContext {
476
345
  /** The 1×1 texture a scissored colour clear blits from. */
477
346
  SDL_GPUTexture *clear_color_texture();
478
347
  SDL_GPUTexture *framebuffer_color(uint32_t id);
348
+ SDL_GPUTexture *framebuffer_depth(uint32_t id);
479
349
  void framebuffer_size(uint32_t id, int *width, int *height);
480
350
  /** Release every GPU handle this context owns, keeping the CPU-side object
481
351
  * table. Used by the destructor and by `lose_context`. */
@@ -490,7 +360,19 @@ class WebGLContext {
490
360
  int draw_color_layer(int slot);
491
361
  SDL_GPUTexture *ensure_texture_gpu(WebGLTexture *texture, bool as_target);
492
362
  SDL_GPUSampler *sampler_for(WebGLTexture *texture);
363
+ SDL_GPUSampler *sampler_for_compare(WebGLTexture *texture);
493
364
  SDL_GPUBuffer *ensure_buffer_gpu(WebGLBuffer *buffer);
365
+ /** Upload `size` bytes at `offset` into an existing GPU buffer, ending the
366
+ * open render pass first (a copy pass cannot run inside one). */
367
+ bool upload_buffer_range(SDL_GPUBuffer *gpu, uint32_t offset, const void *bytes, uint32_t size);
368
+ /** The persistent GPU buffer for a disabled attribute's `current[]` value. */
369
+ SDL_GPUBuffer *constant_attrib_buffer(int index);
370
+ /** Hash the current fixed-function state into a draw key. */
371
+ void draw_state_hash(uint64_t *first, uint64_t *second) const;
372
+ /** The prepared pipeline/layout for `key`, building and caching it on miss. */
373
+ const WebGLDrawSetup *prepare_draw(const WebGLDrawKey &key);
374
+ /** Drop every prepared draw and pipeline key (state/layout changed shape). */
375
+ void clear_draw_cache();
494
376
  void ensure_default_target();
495
377
  void rebuild_default_target();
496
378
  bool ensure_fallback();
@@ -502,11 +384,10 @@ class WebGLContext {
502
384
  SDL_GPUSampler *sampler_gpu(WebGLSampler *sampler);
503
385
  uint32_t buffer_for_target(int target) const;
504
386
  void release_program_pipelines(WebGLProgram *program);
505
- SDL_GPUGraphicsPipeline *create_pipeline(WebGLProgram *program, int mode, int index_type,
387
+ SDL_GPUGraphicsPipeline *create_pipeline(WebGLProgram *program, int mode,
506
388
  int attribute_count, int buffer_count,
507
389
  const SDL_GPUVertexAttribute *attrs,
508
- const SDL_GPUVertexBufferDescription *buffers,
509
- const std::string &key);
390
+ const SDL_GPUVertexBufferDescription *buffers);
510
391
 
511
392
  uint64_t id_ = 0;
512
393
  int width_ = 0;
@@ -518,6 +399,23 @@ class WebGLContext {
518
399
  SDL_GPURenderPass *pass_ = nullptr;
519
400
  bool pass_targets_default_ = true;
520
401
  uint32_t pass_framebuffer_ = 0;
402
+ /** Bumped every time a render pass is actually opened: bindings recorded in
403
+ * a previous pass do not survive an intervening upload. */
404
+ uint64_t pass_serial_ = 0;
405
+ /** State-shape versions. A mutation that changes the vertex layout, the GPU
406
+ * buffer handles or the attachment formats bumps one of these, which
407
+ * invalidates every prepared draw. */
408
+ uint64_t attribs_version_ = 1;
409
+ uint64_t gpu_version_ = 1;
410
+ uint64_t framebuffer_version_ = 1;
411
+ std::unordered_map<WebGLDrawKey, WebGLDrawSetup, WebGLDrawKeyHash> draw_setups_;
412
+ SDL_GPUGraphicsPipeline *last_pipeline_ = nullptr;
413
+ const WebGLDrawSetup *last_setup_ = nullptr;
414
+ uint64_t last_setup_pass_ = 0;
415
+ /** Pipelines for internal helper draws (depth blit), keyed by their state. */
416
+ std::unordered_map<std::string, SDL_GPUGraphicsPipeline *> internal_pipelines_;
417
+ /** Scratch for the sampler bindings of one draw. */
418
+ std::vector<SDL_GPUTextureSamplerBinding> sampler_bindings_;
521
419
 
522
420
  SDL_GPUTexture *color_ = nullptr;
523
421
  SDL_GPUTexture *depth_ = nullptr;
@@ -528,6 +426,10 @@ class WebGLContext {
528
426
  /** Internal program/buffer for a scissored depth/stencil clear. */
529
427
  uint32_t clear_program_ = 0;
530
428
  uint32_t clear_buffer_ = 0;
429
+ /** Internal program/buffer/sampler for a depth blit. */
430
+ uint32_t blit_program_ = 0;
431
+ uint32_t blit_buffer_ = 0;
432
+ SDL_GPUSampler *blit_sampler_ = nullptr;
531
433
 
532
434
  float clear_color_[4] = {0.0f, 0.0f, 0.0f, 0.0f};
533
435
  float clear_depth_ = 1.0f;
@@ -19,10 +19,21 @@ namespace xtgui {
19
19
 
20
20
  bool WebGLContext::blit_framebuffer(const int source[4], const int destination[4], int mask,
21
21
  int filter) {
22
- if ((mask & GL_COLOR_BUFFER_BIT) == 0) return true;
22
+ const bool color_bit = (mask & GL_COLOR_BUFFER_BIT) != 0;
23
+ const bool depth_bit = (mask & GL_DEPTH_BUFFER_BIT) != 0;
24
+ if (!color_bit && !depth_bit) return true;
23
25
  end_pass();
24
26
  ensure_command();
25
27
  if (cmd_ == nullptr) return false;
28
+ if (depth_bit) {
29
+ /* SDL_GPU's blit API is colour-only; the depth copy is a sampling pass that
30
+ * writes `gl_FragDepth`. Stencil bits have no sampleable representation and
31
+ * are left untouched. */
32
+ blit_depth(read_framebuffer_, bound_framebuffer_, source, destination);
33
+ end_pass();
34
+ ensure_command();
35
+ }
36
+ if (!color_bit) return true;
26
37
  SDL_GPUTexture *source_texture = framebuffer_color(read_framebuffer_);
27
38
  SDL_GPUTexture *destination_texture = framebuffer_color(bound_framebuffer_);
28
39
  if (source_texture == nullptr || destination_texture == nullptr ||
@@ -0,0 +1,270 @@
1
+ /*
2
+ * xbintsc GUI engine — `blitFramebuffer` depth copies.
3
+ *
4
+ * SDL_GPU's blit copies colour attachments only. A depth attachment can still
5
+ * be sampled as an ordinary texture, so a full-screen quad samples the source
6
+ * depth and writes it back with `gl_FragDepth` while colour writes are masked
7
+ * off. Stencil is not sampleable through SDL_GPU and is left untouched.
8
+ *
9
+ * The internal program, quad buffer and sampler are ordinary engine objects and
10
+ * live for the context's lifetime. The caller's GL state is saved/restored
11
+ * around the draw.
12
+ */
13
+
14
+ #include "webgl.h"
15
+
16
+ #include <algorithm>
17
+ #include <cstring>
18
+ #include <string>
19
+
20
+ #include "gui_internal.h"
21
+
22
+ namespace xtgui {
23
+ namespace {
24
+
25
+ const char *kBlitVertexShader =
26
+ "#version 300 es\n"
27
+ "uniform highp vec4 u_dst;\n"
28
+ "uniform highp vec4 u_uv;\n"
29
+ "in highp vec2 a_pos;\n"
30
+ "out highp vec2 v_uv;\n"
31
+ "void main() {\n"
32
+ " v_uv = mix(u_uv.xy, u_uv.zw, a_pos);\n"
33
+ " gl_Position = vec4(mix(u_dst.xy, u_dst.zw, a_pos), 0.0, 1.0);\n"
34
+ "}\n";
35
+
36
+ const char *kBlitFragmentShader =
37
+ "#version 300 es\n"
38
+ "precision highp float;\n"
39
+ "uniform highp vec4 u_dst;\n"
40
+ "uniform highp sampler2D u_depth;\n"
41
+ "in highp vec2 v_uv;\n"
42
+ "out highp vec4 fragColor;\n"
43
+ "void main() {\n"
44
+ " fragColor = vec4(1.0);\n"
45
+ " gl_FragDepth = texture(u_depth, v_uv).r;\n"
46
+ "}\n";
47
+
48
+ } // namespace
49
+
50
+ bool WebGLContext::blit_depth(uint32_t source_framebuffer, uint32_t destination_framebuffer,
51
+ const int source[4], const int destination[4]) {
52
+ if (guidev::g_device == nullptr) return false;
53
+ if (source_framebuffer == destination_framebuffer) return false;
54
+ /* A renderbuffer depth attachment is never created with SAMPLER usage. */
55
+ if (source_framebuffer != 0) {
56
+ WebGLFramebuffer *framebuffer = this->framebuffer(source_framebuffer);
57
+ if (framebuffer == nullptr || framebuffer->depth_texture == 0) return false;
58
+ }
59
+ if (destination_framebuffer != 0 && this->framebuffer(destination_framebuffer) == nullptr) {
60
+ return false;
61
+ }
62
+
63
+ int source_width = 0;
64
+ int source_height = 0;
65
+ int destination_width = 0;
66
+ int destination_height = 0;
67
+ framebuffer_size(source_framebuffer, &source_width, &source_height);
68
+ framebuffer_size(destination_framebuffer, &destination_width, &destination_height);
69
+ if (source_width <= 0 || source_height <= 0 || destination_width <= 0 ||
70
+ destination_height <= 0) {
71
+ return false;
72
+ }
73
+ int sx0 = source[0];
74
+ int sy0 = source[1];
75
+ int sx1 = source[2];
76
+ int sy1 = source[3];
77
+ int dx0 = destination[0];
78
+ int dy0 = destination[1];
79
+ int dx1 = destination[2];
80
+ int dy1 = destination[3];
81
+ if (sx1 < sx0) std::swap(sx0, sx1);
82
+ if (sy1 < sy0) std::swap(sy0, sy1);
83
+ if (dx1 < dx0) std::swap(dx0, dx1);
84
+ if (dy1 < dy0) std::swap(dy0, dy1);
85
+ sx0 = std::max(0, std::min(sx0, source_width));
86
+ sx1 = std::max(0, std::min(sx1, source_width));
87
+ sy0 = std::max(0, std::min(sy0, source_height));
88
+ sy1 = std::max(0, std::min(sy1, source_height));
89
+ dx0 = std::max(0, std::min(dx0, destination_width));
90
+ dx1 = std::max(0, std::min(dx1, destination_width));
91
+ dy0 = std::max(0, std::min(dy0, destination_height));
92
+ dy1 = std::max(0, std::min(dy1, destination_height));
93
+ if (sx1 <= sx0 || sy1 <= sy0 || dx1 <= dx0 || dy1 <= dy0) return true;
94
+ SDL_GPUTexture *source_depth = framebuffer_depth(source_framebuffer);
95
+ SDL_GPUTexture *destination_depth = framebuffer_depth(destination_framebuffer);
96
+ if (source_depth == nullptr || destination_depth == nullptr ||
97
+ source_depth == destination_depth) {
98
+ return false;
99
+ }
100
+
101
+ /* Build the internal program/buffer on first use. */
102
+ if (blit_program_ == 0) {
103
+ const uint32_t vs = create_shader(GL_VERTEX_SHADER);
104
+ shader_source(vs, kBlitVertexShader);
105
+ const uint32_t fs = create_shader(GL_FRAGMENT_SHADER);
106
+ shader_source(fs, kBlitFragmentShader);
107
+ if (compile_shader(vs) && compile_shader(fs)) {
108
+ const uint32_t program_id = create_program();
109
+ attach_shader(program_id, vs);
110
+ attach_shader(program_id, fs);
111
+ bind_attrib_location(program_id, 0, "a_pos");
112
+ if (link_program(program_id)) {
113
+ blit_program_ = program_id;
114
+ } else {
115
+ delete_program(program_id);
116
+ }
117
+ }
118
+ if (blit_program_ == 0) {
119
+ delete_shader(vs);
120
+ delete_shader(fs);
121
+ return false;
122
+ }
123
+ }
124
+ if (blit_buffer_ == 0) {
125
+ blit_buffer_ = create_buffer();
126
+ bind_buffer(GL_ARRAY_BUFFER, blit_buffer_);
127
+ const float vertices[8] = {0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f};
128
+ buffer_data(blit_buffer_, (const uint8_t *)vertices, (uint32_t)sizeof(vertices),
129
+ GL_STATIC_DRAW);
130
+ }
131
+ if (blit_sampler_ == nullptr) {
132
+ SDL_GPUSamplerCreateInfo sampler_info;
133
+ memset(&sampler_info, 0, sizeof(sampler_info));
134
+ sampler_info.min_filter = SDL_GPU_FILTER_NEAREST;
135
+ sampler_info.mag_filter = SDL_GPU_FILTER_NEAREST;
136
+ sampler_info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST;
137
+ sampler_info.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
138
+ sampler_info.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
139
+ sampler_info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
140
+ sampler_info.max_lod = 0.0f;
141
+ blit_sampler_ = SDL_CreateGPUSampler(guidev::g_device, &sampler_info);
142
+ }
143
+ WebGLProgram *program = this->program(blit_program_);
144
+ WebGLBuffer *quad_buffer = buffer(blit_buffer_);
145
+ SDL_GPUBuffer *quad_gpu = quad_buffer != nullptr ? ensure_buffer_gpu(quad_buffer) : nullptr;
146
+ if (program == nullptr || program->vs_shader == nullptr || program->fs_shader == nullptr ||
147
+ quad_gpu == nullptr || blit_sampler_ == nullptr) {
148
+ return false;
149
+ }
150
+
151
+ /* NDC rectangle and source UVs. Source V is flipped because GL's origin is
152
+ * bottom-left while SDL_GPU's render targets and uploads are top-down. */
153
+ double dst[4] = {(double)dx0 / (double)destination_width * 2.0 - 1.0,
154
+ (double)dy0 / (double)destination_height * 2.0 - 1.0,
155
+ (double)dx1 / (double)destination_width * 2.0 - 1.0,
156
+ (double)dy1 / (double)destination_height * 2.0 - 1.0};
157
+ double uv[4] = {(double)sx0 / (double)source_width,
158
+ 1.0 - (double)sy0 / (double)source_height,
159
+ (double)sx1 / (double)source_width,
160
+ 1.0 - (double)sy1 / (double)source_height};
161
+
162
+ const WebGLDrawState saved_state = state_;
163
+ const uint32_t saved_program = current_program_;
164
+ state_.blend = false;
165
+ state_.cull = false;
166
+ state_.stencil = false;
167
+ state_.depth_test = true;
168
+ state_.depth_write = true;
169
+ state_.depth_func = GL_ALWAYS;
170
+ for (bool &channel : state_.color_mask) channel = false;
171
+ state_.color_target_count = 0;
172
+ for (int slot = 0; slot < kWebGLMaxDrawBuffers; slot++) {
173
+ if (draw_color_texture(slot) == nullptr) continue;
174
+ state_.color_formats[state_.color_target_count++] = draw_color_format(slot);
175
+ }
176
+ if (state_.color_target_count == 0) state_.color_target_count = 1;
177
+ state_.depth_format = target_depth_format();
178
+ current_program_ = blit_program_;
179
+
180
+ std::string key = "blit-depth/";
181
+ for (int i = 0; i < state_.color_target_count; i++) {
182
+ key += std::to_string(state_.color_formats[i]) + ",";
183
+ }
184
+ key += std::to_string(state_.depth_format);
185
+ SDL_GPUVertexAttribute attribute;
186
+ memset(&attribute, 0, sizeof(attribute));
187
+ attribute.location = 0;
188
+ attribute.buffer_slot = 0;
189
+ attribute.format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2;
190
+ attribute.offset = 0;
191
+ SDL_GPUVertexBufferDescription buffer_description;
192
+ memset(&buffer_description, 0, sizeof(buffer_description));
193
+ buffer_description.slot = 0;
194
+ buffer_description.pitch = 8;
195
+ buffer_description.input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX;
196
+ SDL_GPUGraphicsPipeline *pipeline = nullptr;
197
+ auto cached = internal_pipelines_.find(key);
198
+ if (cached != internal_pipelines_.end()) {
199
+ pipeline = cached->second;
200
+ } else {
201
+ pipeline = create_pipeline(program, GL_TRIANGLE_STRIP, 1, 1, &attribute, &buffer_description);
202
+ if (pipeline != nullptr) internal_pipelines_[key] = pipeline;
203
+ }
204
+ bool ok = pipeline != nullptr;
205
+ if (ok) {
206
+ begin_pass(false, false, false);
207
+ ok = pass_ != nullptr;
208
+ }
209
+ if (ok) {
210
+ SDL_BindGPUGraphicsPipeline(pass_, pipeline);
211
+ SDL_GPUBufferBinding binding;
212
+ memset(&binding, 0, sizeof(binding));
213
+ binding.buffer = quad_gpu;
214
+ SDL_BindGPUVertexBuffers(pass_, 0, &binding, 1);
215
+ SDL_Rect scissor;
216
+ scissor.x = 0;
217
+ scissor.y = 0;
218
+ scissor.w = destination_width;
219
+ scissor.h = destination_height;
220
+ if (scissor_test_ && scissor_box_[2] > 0 && scissor_box_[3] > 0) {
221
+ scissor.x = scissor_box_[0];
222
+ scissor.y = destination_height - (scissor_box_[1] + scissor_box_[3]);
223
+ scissor.w = scissor_box_[2];
224
+ scissor.h = scissor_box_[3];
225
+ }
226
+ if (scissor.x < 0) {
227
+ scissor.w += scissor.x;
228
+ scissor.x = 0;
229
+ }
230
+ if (scissor.y < 0) {
231
+ scissor.h += scissor.y;
232
+ scissor.y = 0;
233
+ }
234
+ if (scissor.x + scissor.w > destination_width) scissor.w = destination_width - scissor.x;
235
+ if (scissor.y + scissor.h > destination_height) scissor.h = destination_height - scissor.y;
236
+ if (scissor.w < 0) scissor.w = 0;
237
+ if (scissor.h < 0) scissor.h = 0;
238
+ SDL_SetGPUScissor(pass_, &scissor);
239
+
240
+ const int dst_location = uniform_location(blit_program_, "u_dst");
241
+ const int uv_location = uniform_location(blit_program_, "u_uv");
242
+ if (dst_location >= 0) uniform_floats(blit_program_, dst_location, 4, dst, 1);
243
+ if (uv_location >= 0) uniform_floats(blit_program_, uv_location, 4, uv, 1);
244
+ if (program->block_size > 0) {
245
+ if (program->vs_has_block) {
246
+ SDL_PushGPUVertexUniformData(cmd_, 0, program->block.data(),
247
+ (Uint32)program->block.size());
248
+ }
249
+ if (program->fs_has_block) {
250
+ SDL_PushGPUFragmentUniformData(cmd_, 0, program->block.data(),
251
+ (Uint32)program->block.size());
252
+ }
253
+ }
254
+ SDL_GPUTextureSamplerBinding sampler_binding;
255
+ sampler_binding.texture = source_depth;
256
+ sampler_binding.sampler = blit_sampler_;
257
+ SDL_BindGPUFragmentSamplers(pass_, 0, &sampler_binding, 1);
258
+ SDL_DrawGPUPrimitives(pass_, 4, 1, 0, 0);
259
+ }
260
+
261
+ state_ = saved_state;
262
+ current_program_ = saved_program;
263
+ /* This pass bound the blit pipeline directly, outside the prepared-draw
264
+ * cache; a later draw in the same pass must bind its own pipeline again. */
265
+ last_setup_ = nullptr;
266
+ last_pipeline_ = nullptr;
267
+ return ok;
268
+ }
269
+
270
+ } // namespace xtgui
@@ -58,6 +58,27 @@ SDL_GPUSamplerAddressMode address_of(int wrap) {
58
58
  }
59
59
  }
60
60
 
61
+ SDL_GPUCompareOp compare_of(int func) {
62
+ switch (func) {
63
+ case GL_NEVER:
64
+ return SDL_GPU_COMPAREOP_NEVER;
65
+ case GL_LESS:
66
+ return SDL_GPU_COMPAREOP_LESS;
67
+ case GL_EQUAL:
68
+ return SDL_GPU_COMPAREOP_EQUAL;
69
+ case GL_LEQUAL:
70
+ return SDL_GPU_COMPAREOP_LESS_OR_EQUAL;
71
+ case GL_GREATER:
72
+ return SDL_GPU_COMPAREOP_GREATER;
73
+ case GL_NOTEQUAL:
74
+ return SDL_GPU_COMPAREOP_NOT_EQUAL;
75
+ case GL_GEQUAL:
76
+ return SDL_GPU_COMPAREOP_GREATER_OR_EQUAL;
77
+ default:
78
+ return SDL_GPU_COMPAREOP_ALWAYS;
79
+ }
80
+ }
81
+
61
82
  } // namespace
62
83
 
63
84
  /* -- textures ------------------------------------------------------------ */
@@ -111,6 +132,9 @@ void WebGLContext::delete_texture(uint32_t id) {
111
132
  if (found->second->sampler != nullptr) {
112
133
  SDL_ReleaseGPUSampler(guidev::g_device, found->second->sampler);
113
134
  }
135
+ if (found->second->compare_sampler != nullptr) {
136
+ SDL_ReleaseGPUSampler(guidev::g_device, found->second->compare_sampler);
137
+ }
114
138
  }
115
139
  textures_.erase(found);
116
140
  }
@@ -159,17 +183,29 @@ bool WebGLContext::tex_parameter(uint32_t id, int pname, int value) {
159
183
  else if (pname == GL_TEXTURE_MAG_FILTER) record->mag_filter = value;
160
184
  else if (pname == GL_TEXTURE_WRAP_S) record->wrap_s = value;
161
185
  else if (pname == GL_TEXTURE_WRAP_T) record->wrap_t = value;
162
- else return true; // other parameters are accepted and ignored
186
+ else if (pname == GL_TEXTURE_COMPARE_MODE) {
187
+ record->compare_mode = value == GL_COMPARE_REF_TO_TEXTURE;
188
+ } else if (pname == GL_TEXTURE_COMPARE_FUNC) {
189
+ record->compare_func = value;
190
+ } else return true; // other parameters are accepted and ignored
163
191
  if (record->sampler != nullptr && guidev::g_device != nullptr) {
164
192
  SDL_ReleaseGPUSampler(guidev::g_device, record->sampler);
165
193
  record->sampler = nullptr;
166
194
  }
195
+ if (record->compare_sampler != nullptr && guidev::g_device != nullptr) {
196
+ SDL_ReleaseGPUSampler(guidev::g_device, record->compare_sampler);
197
+ record->compare_sampler = nullptr;
198
+ }
167
199
  return true;
168
200
  }
169
201
 
170
202
  int WebGLContext::tex_parameter_value(uint32_t id, int pname) {
171
203
  WebGLTexture *record = texture(id);
172
204
  if (record == nullptr) return 0;
205
+ if (pname == GL_TEXTURE_COMPARE_MODE) {
206
+ return record->compare_mode ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE;
207
+ }
208
+ if (pname == GL_TEXTURE_COMPARE_FUNC) return record->compare_func;
173
209
  if (pname == GL_TEXTURE_MIN_FILTER) return record->min_filter;
174
210
  if (pname == GL_TEXTURE_MAG_FILTER) return record->mag_filter;
175
211
  if (pname == GL_TEXTURE_WRAP_S) return record->wrap_s;
@@ -269,6 +305,25 @@ SDL_GPUSampler *WebGLContext::sampler_for(WebGLTexture *record) {
269
305
  return record->sampler;
270
306
  }
271
307
 
308
+ SDL_GPUSampler *WebGLContext::sampler_for_compare(WebGLTexture *record) {
309
+ if (record == nullptr || guidev::g_device == nullptr) return nullptr;
310
+ if (record->compare_sampler != nullptr) return record->compare_sampler;
311
+ SDL_GPUSamplerCreateInfo info;
312
+ memset(&info, 0, sizeof(info));
313
+ info.min_filter = SDL_GPU_FILTER_LINEAR;
314
+ info.mag_filter = SDL_GPU_FILTER_LINEAR;
315
+ info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST;
316
+ info.address_mode_u = address_of(record->wrap_s);
317
+ info.address_mode_v = address_of(record->wrap_t);
318
+ info.address_mode_w = address_of(record->wrap_t);
319
+ info.min_lod = 0.0f;
320
+ info.max_lod = record->has_mipmaps ? 1000.0f : 0.0f;
321
+ info.enable_compare = true;
322
+ info.compare_op = compare_of(record->compare_func);
323
+ record->compare_sampler = SDL_CreateGPUSampler(guidev::g_device, &info);
324
+ return record->compare_sampler;
325
+ }
326
+
272
327
  int WebGLContext::texture_width(uint32_t id) {
273
328
  WebGLTexture *record = texture(id);
274
329
  return record != nullptr ? record->width : 0;
@@ -317,12 +372,14 @@ void WebGLContext::bind_framebuffer(int target, uint32_t id) {
317
372
  if (target != GL_FRAMEBUFFER && target != GL_DRAW_FRAMEBUFFER) return;
318
373
  if (id != bound_framebuffer_) end_pass();
319
374
  bound_framebuffer_ = id;
375
+ framebuffer_version_ += 1;
320
376
  if (target == GL_FRAMEBUFFER) read_framebuffer_ = id;
321
377
  read_buffer_ = id == 0 ? GL_BACK : GL_COLOR_ATTACHMENT0;
322
378
  }
323
379
 
324
380
  bool WebGLContext::framebuffer_texture(uint32_t framebuffer_id, uint32_t texture_id,
325
381
  int attachment) {
382
+ framebuffer_version_ += 1;
326
383
  WebGLFramebuffer *record_framebuffer = framebuffer(framebuffer_id);
327
384
  WebGLTexture *record = texture(texture_id);
328
385
  if (record_framebuffer == nullptr) return false;
@@ -436,6 +493,7 @@ bool WebGLContext::renderbuffer_storage(uint32_t id, int width, int height, bool
436
493
  }
437
494
 
438
495
  bool WebGLContext::framebuffer_renderbuffer(uint32_t framebuffer_id, uint32_t renderbuffer_id) {
496
+ framebuffer_version_ += 1;
439
497
  WebGLFramebuffer *framebuffer_ = framebuffer(framebuffer_id);
440
498
  WebGLRenderbuffer *record = renderbuffer(renderbuffer_id);
441
499
  if (framebuffer_ == nullptr) return false;