xbintsc 0.5.32 → 0.5.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/README.md +10 -7
  2. package/README.zh-CN.md +10 -8
  3. package/dist/scripts/check-version.d.ts +27 -0
  4. package/dist/scripts/check-version.js +71 -0
  5. package/dist/scripts/check-version.js.map +1 -0
  6. package/dist/scripts/package-platform.d.ts +58 -0
  7. package/dist/scripts/package-platform.js +142 -0
  8. package/dist/scripts/package-platform.js.map +1 -0
  9. package/dist/scripts/verify-platform-package.d.ts +34 -0
  10. package/dist/scripts/verify-platform-package.js +161 -0
  11. package/dist/scripts/verify-platform-package.js.map +1 -0
  12. package/dist/src/cli/help.d.ts +1 -1
  13. package/dist/src/cli/main.js +3 -1
  14. package/dist/src/cli/main.js.map +1 -1
  15. package/dist/src/driver/runtime-lib.d.ts +29 -4
  16. package/dist/src/driver/runtime-lib.js +68 -10
  17. package/dist/src/driver/runtime-lib.js.map +1 -1
  18. package/dist/src/driver/version.d.ts +1 -1
  19. package/dist/src/driver/version.js +1 -1
  20. package/dist/src/extensions/gui/index.js +18 -5
  21. package/dist/src/extensions/gui/index.js.map +1 -1
  22. package/dist/src/extensions/media/index.js +4 -3
  23. package/dist/src/extensions/media/index.js.map +1 -1
  24. package/dist/src/parser/context.d.ts +41 -0
  25. package/dist/src/parser/context.js +89 -4
  26. package/dist/src/parser/context.js.map +1 -1
  27. package/dist/src/parser/literals.js +6 -12
  28. package/dist/src/parser/literals.js.map +1 -1
  29. package/dist/tests/driver/platform-package.test.d.ts +1 -0
  30. package/dist/tests/driver/platform-package.test.js +49 -0
  31. package/dist/tests/driver/platform-package.test.js.map +1 -0
  32. package/dist/tests/driver/runtime-lib.test.js +51 -4
  33. package/dist/tests/driver/runtime-lib.test.js.map +1 -1
  34. package/dist/tests/e2e/gui-audio.test.d.ts +14 -0
  35. package/dist/tests/e2e/gui-audio.test.js +287 -0
  36. package/dist/tests/e2e/gui-audio.test.js.map +1 -0
  37. package/dist/tests/e2e/gui-browser.test.js +65 -0
  38. package/dist/tests/e2e/gui-browser.test.js.map +1 -1
  39. package/dist/tests/e2e/gui-canvas-args.test.d.ts +6 -0
  40. package/dist/tests/e2e/gui-canvas-args.test.js +217 -0
  41. package/dist/tests/e2e/gui-canvas-args.test.js.map +1 -0
  42. package/dist/tests/e2e/gui-canvas.test.js.map +1 -1
  43. package/dist/tests/e2e/gui-dom.test.js +215 -0
  44. package/dist/tests/e2e/gui-dom.test.js.map +1 -1
  45. package/dist/tests/e2e/gui-events.test.js +112 -0
  46. package/dist/tests/e2e/gui-events.test.js.map +1 -1
  47. package/dist/tests/e2e/gui-helpers.d.ts +7 -0
  48. package/dist/tests/e2e/gui-helpers.js +1 -0
  49. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  50. package/dist/tests/e2e/gui-media.test.js +78 -0
  51. package/dist/tests/e2e/gui-media.test.js.map +1 -1
  52. package/dist/tests/e2e/gui-style.test.js +55 -0
  53. package/dist/tests/e2e/gui-style.test.js.map +1 -1
  54. package/dist/tests/e2e/gui-webgl.test.js +258 -0
  55. package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
  56. package/dist/tests/e2e/gui-webgl2-3d.test.d.ts +4 -0
  57. package/dist/tests/e2e/gui-webgl2-3d.test.js +264 -0
  58. package/dist/tests/e2e/gui-webgl2-3d.test.js.map +1 -0
  59. package/dist/tests/e2e/gui-webgl2-blit.test.d.ts +10 -0
  60. package/dist/tests/e2e/gui-webgl2-blit.test.js +262 -0
  61. package/dist/tests/e2e/gui-webgl2-blit.test.js.map +1 -0
  62. package/dist/tests/e2e/gui-webgl2-formats.test.d.ts +6 -0
  63. package/dist/tests/e2e/gui-webgl2-formats.test.js +395 -0
  64. package/dist/tests/e2e/gui-webgl2-formats.test.js.map +1 -0
  65. package/dist/tests/e2e/gui-webgl2-mrt.test.d.ts +4 -0
  66. package/dist/tests/e2e/gui-webgl2-mrt.test.js +105 -0
  67. package/dist/tests/e2e/gui-webgl2-mrt.test.js.map +1 -0
  68. package/dist/tests/e2e/gui-webgl2-ubo.test.d.ts +8 -0
  69. package/dist/tests/e2e/gui-webgl2-ubo.test.js +112 -0
  70. package/dist/tests/e2e/gui-webgl2-ubo.test.js.map +1 -0
  71. package/dist/tests/e2e/gui-webgl2.test.js +45 -1
  72. package/dist/tests/e2e/gui-webgl2.test.js.map +1 -1
  73. package/dist/tests/extensions/gui-index.test.js +1 -0
  74. package/dist/tests/extensions/gui-index.test.js.map +1 -1
  75. package/dist/tests/extensions/media.test.js +1 -0
  76. package/dist/tests/extensions/media.test.js.map +1 -1
  77. package/dist/tests/parser/expressions.test.js +30 -0
  78. package/dist/tests/parser/expressions.test.js.map +1 -1
  79. package/dist/tests/platform-tarball.test.d.ts +1 -0
  80. package/dist/tests/platform-tarball.test.js +111 -0
  81. package/dist/tests/platform-tarball.test.js.map +1 -0
  82. package/dist/tests/version.test.js +6 -0
  83. package/dist/tests/version.test.js.map +1 -1
  84. package/doc/ai/contributing.md +11 -0
  85. package/doc/ai/extensions.md +7 -4
  86. package/doc/ai/troubleshooting.md +7 -5
  87. package/doc/ai/zh-CN/contributing.md +9 -0
  88. package/doc/ai/zh-CN/extensions.md +4 -2
  89. package/doc/ai/zh-CN/troubleshooting.md +5 -3
  90. package/doc/gui/api.md +99 -22
  91. package/doc/gui/build.md +11 -4
  92. package/doc/gui/canvas.md +49 -18
  93. package/doc/gui/dom.md +57 -7
  94. package/doc/gui/media.md +4 -0
  95. package/doc/gui/webgl.md +105 -46
  96. package/doc/gui.md +7 -3
  97. package/doc/npm-package.md +55 -27
  98. package/doc/requirements.md +6 -2
  99. package/doc/zh-CN/gui/api.md +84 -14
  100. package/doc/zh-CN/gui/build.md +7 -2
  101. package/doc/zh-CN/gui/canvas.md +44 -19
  102. package/doc/zh-CN/gui/dom.md +48 -4
  103. package/doc/zh-CN/gui/media.md +3 -0
  104. package/doc/zh-CN/gui/webgl.md +22 -16
  105. package/doc/zh-CN/gui.md +3 -2
  106. package/doc/zh-CN/npm-package.md +44 -23
  107. package/doc/zh-CN/requirements.md +4 -1
  108. package/package.json +17 -1
  109. package/runtime/ext_gui/audio.cpp +365 -153
  110. package/runtime/ext_gui/audio_buffer.cpp +166 -0
  111. package/runtime/ext_gui/audio_buffer.h +37 -0
  112. package/runtime/ext_gui/audio_decode.cpp +219 -0
  113. package/runtime/ext_gui/audio_decode.h +39 -0
  114. package/runtime/ext_gui/audio_device.cpp +87 -0
  115. package/runtime/ext_gui/audio_effects.cpp +163 -0
  116. package/runtime/ext_gui/audio_effects.h +25 -0
  117. package/runtime/ext_gui/audio_internal.h +30 -0
  118. package/runtime/ext_gui/audio_mixer.cpp +494 -0
  119. package/runtime/ext_gui/audio_mixer.h +238 -0
  120. package/runtime/ext_gui/audio_param.cpp +104 -0
  121. package/runtime/ext_gui/audio_param_object.cpp +155 -0
  122. package/runtime/ext_gui/browser.cpp +45 -50
  123. package/runtime/ext_gui/browser_abort.cpp +249 -0
  124. package/runtime/ext_gui/browser_dialogs.cpp +76 -0
  125. package/runtime/ext_gui/browser_media.cpp +176 -0
  126. package/runtime/ext_gui/browser_navigation.cpp +130 -0
  127. package/runtime/ext_gui/canvas.cpp +88 -30
  128. package/runtime/ext_gui/canvas.h +74 -9
  129. package/runtime/ext_gui/canvas_image_data.cpp +143 -0
  130. package/runtime/ext_gui/canvas_internal.h +8 -5
  131. package/runtime/ext_gui/canvas_pattern.cpp +90 -0
  132. package/runtime/ext_gui/canvas_raster.cpp +31 -9
  133. package/runtime/ext_gui/canvas_style.cpp +4 -2
  134. package/runtime/ext_gui/css_selector.cpp +21 -3
  135. package/runtime/ext_gui/dom.h +4 -0
  136. package/runtime/ext_gui/dom_api.cpp +1 -0
  137. package/runtime/ext_gui/dom_api_canvas.cpp +120 -114
  138. package/runtime/ext_gui/dom_api_canvas_extra.cpp +57 -2
  139. package/runtime/ext_gui/dom_api_canvas_image.cpp +239 -0
  140. package/runtime/ext_gui/dom_api_canvas_paths.cpp +179 -0
  141. package/runtime/ext_gui/dom_api_computed_style.cpp +124 -0
  142. package/runtime/ext_gui/dom_api_dataset.cpp +154 -0
  143. package/runtime/ext_gui/dom_api_document.cpp +58 -5
  144. package/runtime/ext_gui/dom_api_events.cpp +93 -17
  145. package/runtime/ext_gui/dom_api_internal.h +71 -2
  146. package/runtime/ext_gui/dom_api_mutation.cpp +159 -0
  147. package/runtime/ext_gui/dom_api_node.cpp +40 -19
  148. package/runtime/ext_gui/dom_api_node_read.cpp +37 -1
  149. package/runtime/ext_gui/dom_api_path2d.cpp +358 -0
  150. package/runtime/ext_gui/dom_api_proto.cpp +35 -0
  151. package/runtime/ext_gui/dom_api_style.cpp +223 -13
  152. package/runtime/ext_gui/dom_api_webgl.cpp +7 -0
  153. package/runtime/ext_gui/dom_api_webgl2.cpp +151 -36
  154. package/runtime/ext_gui/dom_api_webgl2_draw.cpp +3 -3
  155. package/runtime/ext_gui/dom_api_webgl_buffer.cpp +6 -2
  156. package/runtime/ext_gui/dom_api_webgl_compressed.cpp +167 -0
  157. package/runtime/ext_gui/dom_api_webgl_framebuffer.cpp +160 -0
  158. package/runtime/ext_gui/dom_api_webgl_shader.cpp +4 -4
  159. package/runtime/ext_gui/dom_api_webgl_source.cpp +161 -0
  160. package/runtime/ext_gui/dom_api_webgl_state.cpp +169 -23
  161. package/runtime/ext_gui/dom_api_webgl_texture.cpp +276 -236
  162. package/runtime/ext_gui/dom_api_webgl_ubo.cpp +81 -0
  163. package/runtime/ext_gui/gui.cpp +103 -14
  164. package/runtime/ext_gui/gui_engine.h +56 -4
  165. package/runtime/ext_gui/gui_events.cpp +80 -9
  166. package/runtime/ext_gui/image_bitmap.cpp +28 -0
  167. package/runtime/ext_gui/image_bitmap.h +26 -0
  168. package/runtime/ext_gui/image_bitmap_browser.cpp +136 -0
  169. package/runtime/ext_gui/listener_options.h +43 -0
  170. package/runtime/ext_gui/media.cpp +32 -0
  171. package/runtime/ext_gui/media.h +13 -0
  172. package/runtime/ext_gui/media_api.h +23 -0
  173. package/runtime/ext_gui/paint.cpp +28 -0
  174. package/runtime/ext_gui/paint.h +4 -0
  175. package/runtime/ext_gui/webgl.h +152 -18
  176. package/runtime/ext_gui/webgl_blit.cpp +95 -0
  177. package/runtime/ext_gui/webgl_buffer.cpp +178 -263
  178. package/runtime/ext_gui/webgl_buffer_objects.cpp +270 -0
  179. package/runtime/ext_gui/webgl_clear.cpp +142 -0
  180. package/runtime/ext_gui/webgl_compressed.cpp +180 -0
  181. package/runtime/ext_gui/webgl_context.cpp +241 -156
  182. package/runtime/ext_gui/webgl_draw.cpp +68 -3
  183. package/runtime/ext_gui/webgl_draw_buffers.cpp +75 -0
  184. package/runtime/ext_gui/webgl_glsl.h +40 -0
  185. package/runtime/ext_gui/webgl_glsl_compile.cpp +6 -0
  186. package/runtime/ext_gui/webgl_glsl_rewrite.cpp +18 -0
  187. package/runtime/ext_gui/webgl_glsl_scan.cpp +20 -11
  188. package/runtime/ext_gui/webgl_glsl_scan_block.cpp +126 -0
  189. package/runtime/ext_gui/webgl_glsl_tokens.h +28 -0
  190. package/runtime/ext_gui/webgl_pipeline.cpp +71 -19
  191. package/runtime/ext_gui/webgl_program_blocks.cpp +58 -0
  192. package/runtime/ext_gui/webgl_program_internal.h +6 -0
  193. package/runtime/ext_gui/webgl_shader.cpp +13 -1
  194. package/runtime/ext_gui/webgl_state.cpp +228 -0
  195. package/runtime/ext_gui/webgl_texture.cpp +144 -17
  196. package/runtime/ext_gui/webgl_texture_3d.cpp +215 -0
  197. package/runtime/ext_gui/webgl_uniform_block.h +31 -0
  198. package/runtime/ext_gui/webgl_webgl2.cpp +58 -5
  199. package/runtime/ext_gui/window.cpp +14 -65
  200. package/runtime/ext_gui/window_events.cpp +125 -0
  201. package/runtime/ext_media/decode_audio.cpp +308 -0
  202. package/runtime/ext_media/media_bridge.c +2 -0
  203. package/runtime/rt.h +43 -0
  204. package/runtime/rt_internal.h +4 -0
  205. package/runtime/xt_array_buffer.c +30 -0
  206. package/runtime/xt_builtins.c +0 -21
  207. package/runtime/xt_containers/access.inc +94 -2
  208. package/runtime/xt_containers/objects.inc +29 -31
  209. package/runtime/xt_typed_array/elements.inc +13 -0
  210. package/scripts/build-media.ts +14 -6
  211. package/scripts/build-runtime.ts +21 -16
  212. package/scripts/check-version.ts +39 -9
  213. package/scripts/package-platform.ts +183 -0
  214. package/scripts/set-platform-version.ts +62 -0
  215. package/scripts/verify-platform-package.ts +182 -0
  216. package/src/cli/main.ts +5 -1
  217. package/src/driver/runtime-lib.ts +73 -9
  218. package/src/driver/version.ts +1 -1
  219. package/src/extensions/gui/index.ts +18 -5
  220. package/src/extensions/media/index.ts +4 -3
  221. package/src/parser/context.ts +99 -4
  222. package/src/parser/literals.ts +6 -12
