xbintsc 0.5.32 → 0.5.36

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 (173) 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 +6 -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-helpers.d.ts +7 -0
  38. package/dist/tests/e2e/gui-helpers.js +1 -0
  39. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  40. package/dist/tests/e2e/gui-media.test.js +78 -0
  41. package/dist/tests/e2e/gui-media.test.js.map +1 -1
  42. package/dist/tests/e2e/gui-style.test.js +55 -0
  43. package/dist/tests/e2e/gui-style.test.js.map +1 -1
  44. package/dist/tests/e2e/gui-webgl.test.js +258 -0
  45. package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
  46. package/dist/tests/e2e/gui-webgl2-3d.test.d.ts +4 -0
  47. package/dist/tests/e2e/gui-webgl2-3d.test.js +264 -0
  48. package/dist/tests/e2e/gui-webgl2-3d.test.js.map +1 -0
  49. package/dist/tests/e2e/gui-webgl2-blit.test.d.ts +10 -0
  50. package/dist/tests/e2e/gui-webgl2-blit.test.js +262 -0
  51. package/dist/tests/e2e/gui-webgl2-blit.test.js.map +1 -0
  52. package/dist/tests/e2e/gui-webgl2-formats.test.d.ts +6 -0
  53. package/dist/tests/e2e/gui-webgl2-formats.test.js +395 -0
  54. package/dist/tests/e2e/gui-webgl2-formats.test.js.map +1 -0
  55. package/dist/tests/e2e/gui-webgl2-mrt.test.d.ts +4 -0
  56. package/dist/tests/e2e/gui-webgl2-mrt.test.js +105 -0
  57. package/dist/tests/e2e/gui-webgl2-mrt.test.js.map +1 -0
  58. package/dist/tests/e2e/gui-webgl2-ubo.test.d.ts +8 -0
  59. package/dist/tests/e2e/gui-webgl2-ubo.test.js +112 -0
  60. package/dist/tests/e2e/gui-webgl2-ubo.test.js.map +1 -0
  61. package/dist/tests/e2e/gui-webgl2.test.js +45 -1
  62. package/dist/tests/e2e/gui-webgl2.test.js.map +1 -1
  63. package/dist/tests/extensions/gui-index.test.js +1 -0
  64. package/dist/tests/extensions/gui-index.test.js.map +1 -1
  65. package/dist/tests/extensions/media.test.js +1 -0
  66. package/dist/tests/extensions/media.test.js.map +1 -1
  67. package/dist/tests/parser/expressions.test.js +30 -0
  68. package/dist/tests/parser/expressions.test.js.map +1 -1
  69. package/dist/tests/platform-tarball.test.d.ts +1 -0
  70. package/dist/tests/platform-tarball.test.js +111 -0
  71. package/dist/tests/platform-tarball.test.js.map +1 -0
  72. package/dist/tests/version.test.js +6 -0
  73. package/dist/tests/version.test.js.map +1 -1
  74. package/doc/ai/contributing.md +11 -0
  75. package/doc/ai/extensions.md +7 -4
  76. package/doc/ai/troubleshooting.md +7 -5
  77. package/doc/ai/zh-CN/contributing.md +9 -0
  78. package/doc/ai/zh-CN/extensions.md +4 -2
  79. package/doc/ai/zh-CN/troubleshooting.md +5 -3
  80. package/doc/gui/api.md +61 -13
  81. package/doc/gui/build.md +11 -4
  82. package/doc/gui/dom.md +6 -3
  83. package/doc/gui/media.md +4 -0
  84. package/doc/gui/webgl.md +105 -46
  85. package/doc/gui.md +7 -3
  86. package/doc/npm-package.md +55 -27
  87. package/doc/requirements.md +6 -2
  88. package/doc/zh-CN/gui/api.md +51 -10
  89. package/doc/zh-CN/gui/build.md +7 -2
  90. package/doc/zh-CN/gui/dom.md +3 -1
  91. package/doc/zh-CN/gui/media.md +3 -0
  92. package/doc/zh-CN/gui/webgl.md +22 -16
  93. package/doc/zh-CN/gui.md +3 -2
  94. package/doc/zh-CN/npm-package.md +44 -23
  95. package/doc/zh-CN/requirements.md +4 -1
  96. package/package.json +17 -1
  97. package/runtime/ext_gui/audio.cpp +365 -153
  98. package/runtime/ext_gui/audio_buffer.cpp +166 -0
  99. package/runtime/ext_gui/audio_buffer.h +37 -0
  100. package/runtime/ext_gui/audio_decode.cpp +219 -0
  101. package/runtime/ext_gui/audio_decode.h +39 -0
  102. package/runtime/ext_gui/audio_device.cpp +87 -0
  103. package/runtime/ext_gui/audio_effects.cpp +163 -0
  104. package/runtime/ext_gui/audio_effects.h +25 -0
  105. package/runtime/ext_gui/audio_internal.h +30 -0
  106. package/runtime/ext_gui/audio_mixer.cpp +494 -0
  107. package/runtime/ext_gui/audio_mixer.h +238 -0
  108. package/runtime/ext_gui/audio_param.cpp +104 -0
  109. package/runtime/ext_gui/audio_param_object.cpp +155 -0
  110. package/runtime/ext_gui/browser.cpp +11 -1
  111. package/runtime/ext_gui/css_selector.cpp +21 -3
  112. package/runtime/ext_gui/dom_api_internal.h +16 -0
  113. package/runtime/ext_gui/dom_api_webgl.cpp +7 -0
  114. package/runtime/ext_gui/dom_api_webgl2.cpp +151 -36
  115. package/runtime/ext_gui/dom_api_webgl2_draw.cpp +3 -3
  116. package/runtime/ext_gui/dom_api_webgl_buffer.cpp +6 -2
  117. package/runtime/ext_gui/dom_api_webgl_compressed.cpp +167 -0
  118. package/runtime/ext_gui/dom_api_webgl_framebuffer.cpp +160 -0
  119. package/runtime/ext_gui/dom_api_webgl_shader.cpp +4 -4
  120. package/runtime/ext_gui/dom_api_webgl_source.cpp +161 -0
  121. package/runtime/ext_gui/dom_api_webgl_state.cpp +169 -23
  122. package/runtime/ext_gui/dom_api_webgl_texture.cpp +276 -236
  123. package/runtime/ext_gui/dom_api_webgl_ubo.cpp +81 -0
  124. package/runtime/ext_gui/gui.cpp +44 -9
  125. package/runtime/ext_gui/gui_engine.h +9 -4
  126. package/runtime/ext_gui/image_bitmap.cpp +28 -0
  127. package/runtime/ext_gui/image_bitmap.h +26 -0
  128. package/runtime/ext_gui/image_bitmap_browser.cpp +136 -0
  129. package/runtime/ext_gui/media.cpp +32 -0
  130. package/runtime/ext_gui/media.h +13 -0
  131. package/runtime/ext_gui/media_api.h +23 -0
  132. package/runtime/ext_gui/webgl.h +152 -18
  133. package/runtime/ext_gui/webgl_blit.cpp +95 -0
  134. package/runtime/ext_gui/webgl_buffer.cpp +178 -263
  135. package/runtime/ext_gui/webgl_buffer_objects.cpp +270 -0
  136. package/runtime/ext_gui/webgl_clear.cpp +142 -0
  137. package/runtime/ext_gui/webgl_compressed.cpp +180 -0
  138. package/runtime/ext_gui/webgl_context.cpp +241 -156
  139. package/runtime/ext_gui/webgl_draw.cpp +68 -3
  140. package/runtime/ext_gui/webgl_draw_buffers.cpp +75 -0
  141. package/runtime/ext_gui/webgl_glsl.h +40 -0
  142. package/runtime/ext_gui/webgl_glsl_compile.cpp +6 -0
  143. package/runtime/ext_gui/webgl_glsl_rewrite.cpp +18 -0
  144. package/runtime/ext_gui/webgl_glsl_scan.cpp +20 -11
  145. package/runtime/ext_gui/webgl_glsl_scan_block.cpp +126 -0
  146. package/runtime/ext_gui/webgl_glsl_tokens.h +28 -0
  147. package/runtime/ext_gui/webgl_pipeline.cpp +71 -19
  148. package/runtime/ext_gui/webgl_program_blocks.cpp +58 -0
  149. package/runtime/ext_gui/webgl_program_internal.h +6 -0
  150. package/runtime/ext_gui/webgl_shader.cpp +13 -1
  151. package/runtime/ext_gui/webgl_state.cpp +228 -0
  152. package/runtime/ext_gui/webgl_texture.cpp +144 -17
  153. package/runtime/ext_gui/webgl_texture_3d.cpp +215 -0
  154. package/runtime/ext_gui/webgl_uniform_block.h +31 -0
  155. package/runtime/ext_gui/webgl_webgl2.cpp +58 -5
  156. package/runtime/ext_media/decode_audio.cpp +308 -0
  157. package/runtime/ext_media/media_bridge.c +2 -0
  158. package/runtime/rt.h +17 -0
  159. package/runtime/xt_array_buffer.c +30 -0
  160. package/runtime/xt_typed_array/elements.inc +13 -0
  161. package/scripts/build-media.ts +14 -6
  162. package/scripts/build-runtime.ts +21 -16
  163. package/scripts/check-version.ts +39 -9
  164. package/scripts/package-platform.ts +183 -0
  165. package/scripts/set-platform-version.ts +62 -0
  166. package/scripts/verify-platform-package.ts +182 -0
  167. package/src/cli/main.ts +5 -1
  168. package/src/driver/runtime-lib.ts +73 -9
  169. package/src/driver/version.ts +1 -1
  170. package/src/extensions/gui/index.ts +6 -5
  171. package/src/extensions/media/index.ts +4 -3
  172. package/src/parser/context.ts +99 -4
  173. package/src/parser/literals.ts +6 -12
