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
@@ -119,32 +119,102 @@ function importLocalNames(declaration: ImportDeclaration): string[] {
119
119
  interface CompiledModule {
120
120
  /** Hoisted `import`/`export … from` statements (specifiers rewritten). */
121
121
  imports: string[];
122
- /** Local names the hoisted imports introduce. */
122
+ /** Synthetic imports that ask the bundler to defer a local module. */
123
+ lazyImports: string[];
124
+ /** Statements that load deferred modules at script-run time. */
125
+ prelude: string[];
126
+ /** Local names the hoisted (eager) imports introduce. */
123
127
  localNames: string[];
124
128
  /** The module body, wrapped as a script function by the caller. */
125
129
  body: string;
126
130
  }
127
131
 
132
+ /**
133
+ * A relative script import is evaluated lazily (after `loadHTML`), like a
134
+ * browser module script. Bare specifiers stay eager: they usually name a
135
+ * compiler extension or a package whose top level does not touch the page.
136
+ */
137
+ function isDeferredSpecifier(specifier: string): boolean {
138
+ return specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/") ||
139
+ /^[A-Za-z]:[\\/]/.test(specifier);
140
+ }
141
+
142
+ /** `const { a, b: c } = load();` for a deferred import's named bindings. */
143
+ function deferredPrelude(alias: string, declaration: ImportDeclaration): string[] {
144
+ const clause = declaration.importClause;
145
+ if (!clause) return [`${alias}();`];
146
+ const lines: string[] = [];
147
+ if (clause.name) lines.push(`const ${clause.name.text} = ${alias}().default;`);
148
+ const bindings = clause.namedBindings;
149
+ if (bindings && bindings.kind === SyntaxKind.NamedImports) {
150
+ const pairs = bindings.elements
151
+ .filter((element) => !element.isTypeOnly)
152
+ .map((element) => {
153
+ const imported = element.propertyName?.text ?? element.name.text;
154
+ return imported === element.name.text ? element.name.text : `${imported}: ${element.name.text}`;
155
+ });
156
+ if (pairs.length > 0) lines.push(`const { ${pairs.join(", ")} } = ${alias}();`);
157
+ } else if (bindings && bindings.kind === SyntaxKind.NamespaceImport) {
158
+ lines.push(`const ${bindings.name.text} = ${alias}();`);
159
+ }
160
+ if (lines.length === 0) lines.push(`${alias}();`);
161
+ return lines;
162
+ }
163
+
128
164
  /** Split an external script module into hoisted imports and a body. */
129
- function compileModuleSource(htmlPath: string, scriptPath: string): CompiledModule {
130
- const source = readFileSync(scriptPath, "utf8");
165
+ function compileModuleSource(htmlPath: string, scriptPath: string, scriptIndex: number): CompiledModule {
166
+ /* Mask the normalized text: the parser indexes the `SourceFile` text, where
167
+ * `\r\n` already became `\n`, so blanking statement spans in the raw file
168
+ * would remove the wrong characters on a CRLF checkout. */
169
+ const source = new SourceFile(scriptPath, readFileSync(scriptPath, "utf8")).text;
131
170
  const bag = new DiagnosticBag();
132
171
  const parsed = new Parser(new SourceFile(scriptPath, source), bag).parseSourceFile();
133
172
  const chars = source.split("");
134
173
  const imports: string[] = [];
174
+ const lazyImports: string[] = [];
175
+ const prelude: string[] = [];
135
176
  const localNames: string[] = [];
177
+ let lazyIndex = 0;
136
178
  const blank = (start: number, end: number): void => {
137
179
  for (let i = start; i < end; i += 1) chars[i] = " ";
138
180
  };
181
+ const hasRuntimeBinding = (clause: ImportDeclaration["importClause"]): boolean => {
182
+ if (!clause) return true;
183
+ if (clause.name) return true;
184
+ const bindings = clause.namedBindings;
185
+ if (!bindings) return false;
186
+ if (bindings.kind === SyntaxKind.NamespaceImport) return true;
187
+ return bindings.elements.some((element) => !element.isTypeOnly);
188
+ };
189
+ const marker = (specifier: string): string => {
190
+ const alias = `__xt_deferred_${scriptIndex}_${lazyIndex}`;
191
+ lazyIndex += 1;
192
+ lazyImports.push(
193
+ `import { __xt_deferred_module as ${alias} } from ${JSON.stringify(rewriteSpecifier(htmlPath, scriptPath, specifier))};`,
194
+ );
195
+ return alias;
196
+ };
139
197
 
140
198
  for (const statement of parsed.statements) {
141
199
  if (statement.kind === SyntaxKind.ImportDeclaration) {
142
200
  const declaration = statement as ImportDeclaration;
201
+ const specifier = declaration.moduleSpecifier.value;
202
+ const clause = declaration.importClause;
203
+ if (
204
+ (clause === undefined || !clause.isTypeOnly) &&
205
+ hasRuntimeBinding(clause) &&
206
+ isDeferredSpecifier(specifier)
207
+ ) {
208
+ const alias = marker(specifier);
209
+ prelude.push(...deferredPrelude(alias, declaration));
210
+ blank(statement.start, statement.end);
211
+ continue;
212
+ }
143
213
  /* The front-end has no side-effect `import "x"` form; spell it as an
144
214
  * empty named import so the dependency is still loaded. */
145
- const hoisted = declaration.importClause
215
+ const hoisted = clause
146
216
  ? rewriteSpecifierText(htmlPath, scriptPath, source, statement, declaration.moduleSpecifier)
147
- : `import {} from ${JSON.stringify(rewriteSpecifier(htmlPath, scriptPath, declaration.moduleSpecifier.value))}`;
217
+ : `import {} from ${JSON.stringify(rewriteSpecifier(htmlPath, scriptPath, specifier))}`;
148
218
  imports.push(hoisted);
149
219
  localNames.push(...importLocalNames(declaration));
150
220
  blank(statement.start, statement.end);
@@ -153,7 +223,12 @@ function compileModuleSource(htmlPath: string, scriptPath: string): CompiledModu
153
223
  if (statement.kind === SyntaxKind.ExportDeclaration) {
154
224
  const declaration = statement as ExportDeclaration;
155
225
  if (declaration.moduleSpecifier) {
156
- imports.push(rewriteSpecifierText(htmlPath, scriptPath, source, statement, declaration.moduleSpecifier));
226
+ if (isDeferredSpecifier(declaration.moduleSpecifier.value)) {
227
+ const alias = marker(declaration.moduleSpecifier.value);
228
+ prelude.push(`${alias}();`);
229
+ } else {
230
+ imports.push(rewriteSpecifierText(htmlPath, scriptPath, source, statement, declaration.moduleSpecifier));
231
+ }
157
232
  }
158
233
  blank(statement.start, statement.end);
159
234
  continue;
@@ -173,7 +248,7 @@ function compileModuleSource(htmlPath: string, scriptPath: string): CompiledModu
173
248
  }
174
249
  }
175
250
 
176
- return { imports, localNames, body: chars.join("") };
251
+ return { imports, lazyImports, prelude, localNames, body: chars.join("") };
177
252
  }
178
253
 
179
254
  /** Resolve a `<script src>` value to a local file path (ignoring `?`/`#`). */
@@ -184,17 +259,22 @@ function resolveScriptPath(htmlPath: string, src: string): string {
184
259
 
185
260
  /** Rewrite `<script>` bodies into registered AOT functions + an HTML string. */
186
261
  export function loadHtmlAsset(path: string, source: string): AssetLoadResult {
262
+ /* The generated module is parsed as a `SourceFile`, which rewrites `\r\n` to
263
+ * `\n`; normalize first so the module text, the offsets recorded for
264
+ * `<script src>` bodies and the exported HTML string all share one layout. */
265
+ const page = new SourceFile(path, source).text;
187
266
  /* `file` is set for an external `<script src>`: its body keeps that file as
188
267
  * the origin of every node, so a `.js` script stays untyped and diagnostics
189
268
  * name the script, not the HTML page. */
190
- const scripts: Array<{ id: string; body: string; file?: string }> = [];
269
+ const scripts: Array<{ id: string; body: string; prelude: string[]; file?: string }> = [];
191
270
  const imports: string[] = [];
271
+ const lazyImports: string[] = [];
192
272
  const importedText = new Set<string>();
193
273
  const bound = new Set<string>();
194
274
 
195
275
  /* The engine's cascade only reads `<style>` elements, so a linked stylesheet
196
276
  * would be dropped at runtime. Inline it (and its `@import`s) now. */
197
- const styles = inlineStylesheets(path, source);
277
+ const styles = inlineStylesheets(path, page);
198
278
  const html = styles.html.replace(SCRIPT_RE, (match, attributes: string, inlineBody: string) => {
199
279
  if (!isExecutable(attribute(attributes, "type"), attribute(attributes, "lang"))) return match;
200
280
  const src = attribute(attributes, "src");
@@ -204,13 +284,14 @@ export function loadHtmlAsset(path: string, source: string): AssetLoadResult {
204
284
  if (!existsSync(scriptPath)) {
205
285
  throw new Error(`gui: <script src="${src}"> not found (looked for ${scriptPath})`);
206
286
  }
207
- const compiled = compileModuleSource(path, scriptPath);
287
+ const compiled = compileModuleSource(path, scriptPath, scripts.length);
208
288
  for (const text of compiled.imports) {
209
289
  if (!importedText.has(text)) {
210
290
  importedText.add(text);
211
291
  imports.push(text);
212
292
  }
213
293
  }
294
+ lazyImports.push(...compiled.lazyImports);
214
295
  for (const name of compiled.localNames) {
215
296
  if (bound.has(name)) {
216
297
  throw new Error(
@@ -219,8 +300,8 @@ export function loadHtmlAsset(path: string, source: string): AssetLoadResult {
219
300
  }
220
301
  bound.add(name);
221
302
  }
222
- const id = hashString(compiled.body);
223
- scripts.push({ id, body: compiled.body, file: scriptPath });
303
+ const id = hashString([...compiled.prelude, compiled.body].join("\n"));
304
+ scripts.push({ id, body: compiled.body, prelude: compiled.prelude, file: scriptPath });
224
305
  return `<script data-xt-id="${id}"></script>`;
225
306
  }
226
307
 
@@ -228,21 +309,23 @@ export function loadHtmlAsset(path: string, source: string): AssetLoadResult {
228
309
  if (src !== undefined) return match;
229
310
  if (inlineBody.trim() === "") return match;
230
311
  const id = hashString(inlineBody);
231
- scripts.push({ id, body: inlineBody });
312
+ scripts.push({ id, body: inlineBody, prelude: [] });
232
313
  return `<script data-xt-id="${id}"></script>`;
233
314
  });
234
315
 
235
- const parts: string[] = [...imports];
316
+ const parts: string[] = [...imports, ...lazyImports];
236
317
  const sourceRanges: AssetSourceRange[] = [];
237
318
  /* Offset of the next part once joined with "\n", so a body's span in the
238
319
  * generated module can be reported back as the file it was copied from. */
239
320
  let offset = parts.reduce((total, part) => total + part.length + 1, 0);
240
- for (const { id, body, file } of scripts) {
321
+ for (const { id, body, prelude, file } of scripts) {
241
322
  const header = `function __xt_script_${id}(window: any, document: any): void {\n`;
323
+ const preludeText = prelude.length > 0 ? `${prelude.join("\n")}\n` : "";
242
324
  const part =
243
- `${header}${body}\n}\n` + `__registerScript(${JSON.stringify(id)}, __xt_script_${id});`;
325
+ `${header}${preludeText}${body}\n}\n` + `__registerScript(${JSON.stringify(id)}, __xt_script_${id});`;
244
326
  if (file !== undefined) {
245
- sourceRanges.push({ start: offset + header.length, end: offset + header.length + body.length, file });
327
+ const bodyStart = offset + header.length + preludeText.length;
328
+ sourceRanges.push({ start: bodyStart, end: bodyStart + body.length, file });
246
329
  }
247
330
  parts.push(part);
248
331
  offset += part.length + 1;
@@ -202,6 +202,7 @@ export const guiExtension: Extension = {
202
202
  */
203
203
  builtins: () => ({
204
204
  __registerScript: { symbol: "xt_register_script" },
205
+ __xt_defer: { symbol: "xt_gui_defer_module" },
205
206
  requestAnimationFrame: { symbol: "xt_browser_request_animation_frame" },
206
207
  cancelAnimationFrame: { symbol: "xt_browser_cancel_animation_frame" },
207
208
  matchMedia: { symbol: "xt_browser_match_media" },
@@ -4,9 +4,10 @@
4
4
  * three.js's `WebGLRenderer` runs unchanged on this engine's real WebGL 2
5
5
  * backend; no Canvas2D fallback (and no renderer wrapper) is needed. The one
6
6
  * shader-side workaround is its `unrollLoops` pass: its lazy global regex does
7
- * not behave reliably on this runtime, and the shader rewriter already defines
8
- * `UNROLLED_LOOP_INDEX` and keeps the numeric `for` loop. Disabling the pattern
9
- * therefore leaves valid GLSL instead of a half-expanded loop.
7
+ * not behave reliably on this runtime. The shader rewriter expands
8
+ * `#pragma unroll_loop_start` itself (and substitutes `UNROLLED_LOOP_INDEX`),
9
+ * so disabling the JavaScript pass leaves valid GLSL instead of a
10
+ * half-expanded loop.
10
11
  */
11
12
 
12
13
  import type { AssetLoadResult } from "../registry.js";