@@ -1,10 +1,10 @@
1
1
  /*
2
- * xbintsc GUI engine — WebGL buffers, vertex-attribute state, texture objects
3
- * and framebuffers.
2
+ * xbintsc GUI engine — WebGL texture objects and framebuffers.
4
3
  *
5
4
  * Uploads are recorded immediately: a copy pass cannot run inside a render
6
- * pass, so a buffer/texture update ends the open pass first. The next draw
7
- * starts a new pass with `LOAD`, so the framebuffer keeps its contents.
5
+ * pass, so a texture update ends the open pass first. The next draw starts a
6
+ * new pass with `LOAD`, so the framebuffer keeps its contents. Buffer objects
7
+ * and vertex-attribute state live in `webgl_buffer_objects.cpp`.
8
8
  */
9
9
 
10
10
  #include "webgl.h"
@@ -39,6 +39,14 @@ bool uses_mipmaps(int gl_filter) {
39
39
  gl_filter == GL_NEAREST_MIPMAP_LINEAR || gl_filter == GL_LINEAR_MIPMAP_LINEAR;
40
40
  }
41
41
 
42
+ bool is_depth_format(SDL_GPUTextureFormat format) {
43
+ return format == SDL_GPU_TEXTUREFORMAT_D16_UNORM ||
44
+ format == SDL_GPU_TEXTUREFORMAT_D24_UNORM ||
45
+ format == SDL_GPU_TEXTUREFORMAT_D32_FLOAT ||
46
+ format == SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT ||
47
+ format == SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT;
48
+ }
49
+
42
50
  SDL_GPUSamplerAddressMode address_of(int wrap) {
43
51
  switch (wrap) {
44
52
  case GL_CLAMP_TO_EDGE:
@@ -50,248 +58,8 @@ SDL_GPUSamplerAddressMode address_of(int wrap) {
50
58
  }
51
59
  }
52
60
 
53
- bool upload_transfer(SDL_GPUDevice *device, SDL_GPUCommandBuffer *cmd, SDL_GPUBuffer *buffer,
54
- const void *bytes, uint32_t size) {
55
- if (size == 0) return true;
56
- SDL_GPUTransferBufferCreateInfo info;
57
- memset(&info, 0, sizeof(info));
58
- info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
59
- info.size = size;
60
- SDL_GPUTransferBuffer *transfer = SDL_CreateGPUTransferBuffer(device, &info);
61
- if (transfer == nullptr) return false;
62
- void *mapped = SDL_MapGPUTransferBuffer(device, transfer, false);
63
- if (mapped == nullptr) {
64
- SDL_ReleaseGPUTransferBuffer(device, transfer);
65
- return false;
66
- }
67
- memcpy(mapped, bytes, size);
68
- SDL_UnmapGPUTransferBuffer(device, transfer);
69
- SDL_GPUCopyPass *copy = SDL_BeginGPUCopyPass(cmd);
70
- SDL_GPUTransferBufferLocation source;
71
- memset(&source, 0, sizeof(source));
72
- source.transfer_buffer = transfer;
73
- SDL_GPUBufferRegion region;
74
- memset(&region, 0, sizeof(region));
75
- region.buffer = buffer;
76
- region.size = size;
77
- SDL_UploadToGPUBuffer(copy, &source, &region, false);
78
- SDL_EndGPUCopyPass(copy);
79
- SDL_ReleaseGPUTransferBuffer(device, transfer);
80
- return true;
81
- }
82
-
83
61
  } // namespace