@@ -1,11 +1,11 @@
1
1
  /*
2
- * xbintsc GUI engine — WebGL context lifetime, GPU targets and fixed-function
3
- * state.
2
+ * xbintsc GUI engine — WebGL context lifetime and GPU targets.
4
3
  *
5
4
  * Every context records into one SDL_GPU command buffer. A render pass stays
6
5
  * open across draws and is ended whenever a copy pass (buffer/texture upload)
7
6
  * or a different attachment has to be recorded; the next draw starts a new
8
7
  * pass with `LOAD`, so the framebuffer persists like a WebGL drawing buffer.
8
+ * Fixed-function state lives in `webgl_state.cpp`.
9
9
  */
10
10
 
11
11
  #include "webgl.h"
@@ -46,38 +46,130 @@ WebGLContext::WebGLContext(uint64_t id, int width, int height, bool webgl2)
46
46
 
47
47
  WebGLContext::~WebGLContext() {
48
48
  submit();
49
- if (guidev::g_device != nullptr) {
50
- SDL_WaitForGPUIdle(guidev::g_device);
51
- for (auto &entry : programs_) release_program_pipelines(entry.second.get());
52
- for (auto &entry : textures_) {
53
- if (entry.second->gpu != nullptr) SDL_ReleaseGPUTexture(guidev::g_device, entry.second->gpu);
54
- if (entry.second->sampler != nullptr) {
55
- SDL_ReleaseGPUSampler(guidev::g_device, entry.second->sampler);
56
- }
57
- }
58
- for (auto &entry : buffers_) {
59
- if (entry.second->gpu != nullptr) SDL_ReleaseGPUBuffer(guidev::g_device, entry.second->gpu);
60
- }
61
- for (auto &entry : samplers_) {
62
- if (entry.second->gpu != nullptr) {
63
- SDL_ReleaseGPUSampler(guidev::g_device, entry.second->gpu);
64
- }
49
+ if (guidev::g_device != nullptr) release_gpu_resources();
50
+ g_contexts.erase(id_);
51
+ }
52
+
53
+ void WebGLContext::release_gpu_resources() {
54
+ if (guidev::g_device == nullptr) return;
55
+ SDL_WaitForGPUIdle(guidev::g_device);
56
+ for (auto &entry : programs_) release_program_pipelines(entry.second.get());
57
+ for (auto &entry : textures_) {
58
+ if (entry.second->gpu != nullptr) SDL_ReleaseGPUTexture(guidev::g_device, entry.second->gpu);
59
+ if (entry.second->sampler != nullptr) {
60
+ SDL_ReleaseGPUSampler(guidev::g_device, entry.second->sampler);
65
61
  }
66
- for (auto &entry : framebuffers_) {
67
- if (entry.second->depth != nullptr) SDL_ReleaseGPUTexture(guidev::g_device, entry.second->depth);
62
+ entry.second->gpu = nullptr;
63
+ entry.second->sampler = nullptr;
64
+ }
65
+ for (auto &entry : buffers_) {
66
+ if (entry.second->gpu != nullptr) SDL_ReleaseGPUBuffer(guidev::g_device, entry.second->gpu);
67
+ entry.second->gpu = nullptr;
68
+ entry.second->capacity = 0;
69
+ }
70
+ for (auto &entry : samplers_) {
71
+ if (entry.second->gpu != nullptr) {
72
+ SDL_ReleaseGPUSampler(guidev::g_device, entry.second->gpu);
73
+ entry.second->gpu = nullptr;
68
74
  }
69
- for (auto &entry : renderbuffers_) {
70
- if (entry.second->depth != nullptr) SDL_ReleaseGPUTexture(guidev::g_device, entry.second->depth);
75
+ }
76
+ for (auto &entry : framebuffers_) {
77
+ if (entry.second->depth != nullptr) {
78
+ SDL_ReleaseGPUTexture(guidev::g_device, entry.second->depth);
79
+ entry.second->depth = nullptr;
71
80
  }
72
- for (SDL_GPUBuffer *buffer : temp_buffers_) {
73
- SDL_ReleaseGPUBuffer(guidev::g_device, buffer);
81
+ }
82
+ for (auto &entry : renderbuffers_) {
83
+ if (entry.second->depth != nullptr) {
84
+ SDL_ReleaseGPUTexture(guidev::g_device, entry.second->depth);
85
+ entry.second->depth = nullptr;
74
86
  }
75
- if (fallback_texture_ != nullptr) SDL_ReleaseGPUTexture(guidev::g_device, fallback_texture_);
76
- if (fallback_sampler_ != nullptr) SDL_ReleaseGPUSampler(guidev::g_device, fallback_sampler_);
77
- if (color_ != nullptr) SDL_ReleaseGPUTexture(guidev::g_device, color_);
78
- if (depth_ != nullptr) SDL_ReleaseGPUTexture(guidev::g_device, depth_);
79
87
  }
80
- g_contexts.erase(id_);
88
+ for (SDL_GPUBuffer *buffer : temp_buffers_) SDL_ReleaseGPUBuffer(guidev::g_device, buffer);
89
+ temp_buffers_.clear();
90
+ if (fallback_texture_ != nullptr) {
91
+ SDL_ReleaseGPUTexture(guidev::g_device, fallback_texture_);
92
+ fallback_texture_ = nullptr;
93
+ }
94
+ if (fallback_sampler_ != nullptr) {
95
+ SDL_ReleaseGPUSampler(guidev::g_device, fallback_sampler_);
96
+ fallback_sampler_ = nullptr;
97
+ }
98
+ if (color_ != nullptr) {
99
+ SDL_ReleaseGPUTexture(guidev::g_device, color_);
100
+ color_ = nullptr;
101
+ }
102
+ if (depth_ != nullptr) {
103
+ SDL_ReleaseGPUTexture(guidev::g_device, depth_);
104
+ depth_ = nullptr;
105
+ }
106
+ if (clear_texture_ != nullptr) {
107
+ SDL_ReleaseGPUTexture(guidev::g_device, clear_texture_);
108
+ clear_texture_ = nullptr;
109
+ }
110
+ }
111
+
112
+ void WebGLContext::lose_context() {
113
+ if (lost_) return;
114
+ submit();
115
+ if (guidev::g_device != nullptr) release_gpu_resources();
116
+ /* The WebGL spec invalidates every object when the context is lost: a page
117
+ * that gets `webglcontextlost` must recreate buffers, textures and programs
118
+ * after the restore. */
119
+ shaders_.clear();
120
+ programs_.clear();
121
+ buffers_.clear();
122
+ textures_.clear();
123
+ framebuffers_.clear();
124
+ renderbuffers_.clear();
125
+ vertex_arrays_.clear();
126
+ samplers_.clear();
127
+ queries_.clear();
128
+ syncs_.clear();
129
+ default_vertex_array_ = WebGLVertexArray();
130
+ for (WebGLAttribState &attrib : attribs_) attrib = WebGLAttribState();
131
+ current_program_ = 0;
132
+ bound_array_ = 0;
133
+ bound_element_ = 0;
134
+ bound_framebuffer_ = 0;
135
+ read_framebuffer_ = 0;
136
+ bound_renderbuffer_ = 0;
137
+ bound_vertex_array_ = 0;
138
+ copy_read_buffer_ = 0;
139
+ copy_write_buffer_ = 0;
140
+ active_unit_ = 0;
141
+ for (uint32_t &texture : bound_textures_) texture = 0;
142
+ for (uint32_t &texture : bound_cube_textures_) texture = 0;
143
+ for (uint32_t &texture : bound_array_textures_) texture = 0;
144
+ for (uint32_t &texture : bound_3d_textures_) texture = 0;
145
+ for (int &target : active_texture_target_) target = GL_TEXTURE_2D;
146
+ for (uint32_t &sampler : bound_samplers_) sampler = 0;
147
+ state_ = WebGLDrawState();
148
+ scissor_test_ = false;
149
+ error_ = 0;
150
+ lost_ = true;
151
+ clear_program_ = 0;
152
+ clear_buffer_ = 0;
153
+ }
154
+
155
+ void WebGLContext::restore_context() {
156
+ if (!lost_) return;
157
+ if (guidev::g_device == nullptr) return;
158
+ lost_ = false;
159
+ error_ = 0;
160
+ ensure_default_target();
161
+ }
162
+
163
+ void WebGLContext::drawing_buffer_storage(int width, int height) {
164
+ if (width <= 0 || height <= 0) {
165
+ set_error(GL_INVALID_VALUE);
166
+ return;
167
+ }
168
+ fixed_storage_ = true;
169
+ if (width == width_ && height == height_) return;
170
+ width_ = width;
171
+ height_ = height;
172
+ rebuild_default_target();
81
173
  }
82
174
 
83
175
  /* -- targets ------------------------------------------------------------- */
@@ -105,7 +197,7 @@ void WebGLContext::ensure_default_target() {
105
197
  info.num_levels = 1;
106
198
  info.sample_count = SDL_GPU_SAMPLECOUNT_1;
107
199
  if (color_ == nullptr) color_ = SDL_CreateGPUTexture(guidev::g_device, &info);
108
- info.format = SDL_GPU_TEXTUREFORMAT_D32_FLOAT;
200
+ info.format = SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT;
109
201
  info.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET;
110
202
  if (depth_ == nullptr) depth_ = SDL_CreateGPUTexture(guidev::g_device, &info);
111
203
  }
@@ -129,11 +221,15 @@ void WebGLContext::rebuild_default_target() {
129
221
  }
130
222
 
131
223
  SDL_GPUTexture *WebGLContext::color_texture() {
224
+ if (lost_) return nullptr;
132
225
  ensure_default_target();
133
226
  return color_;
134
227
  }
135
228
 
136
229
  void WebGLContext::resize(int width, int height) {
230
+ /* After `drawingBufferStorage` the drawing buffer has an explicit size and a
231
+ * canvas resize no longer changes it (WebGL 2). */
232
+ if (fixed_storage_) return;
137
233
  if (width < 0) width = 0;
138
234
  if (height < 0) height = 0;
139
235
  width_ = width > 0 ? width : 1;
@@ -165,12 +261,34 @@ SDL_GPUTexture *WebGLContext::target_color() {
165
261
  return color_;
166
262
  }
167
263
  WebGLFramebuffer *framebuffer = this->framebuffer(bound_framebuffer_);
168
- if (framebuffer == nullptr || framebuffer->color_texture == 0) return nullptr;
169
- WebGLTexture *texture = this->texture(framebuffer->color_texture);
264
+ if (framebuffer == nullptr || framebuffer->color_textures[0] == 0) return nullptr;
265
+ WebGLTexture *texture = this->texture(framebuffer->color_textures[0]);
170
266
  if (texture == nullptr) return nullptr;
171
267
  return ensure_texture_gpu(texture, true);
172
268
  }
173
269
 
270
+ SDL_GPUTexture *WebGLContext::framebuffer_color(uint32_t id) {
271
+ if (id == 0) {
272
+ ensure_default_target();
273
+ return color_;
274
+ }
275
+ WebGLFramebuffer *framebuffer_ = framebuffer(id);
276
+ if (framebuffer_ == nullptr || framebuffer_->color_textures[0] == 0) return nullptr;
277
+ WebGLTexture *texture_ = texture(framebuffer_->color_textures[0]);
278
+ return texture_ != nullptr ? ensure_texture_gpu(texture_, true) : nullptr;
279
+ }
280
+
281
+ void WebGLContext::framebuffer_size(uint32_t id, int *width, int *height) {
282
+ if (id == 0) {
283
+ *width = width_;
284
+ *height = height_;
285
+ return;
286
+ }
287
+ WebGLFramebuffer *framebuffer_ = framebuffer(id);
288
+ *width = framebuffer_ != nullptr ? framebuffer_->width : 0;
289
+ *height = framebuffer_ != nullptr ? framebuffer_->height : 0;
290
+ }
291
+
174
292
  SDL_GPUTexture *WebGLContext::target_depth() {
175
293
  if (bound_framebuffer_ == 0) {
176
294
  ensure_default_target();
@@ -178,12 +296,18 @@ SDL_GPUTexture *WebGLContext::target_depth() {
178
296
  }
179
297
  WebGLFramebuffer *framebuffer = this->framebuffer(bound_framebuffer_);
180
298
  if (framebuffer == nullptr) return nullptr;
299
+ if (framebuffer->depth_texture != 0) {
300
+ WebGLTexture *texture = this->texture(framebuffer->depth_texture);
301
+ if (texture == nullptr) return nullptr;
302
+ texture->as_depth_target = true;
303
+ return ensure_texture_gpu(texture, true);
304
+ }
181
305
  if (framebuffer->depth == nullptr && framebuffer->depth_renderbuffer == 0 &&
182
306
  framebuffer->width > 0 && framebuffer->height > 0 && guidev::g_device != nullptr) {
183
307
  SDL_GPUTextureCreateInfo info;
184
308
  memset(&info, 0, sizeof(info));
185
309
  info.type = SDL_GPU_TEXTURETYPE_2D;
186
- info.format = SDL_GPU_TEXTUREFORMAT_D32_FLOAT;
310
+ info.format = SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT;
187
311
  info.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET;
188
312
  info.width = (Uint32)framebuffer->width;
189
313
  info.height = (Uint32)framebuffer->height;
@@ -195,6 +319,27 @@ SDL_GPUTexture *WebGLContext::target_depth() {
195
319
  return framebuffer->depth;
196
320
  }
197
321
 
322
+ int WebGLContext::target_color_format() {
323
+ if (bound_framebuffer_ == 0) return SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
324
+ WebGLFramebuffer *framebuffer = this->framebuffer(bound_framebuffer_);
325
+ if (framebuffer == nullptr || framebuffer->color_textures[0] == 0) {
326
+ return SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
327
+ }
328
+ WebGLTexture *texture = this->texture(framebuffer->color_textures[0]);
329
+ return texture != nullptr ? (int)texture->format : SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
330
+ }
331
+
332
+ int WebGLContext::target_depth_format() {
333
+ if (bound_framebuffer_ == 0) return SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT;
334
+ WebGLFramebuffer *framebuffer = this->framebuffer(bound_framebuffer_);
335
+ if (framebuffer == nullptr) return SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT;
336
+ if (framebuffer->depth_texture != 0) {
337
+ WebGLTexture *texture = this->texture(framebuffer->depth_texture);
338
+ if (texture != nullptr) return (int)texture->format;
339
+ }
340
+ return SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT;
341
+ }
342
+
198
343
  void WebGLContext::end_pass() {
199
344
  if (pass_ != nullptr && cmd_ != nullptr) {
200
345
  SDL_EndGPURenderPass(pass_);
@@ -202,30 +347,37 @@ void WebGLContext::end_pass() {
202
347
  }
203
348
  }
204
349
 
205
- void WebGLContext::begin_pass(bool clear_color, bool clear_depth) {
350
+ void WebGLContext::begin_pass(bool clear_color, bool clear_depth, bool clear_stencil) {
206
351
  if (guidev::g_device == nullptr) return;
207
352
  if (pass_ != nullptr && pass_targets_default_ == (bound_framebuffer_ == 0) &&
208
- pass_framebuffer_ == bound_framebuffer_ && !clear_color && !clear_depth) {
353
+ pass_framebuffer_ == bound_framebuffer_ && !clear_color && !clear_depth &&
354
+ !clear_stencil) {
209
355
  return;
210
356
  }
211
357
  end_pass();
212
358
  ensure_command();
213
359
  if (cmd_ == nullptr) return;
214
- SDL_GPUTexture *color = target_color();
215
360
  SDL_GPUTexture *depth = target_depth();
216
- if (color == nullptr) {
361
+ SDL_GPUColorTargetInfo color_infos[kWebGLMaxDrawBuffers];
362
+ int color_count = 0;
363
+ for (int slot = 0; slot < kWebGLMaxDrawBuffers; slot++) {
364
+ SDL_GPUTexture *color = draw_color_texture(slot);
365
+ if (color == nullptr) continue;
366
+ SDL_GPUColorTargetInfo &color_info = color_infos[color_count++];
367
+ memset(&color_info, 0, sizeof(color_info));
368
+ color_info.texture = color;
369
+ color_info.layer_or_depth_plane = (Uint32)draw_color_layer(slot);
370
+ color_info.clear_color.r = clear_color_[0];
371
+ color_info.clear_color.g = clear_color_[1];
372
+ color_info.clear_color.b = clear_color_[2];
373
+ color_info.clear_color.a = clear_color_[3];
374
+ color_info.load_op = clear_color ? SDL_GPU_LOADOP_CLEAR : SDL_GPU_LOADOP_LOAD;
375
+ color_info.store_op = SDL_GPU_STOREOP_STORE;
376
+ }
377
+ if (color_count == 0) {
217
378
  set_error(GL_INVALID_FRAMEBUFFER_OPERATION);
218
379
  return;
219
380
  }
220
- SDL_GPUColorTargetInfo color_info;
221
- memset(&color_info, 0, sizeof(color_info));
222
- color_info.texture = color;
223
- color_info.clear_color.r = clear_color_[0];
224
- color_info.clear_color.g = clear_color_[1];
225
- color_info.clear_color.b = clear_color_[2];
226
- color_info.clear_color.a = clear_color_[3];
227
- color_info.load_op = clear_color ? SDL_GPU_LOADOP_CLEAR : SDL_GPU_LOADOP_LOAD;
228
- color_info.store_op = SDL_GPU_STOREOP_STORE;
229
381
 
230
382
  SDL_GPUDepthStencilTargetInfo depth_info;
231
383
  memset(&depth_info, 0, sizeof(depth_info));
@@ -234,111 +386,17 @@ void WebGLContext::begin_pass(bool clear_color, bool clear_depth) {
234
386
  depth_info.load_op = (clear_depth && depth != nullptr) ? SDL_GPU_LOADOP_CLEAR
235
387
  : SDL_GPU_LOADOP_LOAD;
236
388
  depth_info.store_op = SDL_GPU_STOREOP_STORE;
237
- depth_info.stencil_load_op = SDL_GPU_LOADOP_DONT_CARE;
238
- depth_info.stencil_store_op = SDL_GPU_STOREOP_DONT_CARE;
389
+ depth_info.clear_stencil = (Uint8)(clear_stencil_ & 0xFF);
390
+ depth_info.stencil_load_op = (clear_stencil && depth != nullptr) ? SDL_GPU_LOADOP_CLEAR
391
+ : SDL_GPU_LOADOP_LOAD;
392
+ depth_info.stencil_store_op = SDL_GPU_STOREOP_STORE;
239
393
 
240
- pass_ = SDL_BeginGPURenderPass(cmd_, &color_info, 1, depth != nullptr ? &depth_info : nullptr);
394
+ pass_ = SDL_BeginGPURenderPass(cmd_, color_infos, (Uint32)color_count,
395
+ depth != nullptr ? &depth_info : nullptr);
241
396
  pass_targets_default_ = bound_framebuffer_ == 0;
242
397
  pass_framebuffer_ = bound_framebuffer_;
243
398
  }
244
399
 
245
- /* -- state --------------------------------------------------------------- */
246
-
247
- void WebGLContext::enable(int cap) {
248
- if (cap == GL_BLEND) state_.blend = true;
249
- if (cap == GL_DEPTH_TEST) state_.depth_test = true;
250
- if (cap == GL_CULL_FACE) state_.cull = true;
251
- if (cap == GL_SCISSOR_TEST) scissor_test_ = true;
252
- if (cap == GL_STENCIL_TEST) stencil_test_ = true;
253
- }
254
-
255
- void WebGLContext::disable(int cap) {
256
- if (cap == GL_BLEND) state_.blend = false;
257
- if (cap == GL_DEPTH_TEST) state_.depth_test = false;
258
- if (cap == GL_CULL_FACE) state_.cull = false;
259
- if (cap == GL_SCISSOR_TEST) scissor_test_ = false;
260
- if (cap == GL_STENCIL_TEST) stencil_test_ = false;
261
- }
262
-
263
- bool WebGLContext::is_enabled(int cap) const {
264
- if (cap == GL_BLEND) return state_.blend;
265
- if (cap == GL_DEPTH_TEST) return state_.depth_test;
266
- if (cap == GL_CULL_FACE) return state_.cull;
267
- if (cap == GL_SCISSOR_TEST) return scissor_test_;
268
- if (cap == GL_STENCIL_TEST) return stencil_test_;
269
- return false;
270
- }
271
-
272
- void WebGLContext::clear_color(float r, float g, float b, float a) {
273
- clear_color_[0] = r;
274
- clear_color_[1] = g;
275
- clear_color_[2] = b;
276
- clear_color_[3] = a;
277
- }
278
-
279
- void WebGLContext::color_mask(bool r, bool g, bool b, bool a) {
280
- state_.color_mask[0] = r;
281
- state_.color_mask[1] = g;
282
- state_.color_mask[2] = b;
283
- state_.color_mask[3] = a;
284
- }
285
-
286
- void WebGLContext::blend_func(int src, int dst) {
287
- state_.blend_src_rgb = src;
288
- state_.blend_dst_rgb = dst;
289
- state_.blend_src_alpha = src;
290
- state_.blend_dst_alpha = dst;
291
- }
292
-
293
- void WebGLContext::blend_func_separate(int src_rgb, int dst_rgb, int src_alpha, int dst_alpha) {
294
- state_.blend_src_rgb = src_rgb;
295
- state_.blend_dst_rgb = dst_rgb;
296
- state_.blend_src_alpha = src_alpha;
297
- state_.blend_dst_alpha = dst_alpha;
298
- }
299
-
300
- void WebGLContext::blend_equation(int mode) {
301
- state_.blend_eq_rgb = mode;
302
- state_.blend_eq_alpha = mode;
303
- }
304
-
305
- void WebGLContext::blend_equation_separate(int rgb, int alpha) {
306
- state_.blend_eq_rgb = rgb;
307
- state_.blend_eq_alpha = alpha;
308
- }
309
-
310
- void WebGLContext::cull_face(int mode) { state_.cull_face = mode; }
311
-
312
- void WebGLContext::front_face(int mode) { state_.front_face = mode; }
313
-
314
- void WebGLContext::viewport(int x, int y, int w, int h) {
315
- viewport_[0] = x;
316
- viewport_[1] = y;
317
- viewport_[2] = w;
318
- viewport_[3] = h;
319
- }
320
-
321
- void WebGLContext::scissor(int x, int y, int w, int h) {
322
- scissor_box_[0] = x;
323
- scissor_box_[1] = y;
324
- scissor_box_[2] = w;
325
- scissor_box_[3] = h;
326
- }
327
-
328
- void WebGLContext::clear(int mask) {
329
- if (guidev::g_device == nullptr) return;
330
- const bool color_bit = (mask & GL_COLOR_BUFFER_BIT) != 0;
331
- const bool depth_bit = (mask & GL_DEPTH_BUFFER_BIT) != 0;
332
- /* The scissor rectangle is not honoured by a pass-begin clear; WebGL pages
333
- * that clear a sub-rectangle are rare and documented as a limitation. */
334
- begin_pass(color_bit, depth_bit);
335
- }
336
-
337
- void WebGLContext::finish() {
338
- submit();
339
- if (guidev::g_device != nullptr) SDL_WaitForGPUIdle(guidev::g_device);
340
- }
341
-
342
400
  /* -- queries ------------------------------------------------------------- */
343
401
 
344
402
  double WebGLContext::parameter(int pname) {
@@ -352,7 +410,9 @@ double WebGLContext::parameter(int pname) {
352
410
  case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
353
411
  return kWebGLMaxTextureUnits * 2;
354
412
  case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
355
- return 0;
413
+ /* The vertex stage binds its own sampler set (`SDL_BindGPUVertexSamplers`)
414
+ * and the translator emits vertex samplers in descriptor set 0. */
415
+ return kWebGLMaxTextureUnits;
356
416
  case GL_MAX_VERTEX_UNIFORM_VECTORS:
357
417
  case GL_MAX_FRAGMENT_UNIFORM_VECTORS:
358
418
  case GL_MAX_VARYING_VECTORS:
@@ -362,7 +422,7 @@ double WebGLContext::parameter(int pname) {
362
422
  case GL_MAX_VIEWPORT_DIMS:
363
423
  return 8192;
364
424
  case GL_MAX_CUBE_MAP_TEXTURE_SIZE:
365
- return 0;
425
+ return 8192;
366
426
  case GL_SAMPLES:
367
427
  case GL_SAMPLE_BUFFERS:
368
428
  return 0;
@@ -375,7 +435,7 @@ double WebGLContext::parameter(int pname) {
375
435
  case GL_DEPTH_BITS:
376
436
  return 8;
377
437
  case GL_STENCIL_BITS:
378
- return 0;
438
+ return 8;
379
439
  case GL_UNPACK_ALIGNMENT:
380
440
  case GL_PACK_ALIGNMENT:
381
441
  return 4;
@@ -394,13 +454,23 @@ double WebGLContext::parameter(int pname) {
394
454
  return 256;
395
455
  case GL_MAX_DRAW_BUFFERS:
396
456
  case GL_MAX_COLOR_ATTACHMENTS:
397
- return 1;
457
+ return kWebGLMaxDrawBuffers;
458
+ case GL_READ_BUFFER:
459
+ return read_buffer_;
460
+ case GL_DRAW_BUFFER0:
461
+ return draw_buffer_value(0);
462
+ case GL_DRAW_BUFFER1:
463
+ return draw_buffer_value(1);
464
+ case GL_DRAW_BUFFER2:
465
+ return draw_buffer_value(2);
466
+ case GL_DRAW_BUFFER3:
467
+ return draw_buffer_value(3);
398
468
  case GL_MAX_SAMPLES:
399
469
  return 0;
400
470
  case GL_MAX_UNIFORM_BUFFER_BINDINGS:
401
471
  case GL_MAX_VERTEX_UNIFORM_BLOCKS:
402
472
  case GL_MAX_FRAGMENT_UNIFORM_BLOCKS:
403
- return 0;
473
+ return 16;
404
474
  case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT:
405
475
  return 256;
406
476
  case GL_MAX_ELEMENT_INDEX:
@@ -417,11 +487,7 @@ double WebGLContext::parameter(int pname) {
417
487
  case GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS:
418
488
  case GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS:
419
489
  return 1024;
420
- case GL_MAX_UNIFORM_BLOCK_SIZE:
421
- return 16384;
422
- case GL_READ_BUFFER:
423
- case GL_DRAW_BUFFER0:
424
- return GL_BACK;
490
+
425
491
  default:
426
492
  return 0;
427
493
  }
@@ -452,9 +518,28 @@ uint32_t WebGLContext::bound_buffer(int target) const { return buffer_for_target
452
518
 
453
519
  uint32_t WebGLContext::bound_texture(int unit) const {
454
520
  if (unit < 0 || unit >= kWebGLMaxTextureUnits) return 0;
521
+ const int target = active_texture_target_[unit];
522
+ if (target == GL_TEXTURE_CUBE_MAP) return bound_cube_textures_[unit];
523
+ if (target == GL_TEXTURE_2D_ARRAY) return bound_array_textures_[unit];
524
+ if (target == GL_TEXTURE_3D) return bound_3d_textures_[unit];
455
525
  return bound_textures_[unit];
456
526
  }
457
527
 
528
+ uint32_t WebGLContext::bound_cube_texture(int unit) const {
529
+ if (unit < 0 || unit >= kWebGLMaxTextureUnits) return 0;
530
+ return bound_cube_textures_[unit];
531
+ }
532
+
533
+ uint32_t WebGLContext::bound_array_texture(int unit) const {
534
+ if (unit < 0 || unit >= kWebGLMaxTextureUnits) return 0;
535
+ return bound_array_textures_[unit];
536
+ }
537
+
538
+ uint32_t WebGLContext::bound_3d_texture(int unit) const {
539
+ if (unit < 0 || unit >= kWebGLMaxTextureUnits) return 0;
540
+ return bound_3d_textures_[unit];
541
+ }
542
+
458
543
  void WebGLContext::viewport_box(int out[4]) const {
459
544
  for (int i = 0; i < 4; i++) out[i] = viewport_[i];
460
545
  }
@@ -144,6 +144,11 @@ std::string pipeline_key(int mode, int index_size,
144
144
  const std::vector<SDL_GPUVertexBufferDescription> &buffers,
145
145
  const WebGLDrawState &state) {
146
146
  std::string key = std::to_string(mode) + "/" + std::to_string(index_size);
147
+ key += "/f" + std::to_string(state.color_target_count) + ":";
148
+ for (int i = 0; i < state.color_target_count; i++) {
149
+ key += std::to_string(state.color_formats[i]) + ",";
150
+ }
151
+ key += ":" + std::to_string(state.depth_format);
147
152
  for (const SDL_GPUVertexAttribute &attr : attrs) {
148
153
  key += "/" + std::to_string(attr.location) + ":" + std::to_string(attr.buffer_slot) + ":" +
149
154
  std::to_string((int)attr.format) + ":" + std::to_string(attr.offset);
@@ -152,7 +157,7 @@ std::string pipeline_key(int mode, int index_size,
152
157
  key += "|" + std::to_string(buffer.slot) + ":" + std::to_string(buffer.pitch) + ":" +
153
158
  std::to_string((int)buffer.input_rate);
154
159
  }
155
- char state_key[160];
160
+ char state_key[256];
156
161
  snprintf(state_key, sizeof(state_key), "|%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
157
162
  (int)state.blend, state.blend_src_rgb, state.blend_dst_rgb, state.blend_src_alpha,
158
163
  state.blend_dst_alpha, state.blend_eq_rgb, state.blend_eq_alpha, (int)state.depth_test,
@@ -162,6 +167,16 @@ std::string pipeline_key(int mode, int index_size,
162
167
  key += std::to_string((int)state.color_mask[1]);
163
168
  key += std::to_string((int)state.color_mask[2]);
164
169
  key += std::to_string((int)state.color_mask[3]);
170
+ char stencil_key[224];
171
+ snprintf(stencil_key, sizeof(stencil_key), "|s%d,%d,%d,%d,%d,%u,%d,%d,%d,%d,%u,%u,%u",
172
+ (int)state.stencil, state.stencil_front.func, state.stencil_front.fail,
173
+ state.stencil_front.zfail, state.stencil_front.zpass,
174
+ (unsigned)state.stencil_front.write_mask, state.stencil_back.func,
175
+ state.stencil_back.fail, state.stencil_back.zfail, state.stencil_back.zpass,
176
+ (unsigned)state.stencil_back.write_mask,
177
+ (unsigned)state.stencil_front.value_mask,
178
+ (unsigned)state.stencil_front.write_mask);
179
+ key += stencil_key;
165
180
  return key;
166
181
  }
167
182
 
@@ -308,6 +323,15 @@ void WebGLContext::draw(int mode, int first, int count, int index_type, int offs
308
323
  bound_framebuffer_ != 0 ? framebuffer(bound_framebuffer_) : nullptr;
309
324
  const int target_width = framebuffer_ != nullptr ? framebuffer_->width : width_;
310
325
  const int target_height = framebuffer_ != nullptr ? framebuffer_->height : height_;
326
+ /* A pipeline bakes in the attachment formats; float colour targets, MRT
327
+ * layouts and depth textures therefore need their own pipeline variants. */
328
+ state_.color_target_count = 0;
329
+ for (int slot = 0; slot < kWebGLMaxDrawBuffers; slot++) {
330
+ if (draw_color_texture(slot) == nullptr) continue;
331
+ state_.color_formats[state_.color_target_count++] = draw_color_format(slot);
332
+ }
333
+ if (state_.color_target_count == 0) state_.color_target_count = 1;
334
+ state_.depth_format = target_depth_format();
311
335
 
312
336
  std::vector<SDL_GPUVertexAttribute> attributes;
313
337
  std::vector<SDL_GPUVertexBufferDescription> buffers;
@@ -381,7 +405,7 @@ void WebGLContext::draw(int mode, int first, int count, int index_type, int offs
381
405
  bindings.push_back(binding);
382
406
  }
383
407
 
384
- begin_pass(false, false);
408
+ begin_pass(false, false, false);
385
409
  if (pass_ == nullptr) return;
386
410
 
387
411
  SDL_GPUViewport viewport;
@@ -431,6 +455,17 @@ void WebGLContext::draw(int mode, int first, int count, int index_type, int offs
431
455
  if (pipeline == nullptr) return;
432
456
 
433
457
  SDL_BindGPUGraphicsPipeline(pass_, pipeline);
458
+ if (state_.stencil) {
459
+ SDL_SetGPUStencilReference(pass_, (Uint8)(stencil_reference() & 0xFF));
460
+ }
461
+ if (state_.blend) {
462
+ SDL_FColor constants;
463
+ constants.r = blend_color_[0];
464
+ constants.g = blend_color_[1];
465
+ constants.b = blend_color_[2];
466
+ constants.a = blend_color_[3];
467
+ SDL_SetGPUBlendConstants(pass_, constants);
468
+ }
434
469
  if (!bindings.empty()) {
435
470
  SDL_BindGPUVertexBuffers(pass_, 0, bindings.data(), (Uint32)bindings.size());
436
471
  }
@@ -442,6 +477,28 @@ void WebGLContext::draw(int mode, int first, int count, int index_type, int offs
442
477
  index_size == 4 ? SDL_GPU_INDEXELEMENTSIZE_32BIT
443
478
  : SDL_GPU_INDEXELEMENTSIZE_16BIT);
444
479
  }
480
+ /* User uniform blocks live in the same std140 block; refresh their members
481
+ * from the CPU-side buffer bound to each block's binding point. */
482
+ for (const WebGLUniformBlock &block : program_->blocks) {
483
+ if (block.binding < 0 || block.binding >= 16) continue;
484
+ const WebGLUniformBinding &binding = uniform_bindings_[block.binding];
485
+ if (binding.buffer == 0) continue;
486
+ WebGLBuffer *uniform_buffer = buffer(binding.buffer);
487
+ if (uniform_buffer == nullptr) continue;
488
+ size_t offset = binding.offset;
489
+ size_t length = (size_t)block.data_size;
490
+ if (binding.size > 0 && length > binding.size) length = binding.size;
491
+ if (offset > uniform_buffer->data.size()) continue;
492
+ if (length > uniform_buffer->data.size() - offset) {
493
+ length = uniform_buffer->data.size() - offset;
494
+ }
495
+ if (block.base_offset >= 0 &&
496
+ block.base_offset + (int)length <= program_->block_size) {
497
+ memcpy(program_->block.data() + block.base_offset,
498
+ uniform_buffer->data.data() + offset, length);
499
+ }
500
+ }
501
+
445
502
  if (program_->vs_has_block && program_->block_size > 0) {
446
503
  SDL_PushGPUVertexUniformData(cmd_, 0, program_->block.data(), (Uint32)program_->block.size());
447
504
  }
@@ -457,7 +514,15 @@ void WebGLContext::draw(int mode, int first, int count, int index_type, int offs
457
514
  const WebGLUniform &uniform = program_->uniforms[(size_t)uniform_index];
458
515
  const int unit =
459
516
  uniform.unit >= 0 && uniform.unit < kWebGLMaxTextureUnits ? uniform.unit : 0;
460
- WebGLTexture *sampled = texture(bound_textures_[unit]);
517
+ uint32_t texture_id = bound_textures_[unit];
518
+ if (uniform.layout.gl_type == GL_SAMPLER_CUBE) {
519
+ texture_id = bound_cube_textures_[unit];
520
+ } else if (uniform.layout.gl_type == GL_SAMPLER_3D) {
521
+ texture_id = bound_3d_textures_[unit];
522
+ } else if (uniform.layout.gl_type == GL_SAMPLER_2D_ARRAY) {
523
+ texture_id = bound_array_textures_[unit];
524
+ }
525
+ WebGLTexture *sampled = texture(texture_id);
461
526
  SDL_GPUTexture *gpu = sampled != nullptr ? ensure_texture_gpu(sampled, sampled->as_target)
462
527
  : nullptr;
463
528
  WebGLSampler *sampler_object = this->sampler(bound_samplers_[unit]);