84
62
 
85
- /* -- buffers ------------------------------------------------------------- */
86
-
87
- uint32_t WebGLContext::create_buffer() {
88
- auto record = std::make_unique<WebGLBuffer>();
89
- record->id = next_object_id_++;
90
- const uint32_t id = record->id;
91
- buffers_[id] = std::move(record);
92
- return id;
93
- }
94
-
95
- void WebGLContext::delete_buffer(uint32_t id) {
96
- auto found = buffers_.find(id);
97
- if (found == buffers_.end()) return;
98
- if (bound_array_ == id) bound_array_ = 0;
99
- if (bound_element_ == id) bound_element_ = 0;
100
- for (WebGLAttribState &attrib : attribs_) {
101
- if (attrib.buffer == id) attrib.buffer = 0;
102
- }
103
- if (found->second->gpu != nullptr && guidev::g_device != nullptr) {
104
- submit(); SDL_WaitForGPUIdle(guidev::g_device);
105
- SDL_ReleaseGPUBuffer(guidev::g_device, found->second->gpu);
106
- }
107
- buffers_.erase(found);
108
- }
109
-
110
- void WebGLContext::bind_buffer(int target, uint32_t id) {
111
- if (id != 0 && buffers_.find(id) == buffers_.end()) return;
112
- if (target == GL_ARRAY_BUFFER) bound_array_ = id;
113
- if (target == GL_ELEMENT_ARRAY_BUFFER) bound_element_ = id;
114
- if (target == GL_COPY_READ_BUFFER) copy_read_buffer_ = id;
115
- if (target == GL_COPY_WRITE_BUFFER) copy_write_buffer_ = id;
116
- }
117
-
118
- uint32_t WebGLContext::buffer_for_target(int target) const {
119
- if (target == GL_ELEMENT_ARRAY_BUFFER) return bound_element_;
120
- if (target == GL_ARRAY_BUFFER) return bound_array_;
121
- if (target == GL_COPY_READ_BUFFER) return copy_read_buffer_;
122
- if (target == GL_COPY_WRITE_BUFFER) return copy_write_buffer_;
123
- return 0;
124
- }
125
-
126
- bool WebGLContext::copy_buffer_sub_data(int read_target, int write_target, uint32_t read_offset,
127
- uint32_t write_offset, uint32_t size) {
128
- WebGLBuffer *read_buffer = buffer(buffer_for_target(read_target));
129
- WebGLBuffer *write_buffer = buffer(buffer_for_target(write_target));
130
- if (read_buffer == nullptr || write_buffer == nullptr ||
131
- read_offset + size > read_buffer->data.size() ||
132
- write_offset + size > write_buffer->data.size()) {
133
- set_error(GL_INVALID_VALUE);
134
- return false;
135
- }
136
- if (size > 0) {
137
- memmove(write_buffer->data.data() + write_offset,
138
- read_buffer->data.data() + read_offset, size);
139
- }
140
- write_buffer->dirty = true;
141
- return ensure_buffer_gpu(write_buffer) != nullptr || size == 0;
142
- }
143
-
144
- bool WebGLContext::get_buffer_sub_data(int target, uint32_t offset, uint8_t *out,
145
- uint32_t size) {
146
- WebGLBuffer *record = buffer(buffer_for_target(target));
147
- if (record == nullptr || offset + size > record->data.size()) {
148
- set_error(GL_INVALID_VALUE);
149
- return false;
150
- }
151
- if (size > 0) memcpy(out, record->data.data() + offset, size);
152
- return true;
153
- }
154
-
155
- SDL_GPUBuffer *WebGLContext::ensure_buffer_gpu(WebGLBuffer *buffer) {
156
- if (buffer == nullptr || guidev::g_device == nullptr) return nullptr;
157
- if (buffer->data.empty()) return nullptr;
158
- end_pass();
159
- ensure_command();
160
- if (cmd_ == nullptr) return nullptr;
161
- const uint32_t size = (uint32_t)buffer->data.size();
162
- if (buffer->gpu == nullptr || buffer->capacity < size) {
163
- if (buffer->gpu != nullptr) {
164
- submit(); SDL_WaitForGPUIdle(guidev::g_device);
165
- SDL_ReleaseGPUBuffer(guidev::g_device, buffer->gpu);
166
- }
167
- SDL_GPUBufferCreateInfo info;
168
- memset(&info, 0, sizeof(info));
169
- info.usage = SDL_GPU_BUFFERUSAGE_VERTEX | SDL_GPU_BUFFERUSAGE_INDEX;
170
- info.size = size;
171
- buffer->gpu = SDL_CreateGPUBuffer(guidev::g_device, &info);
172
- buffer->capacity = size;
173
- if (buffer->gpu == nullptr) {
174
- set_error(GL_OUT_OF_MEMORY);
175
- return nullptr;
176
- }
177
- }
178
- if (buffer->dirty) {
179
- /* Recreating the buffer above submitted the pending command buffer. */
180
- ensure_command();
181
- if (cmd_ == nullptr) return nullptr;
182
- if (!upload_transfer(guidev::g_device, cmd_, buffer->gpu, buffer->data.data(), size)) {
183
- set_error(GL_OUT_OF_MEMORY);
184
- return nullptr;
185
- }
186
- buffer->dirty = false;
187
- }
188
- return buffer->gpu;
189
- }
190
-
191
- bool WebGLContext::buffer_data(uint32_t id, const uint8_t *bytes, uint32_t size, int usage) {
192
- (void)usage;
193
- WebGLBuffer *record = buffer(id);
194
- if (record == nullptr) {
195
- set_error(GL_INVALID_OPERATION);
196
- return false;
197
- }
198
- record->data.assign(size, 0);
199
- if (bytes != nullptr && size > 0) memcpy(record->data.data(), bytes, size);
200
- record->dirty = true;
201
- return ensure_buffer_gpu(record) != nullptr || size == 0;
202
- }
203
-
204
- bool WebGLContext::buffer_sub_data(uint32_t id, uint32_t offset, const uint8_t *bytes,
205
- uint32_t size) {
206
- WebGLBuffer *record = buffer(id);
207
- if (record == nullptr || offset > record->data.size() ||
208
- offset + size > record->data.size()) {
209
- set_error(GL_INVALID_VALUE);
210
- return false;
211
- }
212
- memcpy(record->data.data() + offset, bytes, size);
213
- record->dirty = true;
214
- return ensure_buffer_gpu(record) != nullptr;
215
- }
216
-
217
- uint32_t WebGLContext::buffer_size(uint32_t id) {
218
- WebGLBuffer *record = buffer(id);
219
- return record != nullptr ? (uint32_t)record->data.size() : 0;
220
- }
221
-
222
- /* -- vertex attributes --------------------------------------------------- */
223
-
224
- void WebGLContext::enable_attrib(int index, bool enabled) {
225
- if (index < 0 || index >= kWebGLMaxAttribs) {
226
- set_error(GL_INVALID_VALUE);
227
- return;
228
- }
229
- attribs_[index].enabled = enabled;
230
- }
231
-
232
- bool WebGLContext::attrib_enabled(int index) const {
233
- return index >= 0 && index < kWebGLMaxAttribs && attribs_[index].enabled;
234
- }
235
-
236
- void WebGLContext::attrib_pointer(int index, int size, int type, bool normalized, int stride,
237
- int offset) {
238
- if (index < 0 || index >= kWebGLMaxAttribs) {
239
- set_error(GL_INVALID_VALUE);
240
- return;
241
- }
242
- WebGLAttribState &attrib = attribs_[index];
243
- attrib.buffer = bound_array_;
244
- attrib.size = size;
245
- attrib.type = type;
246
- attrib.normalized = normalized;
247
- attrib.integer = false;
248
- attrib.stride = stride > 0 ? stride : 0;
249
- attrib.offset = offset;
250
- }
251
-
252
- void WebGLContext::attrib_ipointer(int index, int size, int type, int stride, int offset) {
253
- if (index < 0 || index >= kWebGLMaxAttribs) {
254
- set_error(GL_INVALID_VALUE);
255
- return;
256
- }
257
- WebGLAttribState &attrib = attribs_[index];
258
- attrib.buffer = bound_array_;
259
- attrib.size = size;
260
- attrib.type = type;
261
- attrib.normalized = false;
262
- attrib.integer = true;
263
- attrib.stride = stride > 0 ? stride : 0;
264
- attrib.offset = offset;
265
- }
266
-
267
- void WebGLContext::attrib_divisor(int index, int divisor) {
268
- if (index < 0 || index >= kWebGLMaxAttribs) {
269
- set_error(GL_INVALID_VALUE);
270
- return;
271
- }
272
- /* SDL 3.2's `instance_step_rate` is reserved and must be 0, so only the two
273
- * rates WebGL programs use — per vertex (0) and per instance (1) — map onto
274
- * the backend. A larger divisor is clamped. */
275
- attribs_[index].divisor = divisor > 0 ? 1 : 0;
276
- }
277
-
278
- void WebGLContext::attrib_value(int index, int components, const float *values) {
279
- if (index < 0 || index >= kWebGLMaxAttribs) return;
280
- for (int i = 0; i < 4; i++) attribs_[index].current[i] = i < components ? values[i] : 0.0f;
281
- if (components < 4) attribs_[index].current[3] = 1.0f;
282
- }
283
-
284
- void WebGLContext::attrib_state(int index, int *size, int *type, int *stride, bool *normalized,
285
- float current[4]) const {
286
- if (index < 0 || index >= kWebGLMaxAttribs) return;
287
- const WebGLAttribState &attrib = attribs_[index];
288
- *size = attrib.size;
289
- *type = attrib.type;
290
- *stride = attrib.stride;
291
- *normalized = attrib.normalized;
292
- for (int i = 0; i < 4; i++) current[i] = attrib.current[i];
293
- }
294
-
295
63
  /* -- textures ------------------------------------------------------------ */
296
64
 
297
65
  uint32_t WebGLContext::create_texture() {
@@ -312,6 +80,29 @@ void WebGLContext::delete_texture(uint32_t id) {
312
80
  for (uint32_t &bound : bound_textures_) {
313
81
  if (bound == id) bound = 0;
314
82
  }
83
+ for (uint32_t &bound : bound_cube_textures_) {
84
+ if (bound == id) bound = 0;
85
+ }
86
+ for (uint32_t &bound : bound_array_textures_) {
87
+ if (bound == id) bound = 0;
88
+ }
89
+ for (uint32_t &bound : bound_3d_textures_) {
90
+ if (bound == id) bound = 0;
91
+ }
92
+ for (auto &entry : framebuffers_) {
93
+ bool cleared = false;
94
+ for (uint32_t &color : entry.second->color_textures) {
95
+ if (color == id) {
96
+ color = 0;
97
+ cleared = true;
98
+ }
99
+ }
100
+ if (cleared) {
101
+ entry.second->width = 0;
102
+ entry.second->height = 0;
103
+ }
104
+ if (entry.second->depth_texture == id) entry.second->depth_texture = 0;
105
+ }
315
106
  if (guidev::g_device != nullptr) {
316
107
  if (found->second->gpu != nullptr) {
317
108
  submit(); SDL_WaitForGPUIdle(guidev::g_device);
@@ -326,11 +117,31 @@ void WebGLContext::delete_texture(uint32_t id) {
326
117
 
327
118
  void WebGLContext::bind_texture(int target, uint32_t id) {
328
119
  if (id != 0 && textures_.find(id) == textures_.end()) return;
329
- /* Cube/array/3D textures are outside the supported subset; ignore the
330
- * binding instead of raising an error (three.js binds placeholder cube
331
- * textures even for unlit scenes). */
332
- if (target != GL_TEXTURE_2D) return;
333
- bound_textures_[active_unit_] = id;
120
+ uint32_t *slot = nullptr;
121
+ if (target == GL_TEXTURE_2D) {
122
+ slot = &bound_textures_[active_unit_];
123
+ } else if (target == GL_TEXTURE_CUBE_MAP) {
124
+ slot = &bound_cube_textures_[active_unit_];
125
+ } else if (target == GL_TEXTURE_2D_ARRAY) {
126
+ slot = &bound_array_textures_[active_unit_];
127
+ } else if (target == GL_TEXTURE_3D) {
128
+ slot = &bound_3d_textures_[active_unit_];
129
+ } else {
130
+ return;
131
+ }
132
+ *slot = id;
133
+ active_texture_target_[active_unit_] = target;
134
+ if (id != 0) {
135
+ WebGLTexture *record = texture(id);
136
+ if (record != nullptr) {
137
+ if (!record->defined && record->gpu == nullptr) {
138
+ record->target = target;
139
+ } else if (record->target != target) {
140
+ *slot = 0;
141
+ return;
142
+ }
143
+ }
144
+ }
334
145
  }
335
146
 
336
147
  void WebGLContext::active_texture(int unit) {
@@ -369,26 +180,65 @@ int WebGLContext::tex_parameter_value(uint32_t id, int pname) {
369
180
  SDL_GPUTexture *WebGLContext::ensure_texture_gpu(WebGLTexture *record, bool as_target) {
370
181
  if (record == nullptr || guidev::g_device == nullptr) return nullptr;
371
182
  if (record->width <= 0 || record->height <= 0) return record->gpu;
372
- if (record->gpu != nullptr && (!as_target || record->as_target)) return record->gpu;
183
+ const bool depth = is_depth_format(record->format);
184
+ record->depth = depth;
185
+ /* Allocate sample *and* target usage up front: attaching a texture that was
186
+ * already uploaded must not recreate it (shadow maps upload or attach in
187
+ * either order). Compressed textures are sample-only; layered textures can
188
+ * still be colour targets (a single layer is selected at render time). */
189
+ const uint32_t wanted =
190
+ record->compressed
191
+ ? SDL_GPU_TEXTUREUSAGE_SAMPLER
192
+ : (SDL_GPU_TEXTUREUSAGE_SAMPLER |
193
+ (depth ? SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET
194
+ : SDL_GPU_TEXTUREUSAGE_COLOR_TARGET));
195
+ if (record->gpu != nullptr && record->usage == wanted) {
196
+ if (as_target) {
197
+ if (depth) record->as_depth_target = true;
198
+ else if (!record->compressed) record->as_target = true;
199
+ }
200
+ return record->gpu;
201
+ }
373
202
  if (record->gpu != nullptr) {
374
203
  submit(); SDL_WaitForGPUIdle(guidev::g_device);
375
204
  SDL_ReleaseGPUTexture(guidev::g_device, record->gpu);
376
205
  record->gpu = nullptr;
377
206
  }
378
- record->as_target = as_target;
379
- record->levels = mip_levels_for(record->width, record->height);
207
+ record->as_target = as_target && !depth && !record->compressed;
208
+ record->as_depth_target = as_target && depth;
209
+ record->usage = wanted;
210
+ record->levels =
211
+ (depth || record->compressed) ? 1 : mip_levels_for(record->width, record->height);
212
+ SDL_GPUTextureType texture_type = SDL_GPU_TEXTURETYPE_2D;
213
+ int layers = 1;
214
+ if (record->target == GL_TEXTURE_CUBE_MAP) {
215
+ texture_type = SDL_GPU_TEXTURETYPE_CUBE;
216
+ layers = 6;
217
+ } else if (record->target == GL_TEXTURE_2D_ARRAY) {
218
+ texture_type = SDL_GPU_TEXTURETYPE_2D_ARRAY;
219
+ layers = record->layers > 0 ? record->layers : 1;
220
+ } else if (record->target == GL_TEXTURE_3D) {
221
+ texture_type = SDL_GPU_TEXTURETYPE_3D;
222
+ layers = record->layers > 0 ? record->layers : 1;
223
+ }
380
224
  SDL_GPUTextureCreateInfo info;
381
225
  memset(&info, 0, sizeof(info));
382
- info.type = SDL_GPU_TEXTURETYPE_2D;
226
+ info.type = texture_type;
383
227
  info.format = record->format;
384
- info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER;
385
- if (as_target) info.usage |= SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
228
+ info.usage = wanted;
386
229
  info.width = (Uint32)record->width;
387
230
  info.height = (Uint32)record->height;
388
- info.layer_count_or_depth = 1;
231
+ info.layer_count_or_depth = (Uint32)layers;
389
232
  info.num_levels = (Uint32)record->levels;
390
233
  info.sample_count = SDL_GPU_SAMPLECOUNT_1;
391
234
  record->gpu = SDL_CreateGPUTexture(guidev::g_device, &info);
235
+ if (record->gpu == nullptr && depth) {
236
+ /* Some backends cannot sample a depth-stencil format; an attachment-only
237
+ * texture is still useful for depth-only rendering. */
238
+ info.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET;
239
+ record->gpu = SDL_CreateGPUTexture(guidev::g_device, &info);
240
+ record->usage = info.usage;
241
+ }
392
242
  if (record->gpu == nullptr) set_error(GL_OUT_OF_MEMORY);
393
243
  return record->gpu;
394
244
  }
@@ -407,6 +257,14 @@ SDL_GPUSampler *WebGLContext::sampler_for(WebGLTexture *record) {
407
257
  info.address_mode_w = address_of(record->wrap_t);
408
258
  info.min_lod = 0.0f;
409
259
  info.max_lod = record->has_mipmaps ? 1000.0f : 0.0f;
260
+ if (record->depth) {
261
+ /* Depth formats are sampled unfiltered; a depth value has no meaning once
262
+ * interpolated across a texel boundary. */
263
+ info.min_filter = SDL_GPU_FILTER_NEAREST;
264
+ info.mag_filter = SDL_GPU_FILTER_NEAREST;
265
+ info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST;
266
+ info.max_lod = 0.0f;
267
+ }
410
268
  record->sampler = SDL_CreateGPUSampler(guidev::g_device, &info);
411
269
  return record->sampler;
412
270
  }
@@ -416,6 +274,16 @@ int WebGLContext::texture_width(uint32_t id) {
416
274
  return record != nullptr ? record->width : 0;
417
275
  }
418
276
 
277
+ int WebGLContext::texture_height(uint32_t id) {
278
+ WebGLTexture *record = texture(id);
279
+ return record != nullptr ? record->height : 0;
280
+ }
281
+
282
+ int WebGLContext::texture_format(uint32_t id) {
283
+ WebGLTexture *record = texture(id);
284
+ return record != nullptr ? (int)record->format : 0;
285
+ }
286
+
419
287
  /* -- framebuffers -------------------------------------------------------- */
420
288
 
421
289
  uint32_t WebGLContext::create_framebuffer() {
@@ -429,7 +297,8 @@ uint32_t WebGLContext::create_framebuffer() {
429
297
  void WebGLContext::delete_framebuffer(uint32_t id) {
430
298
  auto found = framebuffers_.find(id);
431
299
  if (found == framebuffers_.end()) return;
432
- if (bound_framebuffer_ == id) bind_framebuffer(0);
300
+ if (bound_framebuffer_ == id) bind_framebuffer(GL_FRAMEBUFFER, 0);
301
+ if (read_framebuffer_ == id) read_framebuffer_ = 0;
433
302
  if (found->second->depth != nullptr && found->second->depth_renderbuffer == 0 &&
434
303
  guidev::g_device != nullptr) {
435
304
  submit(); SDL_WaitForGPUIdle(guidev::g_device);
@@ -438,18 +307,47 @@ void WebGLContext::delete_framebuffer(uint32_t id) {
438
307
  framebuffers_.erase(found);
439
308
  }
440
309
 
441
- void WebGLContext::bind_framebuffer(uint32_t id) {
310
+ void WebGLContext::bind_framebuffer(int target, uint32_t id) {
442
311
  if (id != 0 && framebuffers_.find(id) == framebuffers_.end()) return;
312
+ if (target == GL_READ_FRAMEBUFFER) {
313
+ read_framebuffer_ = id;
314
+ read_buffer_ = id == 0 ? GL_BACK : GL_COLOR_ATTACHMENT0;
315
+ return;
316
+ }
317
+ if (target != GL_FRAMEBUFFER && target != GL_DRAW_FRAMEBUFFER) return;
443
318
  if (id != bound_framebuffer_) end_pass();
444
319
  bound_framebuffer_ = id;
320
+ if (target == GL_FRAMEBUFFER) read_framebuffer_ = id;
321
+ read_buffer_ = id == 0 ? GL_BACK : GL_COLOR_ATTACHMENT0;
445
322
  }
446
323
 
447
- bool WebGLContext::framebuffer_texture(uint32_t framebuffer_id, uint32_t texture_id) {
324
+ bool WebGLContext::framebuffer_texture(uint32_t framebuffer_id, uint32_t texture_id,
325
+ int attachment) {
448
326
  WebGLFramebuffer *record_framebuffer = framebuffer(framebuffer_id);
449
327
  WebGLTexture *record = texture(texture_id);
450
328
  if (record_framebuffer == nullptr) return false;
451
329
  if (texture_id != 0 && record == nullptr) return false;
452
- record_framebuffer->color_texture = texture_id;
330
+ if (attachment == GL_DEPTH_ATTACHMENT || attachment == GL_DEPTH_STENCIL_ATTACHMENT) {
331
+ record_framebuffer->depth_texture = texture_id;
332
+ record_framebuffer->depth_renderbuffer = 0;
333
+ if (record_framebuffer->depth != nullptr) {
334
+ submit(); SDL_WaitForGPUIdle(guidev::g_device);
335
+ SDL_ReleaseGPUTexture(guidev::g_device, record_framebuffer->depth);
336
+ record_framebuffer->depth = nullptr;
337
+ }
338
+ if (record != nullptr) {
339
+ record->as_depth_target = true;
340
+ ensure_texture_gpu(record, true);
341
+ if (record_framebuffer->width <= 0 || record_framebuffer->height <= 0) {
342
+ record_framebuffer->width = record->width;
343
+ record_framebuffer->height = record->height;
344
+ }
345
+ }
346
+ return true;
347
+ }
348
+ const int color_index = attachment - GL_COLOR_ATTACHMENT0;
349
+ if (color_index < 0 || color_index >= kWebGLMaxDrawBuffers) return false;
350
+ record_framebuffer->color_textures[color_index] = texture_id;
453
351
  if (record != nullptr) {
454
352
  record_framebuffer->width = record->width;
455
353
  record_framebuffer->height = record->height;
@@ -458,10 +356,25 @@ bool WebGLContext::framebuffer_texture(uint32_t framebuffer_id, uint32_t texture
458
356
  return true;
459
357
  }
460
358
 
359
+ bool WebGLContext::framebuffer_texture_layer(uint32_t framebuffer_id, uint32_t texture_id,
360
+ int attachment, int layer) {
361
+ if (!framebuffer_texture(framebuffer_id, texture_id, attachment)) return false;
362
+ const int index = attachment - GL_COLOR_ATTACHMENT0;
363
+ WebGLFramebuffer *record = framebuffer(framebuffer_id);
364
+ if (record != nullptr && index >= 0 && index < kWebGLMaxDrawBuffers) {
365
+ record->color_layers[index] = layer;
366
+ }
367
+ /* SDL_GPU 3.2 depth-stencil targets have no layer selector; the depth
368
+ * attachment still binds, without per-layer rendering. */
369
+ return true;
370
+ }
371
+
461
372
  bool WebGLContext::framebuffer_status(uint32_t id) {
462
373
  WebGLFramebuffer *framebuffer_ = framebuffer(id);
463
374
  if (framebuffer_ == nullptr) return false;
464
- if (framebuffer_->color_texture == 0) {
375
+ bool attached = false;
376
+ for (uint32_t color : framebuffer_->color_textures) attached = attached || color != 0;
377
+ if (!attached) {
465
378
  set_error(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
466
379
  return false;
467
380
  }
@@ -508,8 +421,10 @@ bool WebGLContext::renderbuffer_storage(uint32_t id, int width, int height, bool
508
421
  SDL_GPUTextureCreateInfo info;
509
422
  memset(&info, 0, sizeof(info));
510
423
  info.type = SDL_GPU_TEXTURETYPE_2D;
511
- info.format = stencil ? SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT
512
- : SDL_GPU_TEXTUREFORMAT_D32_FLOAT;
424
+ /* Every depth target uses the same depth-stencil format so one pipeline
425
+ * works with the default framebuffer and with render-to-texture targets;
426
+ * `stencil` only records whether the page asked for a stencil attachment. */
427
+ info.format = SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT;
513
428
  info.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET;
514
429
  info.width = (Uint32)width;
515
430
  info.height = (Uint32)height;