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.
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/codegen/generator/module.js +23 -7
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.js +1 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.js +1 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/deferred.d.ts +63 -0
- package/dist/src/driver/bundler/deferred.js +279 -0
- package/dist/src/driver/bundler/deferred.js.map +1 -0
- package/dist/src/driver/bundler/merge.js +214 -2
- package/dist/src/driver/bundler/merge.js.map +1 -1
- package/dist/src/driver/runtime-objects.d.ts +1 -1
- package/dist/src/driver/runtime-objects.js +1 -0
- package/dist/src/driver/runtime-objects.js.map +1 -1
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/extensions/gui/html.js +98 -15
- package/dist/src/extensions/gui/html.js.map +1 -1
- package/dist/src/extensions/gui/index.js +1 -0
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/extensions/gui/three.d.ts +4 -3
- package/dist/src/extensions/gui/three.js +4 -3
- package/dist/src/extensions/gui/three.js.map +1 -1
- package/dist/tests/codegen/closures.test.js +7 -0
- package/dist/tests/codegen/closures.test.js.map +1 -1
- package/dist/tests/e2e/compile-objects-classes.test.js +31 -0
- package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
- package/dist/tests/e2e/gui-canvas-state.test.d.ts +9 -0
- package/dist/tests/e2e/gui-canvas-state.test.js +297 -0
- package/dist/tests/e2e/gui-canvas-state.test.js.map +1 -0
- package/dist/tests/e2e/gui-draw-perf.test.d.ts +22 -0
- package/dist/tests/e2e/gui-draw-perf.test.js +405 -0
- package/dist/tests/e2e/gui-draw-perf.test.js.map +1 -0
- package/dist/tests/e2e/gui-perf.test.d.ts +23 -0
- package/dist/tests/e2e/gui-perf.test.js +283 -0
- package/dist/tests/e2e/gui-perf.test.js.map +1 -0
- package/dist/tests/e2e/gui-script-import-order.test.d.ts +10 -0
- package/dist/tests/e2e/gui-script-import-order.test.js +169 -0
- package/dist/tests/e2e/gui-script-import-order.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl.test.js +4 -4
- package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
- package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.d.ts +8 -0
- package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.js +336 -0
- package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.d.ts +10 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.js +108 -0
- package/dist/tests/e2e/gui-webgl2-shadow.test.js.map +1 -0
- package/dist/tests/e2e/url.test.d.ts +6 -0
- package/dist/tests/e2e/url.test.js +114 -0
- package/dist/tests/e2e/url.test.js.map +1 -0
- package/dist/tests/extensions/gui.test.js +45 -10
- package/dist/tests/extensions/gui.test.js.map +1 -1
- package/doc/gui/api.md +28 -2
- package/doc/gui/canvas.md +110 -27
- package/doc/gui/known-issues.md +32 -10
- package/doc/gui/rendering.md +77 -0
- package/doc/gui/runtime.md +18 -3
- package/doc/gui/webgl.md +58 -29
- package/doc/gui-scripts.md +19 -19
- package/doc/gui.md +43 -9
- package/doc/implemented/fetch.md +5 -0
- package/doc/implemented/modules.md +13 -0
- package/doc/implemented/stdlib.md +17 -0
- package/doc/unimplemented/deviations.md +1 -0
- package/doc/zh-CN/gui/api.md +25 -2
- package/doc/zh-CN/gui/canvas.md +95 -26
- package/doc/zh-CN/gui/known-issues.md +21 -4
- package/doc/zh-CN/gui/rendering.md +59 -0
- package/doc/zh-CN/gui/runtime.md +13 -3
- package/doc/zh-CN/gui/webgl.md +30 -8
- package/doc/zh-CN/gui-scripts.md +14 -13
- package/doc/zh-CN/gui.md +32 -8
- package/doc/zh-CN/implemented/fetch.md +4 -0
- package/doc/zh-CN/implemented/modules.md +10 -0
- package/doc/zh-CN/implemented/stdlib.md +15 -0
- package/doc/zh-CN/unimplemented/deviations.md +1 -0
- package/package.json +7 -7
- package/runtime/ext_gui/browser.cpp +3 -1
- package/runtime/ext_gui/canvas.cpp +135 -89
- package/runtime/ext_gui/canvas.h +116 -0
- package/runtime/ext_gui/canvas_clip.cpp +143 -0
- package/runtime/ext_gui/canvas_composite.cpp +241 -0
- package/runtime/ext_gui/canvas_coverage.cpp +241 -0
- package/runtime/ext_gui/canvas_gradient.cpp +41 -4
- package/runtime/ext_gui/canvas_image_data.cpp +4 -0
- package/runtime/ext_gui/canvas_internal.h +34 -9
- package/runtime/ext_gui/canvas_raster.cpp +207 -178
- package/runtime/ext_gui/canvas_shadow.cpp +161 -0
- package/runtime/ext_gui/canvas_stroke.cpp +37 -9
- package/runtime/ext_gui/canvas_style.cpp +59 -0
- package/runtime/ext_gui/canvas_text.cpp +140 -0
- package/runtime/ext_gui/css.h +7 -0
- package/runtime/ext_gui/css_sheet.cpp +22 -0
- package/runtime/ext_gui/document.cpp +25 -1
- package/runtime/ext_gui/document.h +8 -0
- package/runtime/ext_gui/dom_api_canvas.cpp +16 -1
- package/runtime/ext_gui/dom_api_canvas_image.cpp +9 -0
- package/runtime/ext_gui/dom_api_canvas_state.cpp +217 -0
- package/runtime/ext_gui/dom_api_internal.h +36 -37
- package/runtime/ext_gui/dom_api_offscreen.cpp +213 -0
- package/runtime/ext_gui/dom_api_proto.cpp +18 -0
- package/runtime/ext_gui/dom_api_webgl.cpp +29 -9
- package/runtime/ext_gui/dom_api_webgl2.cpp +33 -1
- package/runtime/ext_gui/dom_api_webgl_internal.h +58 -0
- package/runtime/ext_gui/dom_api_webgl_source.cpp +18 -0
- package/runtime/ext_gui/gradient_values.cpp +12 -1
- package/runtime/ext_gui/gui.cpp +5 -0
- package/runtime/ext_gui/gui_canvas.cpp +4 -0
- package/runtime/ext_gui/gui_engine.h +32 -0
- package/runtime/ext_gui/gui_events.cpp +182 -58
- package/runtime/ext_gui/gui_media.cpp +3 -0
- package/runtime/ext_gui/image.cpp +69 -0
- package/runtime/ext_gui/image_bitmap_browser.cpp +11 -0
- package/runtime/ext_gui/layout.cpp +4 -0
- package/runtime/ext_gui/paint.cpp +19 -0
- package/runtime/ext_gui/paint_background.cpp +18 -1
- package/runtime/ext_gui/perf.cpp +171 -0
- package/runtime/ext_gui/perf.h +127 -0
- package/runtime/ext_gui/renderer.cpp +109 -41
- package/runtime/ext_gui/renderer.h +33 -9
- package/runtime/ext_gui/renderer_image.cpp +135 -65
- package/runtime/ext_gui/renderer_internal.h +15 -3
- package/runtime/ext_gui/renderer_text.cpp +24 -12
- package/runtime/ext_gui/script.cpp +31 -0
- package/runtime/ext_gui/style.cpp +101 -30
- package/runtime/ext_gui/style.h +6 -3
- package/runtime/ext_gui/style_print.cpp +7 -1
- package/runtime/ext_gui/text.cpp +23 -4
- package/runtime/ext_gui/text.h +29 -0
- package/runtime/ext_gui/text_cache.cpp +118 -0
- package/runtime/ext_gui/text_glyph_cache.cpp +100 -0
- package/runtime/ext_gui/webgl.h +48 -146
- package/runtime/ext_gui/webgl_blit.cpp +12 -1
- package/runtime/ext_gui/webgl_blit_depth.cpp +270 -0
- package/runtime/ext_gui/webgl_buffer.cpp +59 -1
- package/runtime/ext_gui/webgl_buffer_objects.cpp +106 -9
- package/runtime/ext_gui/webgl_clear.cpp +3 -0
- package/runtime/ext_gui/webgl_context.cpp +29 -3
- package/runtime/ext_gui/webgl_draw.cpp +75 -270
- package/runtime/ext_gui/webgl_draw_buffers.cpp +1 -0
- package/runtime/ext_gui/webgl_draw_setup.cpp +321 -0
- package/runtime/ext_gui/webgl_draw_state.h +130 -0
- package/runtime/ext_gui/webgl_glsl.h +3 -0
- package/runtime/ext_gui/webgl_glsl_scan.cpp +3 -8
- package/runtime/ext_gui/webgl_glsl_unroll.cpp +283 -0
- package/runtime/ext_gui/webgl_msl.cpp +4 -0
- package/runtime/ext_gui/webgl_objects.h +148 -0
- package/runtime/ext_gui/webgl_pipeline.cpp +8 -8
- package/runtime/ext_gui/webgl_program_internal.h +35 -0
- package/runtime/ext_gui/webgl_shader.cpp +34 -36
- package/runtime/ext_gui/webgl_webgl2.cpp +36 -0
- package/runtime/ext_gui/window.cpp +56 -1
- package/runtime/rt.h +3 -0
- package/runtime/xt_fetch/async.inc +60 -0
- package/runtime/xt_url_object.c +463 -0
- package/src/codegen/generator/module.ts +22 -7
- package/src/codegen/generator/state.ts +1 -0
- package/src/codegen/generator/tables.ts +1 -0
- package/src/driver/bundler/deferred.ts +351 -0
- package/src/driver/bundler/merge.ts +219 -2
- package/src/driver/runtime-objects.ts +1 -0
- package/src/driver/version.ts +1 -1
- package/src/extensions/gui/html.ts +100 -17
- package/src/extensions/gui/index.ts +1 -0
- package/src/extensions/gui/three.ts +4 -3
package/doc/gui/webgl.md
CHANGED
|
@@ -23,8 +23,11 @@ display.
|
|
|
23
23
|
Both **WebGL 1.0** and **WebGL 2.0** are available. A canvas keeps the first
|
|
24
24
|
context kind it asks for: after `getContext("webgl2")` a later
|
|
25
25
|
`getContext("webgl")` answers `null`, and the other way round. A WebGL 2 context
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
has its own `WebGL2RenderingContext` prototype (with a copy of the WebGL 1
|
|
27
|
+
constants and methods), so `gl instanceof WebGL2RenderingContext` is true while
|
|
28
|
+
`gl instanceof WebGLRenderingContext` is false, like a browser — libraries such
|
|
29
|
+
as three.js use exactly that check to pick their WebGL 2 path. `gl.constructor.name`
|
|
30
|
+
reports the type as well.
|
|
28
31
|
|
|
29
32
|
## How a frame reaches the screen
|
|
30
33
|
|
|
@@ -33,9 +36,10 @@ WebGL 1 one) and adds the WebGL 2 constants and methods.
|
|
|
33
36
|
`height` attributes. `canvas.width = …` recreates them and discards the
|
|
34
37
|
contents, like the DOM rule.
|
|
35
38
|
- The context records into its own SDL_GPU command buffer. `clear`/`draw*`
|
|
36
|
-
keep a render pass open; a buffer/texture upload or a framebuffer
|
|
37
|
-
it and starts a new pass with `LOAD`, so the drawing buffer
|
|
38
|
-
draws just like WebGL's
|
|
39
|
+
keep a render pass open; only a buffer/texture upload or a framebuffer
|
|
40
|
+
change ends it and starts a new pass with `LOAD`, so the drawing buffer
|
|
41
|
+
persists between draws just like WebGL's and a draw that has nothing to
|
|
42
|
+
upload never breaks the pass.
|
|
39
43
|
- Once per frame, before the page's own render pass, the engine submits every
|
|
40
44
|
WebGL command buffer. The image pipeline then samples the colour texture as
|
|
41
45
|
an ordinary `PaintImage`, with no CPU copy.
|
|
@@ -54,8 +58,9 @@ format, so the engine ships two statically linked translators:
|
|
|
54
58
|
|
|
55
59
|
- **glslang** compiles the rewritten GLSL to SPIR-V, which the Vulkan backend
|
|
56
60
|
consumes directly;
|
|
57
|
-
- **SPIRV-Cross** cross-compiles that same SPIR-V to Metal Shading Language
|
|
58
|
-
which the Metal backend
|
|
61
|
+
- **SPIRV-Cross** cross-compiles that same SPIR-V to Metal Shading Language 2.0
|
|
62
|
+
text (needed for arrays of textures/samplers), which the Metal backend
|
|
63
|
+
compiles at run time.
|
|
59
64
|
|
|
60
65
|
Both live in `gui.a` / `gui.lib`; the Metal path reuses the SPIR-V module rather
|
|
61
66
|
than growing a second GLSL front end, so both backends accept exactly the same
|
|
@@ -85,6 +90,8 @@ The translator (`runtime/ext_gui/webgl_glsl*.cpp`) handles GLSL ES 1.00 and
|
|
|
85
90
|
| non-sampler uniforms | one `layout(std140) uniform XtUniforms { … }` block |
|
|
86
91
|
| `uniform sampler2D u_tex` | `layout(set = 2, binding = N) uniform sampler2D u_tex` (fragment) or set 0 (vertex) |
|
|
87
92
|
| `gl_FragData[0]` | `xt_FragColor` |
|
|
93
|
+
| `gl_FragDepth` / `gl_FragDepthEXT` | kept (SPIR-V `DepthReplacing`; MSL `[[depth(any)]]`) |
|
|
94
|
+
| `sampler2DShadow` / `samplerCubeShadow` / `sampler2DArrayShadow` | kept; bound as an SDL_GPU comparison sampler |
|
|
88
95
|
| a missing `#version` / `#version 100` | `#version 310 es` |
|
|
89
96
|
|
|
90
97
|
Non-sampler uniforms are merged across both stages and emitted as one block in
|
|
@@ -96,9 +103,21 @@ reflected offsets; `uniform1i` on a sampler stores the texture unit. The block
|
|
|
96
103
|
is pushed with the dynamic uniform-buffer API on every draw.
|
|
97
104
|
|
|
98
105
|
Limits of the translator: struct-typed uniforms, user uniform blocks, uniform
|
|
99
|
-
arrays whose size is not resolved by the preprocessor,
|
|
100
|
-
|
|
101
|
-
|
|
106
|
+
arrays whose size is not resolved by the preprocessor, and a sampler array
|
|
107
|
+
indexed by a non-constant expression are rejected (the link/shader info log
|
|
108
|
+
says so). A `#pragma unroll_loop_start` loop is expanded before rewriting, so
|
|
109
|
+
the `sampler2D shadowMap[ NUM_DIR_LIGHT_SHADOWS ]` / `shadowMap[ i ]` shape
|
|
110
|
+
three.js emits becomes constant-indexed. Shader code inside `#ifdef` is fine;
|
|
111
|
+
non-sampler uniform declarations are moved to the enclosing top-level position
|
|
112
|
+
so the block stays unconditional.
|
|
113
|
+
|
|
114
|
+
A fragment shader may write `gl_FragDepth`; glslang marks the SPIR-V module
|
|
115
|
+
`DepthReplacing` and SPIRV-Cross emits `[[depth(any)]]`, so the write reaches the
|
|
116
|
+
depth attachment on both backends. The comparative samplers
|
|
117
|
+
(`sampler2DShadow`, `samplerCubeShadow`, `sampler2DArrayShadow`) use the same
|
|
118
|
+
SDL_GPU sampler objects with `enable_compare` set; `TEXTURE_COMPARE_MODE` and
|
|
119
|
+
`TEXTURE_COMPARE_FUNC` are read from the texture (or from a bound sampler
|
|
120
|
+
object in WebGL 2).
|
|
102
121
|
|
|
103
122
|
### GLSL ES 3.00 (WebGL 2)
|
|
104
123
|
|
|
@@ -169,9 +188,10 @@ available from GLSL ES 3.00, which is what the instanced example uses.
|
|
|
169
188
|
`framebufferTexture2D` (`COLOR_ATTACHMENT0`, `DEPTH_ATTACHMENT`,
|
|
170
189
|
`DEPTH_STENCIL_ATTACHMENT`), `checkFramebufferStatus`, `createRenderbuffer`,
|
|
171
190
|
`bindRenderbuffer`, `renderbufferStorage` (depth or depth-stencil),
|
|
172
|
-
`framebufferRenderbuffer`, `delete*`, and `blitFramebuffer` (colour
|
|
173
|
-
|
|
174
|
-
for render-to-texture,
|
|
191
|
+
`framebufferRenderbuffer`, `delete*`, and `blitFramebuffer` (colour through
|
|
192
|
+
SDL's blit; depth through a sampling pass that writes `gl_FragDepth`; stencil
|
|
193
|
+
bits are accepted but not copied). This is enough for render-to-texture,
|
|
194
|
+
shadow maps and post-processing passes.
|
|
175
195
|
- **State**: `enable`/`disable`/`isEnabled` for `BLEND`, `DEPTH_TEST`,
|
|
176
196
|
`CULL_FACE`, `SCISSOR_TEST` and `STENCIL_TEST`,
|
|
177
197
|
`clearColor`/`clearDepth`/`clearStencil`, `clear` (with `SCISSOR_TEST` it
|
|
@@ -240,12 +260,17 @@ backend's `[0, 1]` clip space rather than GL's `[-1, 1] → [0, 1]` remap:
|
|
|
240
260
|
|
|
241
261
|
## Not implemented
|
|
242
262
|
|
|
243
|
-
Transform feedback (the entry points are no-ops
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
263
|
+
Transform feedback (the entry points are no-ops: SDL_GPU has no transform
|
|
264
|
+
feedback stage) and MSAA/sample buffers (SDL_GPU 3.2 can resolve multisampled
|
|
265
|
+
colour targets, but its Metal resolve returns undefined pixels with scissor
|
|
266
|
+
tests and instanced draws, so contexts stay single-sampled and `SAMPLE_BUFFERS`
|
|
267
|
+
reports 0). `blitFramebuffer` copies colour and depth; stencil bits are accepted
|
|
268
|
+
but not copied because SDL_GPU cannot sample a stencil attachment.
|
|
269
|
+
|
|
270
|
+
Shader limitations that remain: struct-typed uniforms and user uniform blocks
|
|
271
|
+
are flattened, but built-in interface blocks (`gl_PerVertex` redeclarations) are
|
|
272
|
+
not part of the translated subset. A shader that uses one still fails with a
|
|
273
|
+
compile/link log rather than silently misbehaving.
|
|
249
274
|
|
|
250
275
|
## Platform
|
|
251
276
|
|
|
@@ -267,7 +292,10 @@ uses the 2D canvas or the DOM pays no run-time cost beyond the archive size.
|
|
|
267
292
|
- `tests/e2e/gui-webgl.test.ts` compiles GLSL, draws a textured and a
|
|
268
293
|
depth-tested quad, checks face culling and reads the pixels back; it also
|
|
269
294
|
covers blend constants, dynamic buffer usage, vertex-shader texture sampling,
|
|
270
|
-
`gl_FragDepth
|
|
295
|
+
`gl_FragDepth`, context loss/restore and cube maps.
|
|
296
|
+
- `tests/e2e/gui-webgl2-shadow-sampler.test.ts` covers hardware
|
|
297
|
+
`sampler2DShadow` comparisons, a shader-written `gl_FragDepth` that occludes a
|
|
298
|
+
later draw, and a partial `blitFramebuffer` depth copy.
|
|
271
299
|
- `tests/e2e/gui-webgl2.test.ts` covers the WebGL 2 shader/state additions:
|
|
272
300
|
`#version 300 es` shaders, a VAO round trip, instanced draws with a divisor,
|
|
273
301
|
`texStorage2D`, `clearBufferfv`, buffer copies, sampler objects, struct-uniform
|
|
@@ -299,17 +327,18 @@ same assertions as the SPIR-V one.
|
|
|
299
327
|
|
|
300
328
|
## three.js
|
|
301
329
|
|
|
302
|
-
The
|
|
303
|
-
|
|
304
|
-
|
|
330
|
+
The shader rewriter expands three.js's `#pragma unroll_loop_start` loops into
|
|
331
|
+
constant indices (its sampler arrays would otherwise fail Vulkan GLSL), and the
|
|
332
|
+
`.js` asset loader keeps the renderer's own unroller out of the way.
|
|
333
|
+
`WebGLRenderer` runs unchanged on this engine's real WebGL 2 backend, so **no
|
|
334
|
+
Canvas2D fallback is bundled any more**. Verified with three.js r160 through SDL_GPU:
|
|
305
335
|
`MeshBasicMaterial`, `MeshStandardMaterial` and `MeshPhongMaterial`,
|
|
306
336
|
`CanvasTexture`, linear fog, ambient/directional/point/spot/hemisphere lights,
|
|
307
337
|
`Sprite`, `Line`, `Points` and `InstancedMesh`.
|
|
308
338
|
|
|
309
339
|
Features outside this engine's subset still cannot be emulated: transform
|
|
310
|
-
feedback and MSAA.
|
|
311
|
-
|
|
312
|
-
three.js shadow maps
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
through the GPU.
|
|
340
|
+
feedback and MSAA. Depth textures, float render targets and compressed textures
|
|
341
|
+
are available, and comparative `sampler2DShadow` sampling is translated, so
|
|
342
|
+
three.js shadow maps can use the hardware depth compare on both backends. A
|
|
343
|
+
scene that needs transform feedback or MSAA will not render correctly;
|
|
344
|
+
everything the previous Canvas2D fallback could show now goes through the GPU.
|
package/doc/gui-scripts.md
CHANGED
|
@@ -155,17 +155,18 @@ export default __html;
|
|
|
155
155
|
### 2b. External `<script src>`
|
|
156
156
|
|
|
157
157
|
`<script src="./app.ts">` is read relative to the HTML file, parsed, and split:
|
|
158
|
-
its top-level `import`/`export … from` statements are
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
158
|
+
its top-level `import`/`export … from` statements are removed from the body, and
|
|
159
|
+
the remaining statements are wrapped in the script function. Relative imports
|
|
160
|
+
are **deferred**, like a browser module script: the loader emits a synthetic
|
|
161
|
+
`__xt_deferred_module` marker and the generated script calls the module wrapper
|
|
162
|
+
at `loadHTML` time. The bundler wraps the whole local import graph in functions
|
|
163
|
+
that return their exports (memoised by `__xt_defer`), so top-level module code
|
|
164
|
+
runs after the document exists and two scripts importing the same module share
|
|
165
|
+
one instance. Bare specifiers (`gui`, a package) stay eager.
|
|
166
|
+
|
|
167
|
+
A missing `src` file is still a clear error (turned into a bundler diagnostic),
|
|
168
|
+
and `src` URLs (`https://…`, `data:…`, `//host`) are left untouched — they are
|
|
169
|
+
not compile-time assets and never run.
|
|
169
170
|
|
|
170
171
|
`src` URLs (`https://…`, `data:…`, `//host`) are left untouched — they are not
|
|
171
172
|
compile-time assets and never run.
|
|
@@ -302,11 +303,11 @@ legacy `target` field over the handle.
|
|
|
302
303
|
- **M9b** ✅ — gui `.html` loader (`src/extensions/gui/html.ts`), the
|
|
303
304
|
`__registerScript` builtin, the script registry (`runtime/ext_gui/script.cpp`),
|
|
304
305
|
`loadHTML` execution and `DOMContentLoaded`/`load`; e2e coverage.
|
|
305
|
-
- **M9c** ✅ — `<script src>` files are read
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
306
|
+
- **M9c** ✅ — `<script src>` files are read and their body is wrapped and
|
|
307
|
+
registered; relative imports are deferred and evaluated by a memoised
|
|
308
|
+
module wrapper at `loadHTML` time, so the import graph sees a parsed
|
|
309
|
+
document and shares one instance per module. Missing files surface as
|
|
310
|
+
diagnostics. All scripts run in document order. e2e coverage.
|
|
310
311
|
- **M9d** *(optional)* — detect inline HTML in template literals passed to
|
|
311
312
|
`win.loadHTML(...)` and transform them too (fragile; deferred).
|
|
312
313
|
- **M10 — polish** ✅ — `requestAnimationFrame`/`cancelAnimationFrame` on the
|
|
@@ -330,9 +331,8 @@ legacy `target` field over the handle.
|
|
|
330
331
|
network fetches) do not run. Documented.
|
|
331
332
|
- **Template-literal HTML** passed to `win.loadHTML(…)` is not transformed in
|
|
332
333
|
M9b (only imported assets are). M9d addresses this if needed.
|
|
333
|
-
- **
|
|
334
|
-
|
|
335
|
-
is possible later.
|
|
334
|
+
- **Top-level `await`** inside a deferred `<script src>` module is not
|
|
335
|
+
supported: the module body is wrapped in a synchronous init function.
|
|
336
336
|
- **Assets.** `<script src>` is resolved relative to the HTML asset; URL `src`
|
|
337
337
|
values are ignored. There is no fetch/network loading.
|
|
338
338
|
|
package/doc/gui.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# xbintsc GUI extension (self-hosted HTML/CSS renderer)
|
|
2
2
|
|
|
3
|
-
Status: **
|
|
3
|
+
Status: **M20** — features (M1–M15) are complete: HTML parsing, CSS selector
|
|
4
4
|
matching, the cascade, computed styles and layout (block, inline, Flexbox and
|
|
5
5
|
CSS positioning) are
|
|
6
6
|
in place, and the engine *paints*: it builds a display list of rectangles, images
|
|
@@ -44,6 +44,10 @@ them) can be translated, scaled and rotated without reflowing anything — and
|
|
|
44
44
|
`clip-path` and the `filter` colour/shadow effects. `backdrop-filter` and true
|
|
45
45
|
group blur need an offscreen compositor and remain documented approximations or
|
|
46
46
|
unsupported — see [Rendering](./gui/rendering.md#backgrounds-generated-content-and-effects-m19).
|
|
47
|
+
**M20** makes large documents interactive: `@media` is evaluated once per rule,
|
|
48
|
+
layout and paint share a bounded shaping cache, and a frame only does (and
|
|
49
|
+
presents) the work a change requires — see
|
|
50
|
+
[Performance](./gui/rendering.md#performance-m20).
|
|
47
51
|
This overview records the locked decisions, the architecture, the milestone plan
|
|
48
52
|
and the current progress; the engine itself is documented by function in the
|
|
49
53
|
pages above.
|
|
@@ -219,10 +223,11 @@ engine were Rust.
|
|
|
219
223
|
registered through the `__registerScript` builtin and replaced by
|
|
220
224
|
`<script data-xt-id="<hash>">` markers; `win.loadHTML` runs the matching
|
|
221
225
|
functions after parsing and fires `DOMContentLoaded` then `load`.
|
|
222
|
-
- **M9c** ✅ — `<script src>` files are read relative to the HTML
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
+
- **M9c** ✅ — `<script src>` files are read relative to the HTML and their
|
|
227
|
+
body is wrapped/registered; relative imports are deferred to `loadHTML`
|
|
228
|
+
through a memoised module wrapper, so imported modules see the parsed
|
|
229
|
+
document and share state. Missing files become diagnostics. Everything
|
|
230
|
+
runs in document order.
|
|
226
231
|
10. **M10 — polish** ✅
|
|
227
232
|
- `win.requestAnimationFrame(fn)` / `win.cancelAnimationFrame(id)`; callbacks
|
|
228
233
|
run at the top of each frame with the frame timestamp and may mutate the
|
|
@@ -341,6 +346,18 @@ engine were Rust.
|
|
|
341
346
|
offscreen backdrop pass (see [Known issues](./gui/known-issues.md)).
|
|
342
347
|
- E2E coverage in `tests/e2e/gui-effects.test.ts`; see
|
|
343
348
|
[Rendering](./gui/rendering.md#backgrounds-generated-content-and-effects-m19).
|
|
349
|
+
20. **M20 — rendering performance** ✅
|
|
350
|
+
- `@media` evaluated once per rule per restyle, generated content skipped for
|
|
351
|
+
sheets without pseudo-elements, and `:hover`/`:focus` restyle only when
|
|
352
|
+
the sheet tracks them.
|
|
353
|
+
- Bounded two-generation shaping cache shared by layout and paint, plus a
|
|
354
|
+
primary-face cache.
|
|
355
|
+
- Frame loop: reconcile media/canvas only when the document changed, present
|
|
356
|
+
only when something happened, and gate the whole pass behind it.
|
|
357
|
+
- Reused display list/vertex staging/transfer buffers, one upload submit per
|
|
358
|
+
dirty frame, and redundant render-state binds removed.
|
|
359
|
+
- `win.stats()`/`XT_GUI_PERF` counters and `tests/e2e/gui-perf.test.ts`; see
|
|
360
|
+
[Performance](./gui/rendering.md#performance-m20).
|
|
344
361
|
|
|
345
362
|
## Progress log
|
|
346
363
|
|
|
@@ -387,10 +404,11 @@ engine were Rust.
|
|
|
387
404
|
bodies into AOT functions registered at startup and run by `win.loadHTML`
|
|
388
405
|
(before first layout; `DOMContentLoaded` then `load`). Unit tests in
|
|
389
406
|
`tests/extensions/gui.test.ts`, e2e in `tests/e2e/gui-*.test.ts`.
|
|
390
|
-
- **M9c** ✅ external `<script src>` files are read
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
caught by `loadGraph`
|
|
407
|
+
- **M9c** ✅ external `<script src>` files are read and their body
|
|
408
|
+
wrapped/registered; relative imports are deferred to `loadHTML` through a
|
|
409
|
+
memoised module wrapper (so module-level DOM work sees the parsed document),
|
|
410
|
+
while missing files are diagnostics. Loader errors are caught by `loadGraph`
|
|
411
|
+
and reported as build errors.
|
|
394
412
|
- **M10** ✅ `requestAnimationFrame`/`cancelAnimationFrame` on window handles
|
|
395
413
|
(callbacks run with the frame timestamp before layout each frame) plus
|
|
396
414
|
`offsetWidth`/`offsetHeight`/`contains` on element handles; e2e coverage in
|
|
@@ -488,4 +506,20 @@ engine were Rust.
|
|
|
488
506
|
functions plus nine-tap blur/drop-shadow and emits `text-shadow` runs, and
|
|
489
507
|
`paint_clip.cpp`/`renderer_clip.cpp` clip geometry against triangulated
|
|
490
508
|
`clip-path` polygons. E2E: `tests/e2e/gui-effects.test.ts`.
|
|
509
|
+
- **M20** ✅ rendering performance. `@media` is evaluated once per rule per
|
|
510
|
+
restyle, generated content is skipped for sheets without `::before`/`::after`,
|
|
511
|
+
and `:hover`/`:focus` only restyle when the sheet tracks them
|
|
512
|
+
(`style.cpp`, `css_sheet.cpp`). A bounded two-generation shaping cache
|
|
513
|
+
(`text_cache.cpp`) serves layout and paint, so a word is shaped once instead
|
|
514
|
+
of on every layout pass and repaint; primary faces are cached by
|
|
515
|
+
(class, size) too. The frame loop reconciles decoders/canvases only when the
|
|
516
|
+
document changed and presents only when something happened
|
|
517
|
+
(`gui_events.cpp`), so a static page does no GPU work
|
|
518
|
+
(`XT_GUI_ALWAYS_PRESENT=1` restores the old behaviour for debugging). The
|
|
519
|
+
display list, vertex staging and transfer buffers are reused, a dirty frame's
|
|
520
|
+
uploads share one command buffer, and the renderer only re-binds state that
|
|
521
|
+
changed (`renderer*.cpp`, `gui_engine.h`). `win.stats()` reports per-frame
|
|
522
|
+
timings and cumulative work counters and `XT_GUI_PERF` prints them
|
|
523
|
+
(`perf.{h,cpp}`); `tests/e2e/gui-perf.test.ts` pins a 200-row list. See
|
|
524
|
+
[Performance](./gui/rendering.md#performance-m20).
|
|
491
525
|
|
package/doc/implemented/fetch.md
CHANGED
|
@@ -13,6 +13,11 @@ Location: `runtime/xt_fetch.c`, `runtime/xt_fetch/async.inc`, `runtime/xt_async_
|
|
|
13
13
|
exchange, up to 20 redirects, and a 301/302/303 switches POST to GET.
|
|
14
14
|
- It accepts `http://` URLs only — `https://` rejects with a `TypeError` because
|
|
15
15
|
no TLS backend is linked.
|
|
16
|
+
- A reference with **no scheme** (a relative path) and `file://` URLs read a
|
|
17
|
+
local file, so a compiled program can load an asset from disk without an HTTP
|
|
18
|
+
server. Relative paths resolve against the working directory; `GET` / `HEAD`
|
|
19
|
+
are supported. On Windows the standard `file:///C:/dir/file` spelling and
|
|
20
|
+
`file://C:/dir/file` / `file://C:\dir\file` all name the same file.
|
|
16
21
|
- Options: `method`, `headers` (plain object or `Headers`), `body` and `redirect`
|
|
17
22
|
(`follow` / `manual` / `error`).
|
|
18
23
|
- The resolved `Response`-shaped object exposes `ok` / `status` / `statusText` /
|
|
@@ -19,6 +19,19 @@ Location: `src/parser/parser.ts` (syntax), `src/driver/modules.ts` (bundling),
|
|
|
19
19
|
- Imported bindings and namespace members are snapshots at module-evaluation time (no live bindings) — see [Unimplemented: module system](../unimplemented/modules.md).
|
|
20
20
|
- Type-only imports and re-exports (`import type { T } from "..."`, `import { type T } from "..."`, `export type { T } from "..."`) are erased. They are **not** module edges: the target is not loaded or bundled, and a type-only cycle between modules is not a circular dependency. Use a side-effect import (`import "./mod"`) when a module must be evaluated without binding a name.
|
|
21
21
|
|
|
22
|
+
## Deferred evaluation
|
|
23
|
+
|
|
24
|
+
- The `gui` `.html` asset loader marks relative `<script src>` imports with a
|
|
25
|
+
synthetic `__xt_deferred_module` binding. The bundler then wraps that module
|
|
26
|
+
and its local import graph in functions that return their exports, and the
|
|
27
|
+
generated script calls them after `loadHTML` (`__xt_defer` memoises each
|
|
28
|
+
module). This matches a browser module script, where the graph is evaluated
|
|
29
|
+
once the document exists.
|
|
30
|
+
- Modules also reachable through a normal (non-marker) import stay eager and get
|
|
31
|
+
a thin wrapper over their already-initialised exports.
|
|
32
|
+
- The wrapper body is synchronous: top-level `await` inside a deferred script
|
|
33
|
+
module is not supported.
|
|
34
|
+
|
|
22
35
|
## import.meta
|
|
23
36
|
|
|
24
37
|
- `import.meta.url` / `.dirname` / `.filename`, resolved from the running executable's path.
|
|
@@ -55,6 +55,23 @@ own pages: [Collections](./collections.md), [Typed arrays and binary data](./typ
|
|
|
55
55
|
- `Symbol(description)` primitives (`XT_OBJECT_KIND_SYMBOL`), the 13 well-known symbols (`Symbol.iterator`, `Symbol.asyncIterator`, `Symbol.match`, ...), `Symbol.for` / `Symbol.keyFor` global registry, `symbol.description` / `toString()` / `valueOf()`.
|
|
56
56
|
- Symbols are valid property keys (`Object.getOwnPropertySymbols`, symbol keyed `get`/`set`/`in`/`delete`), are skipped by `Object.keys` / `values` / `entries` / `for...in` / `JSON.stringify`, and print as `Symbol(desc)`.
|
|
57
57
|
|
|
58
|
+
## URL
|
|
59
|
+
|
|
60
|
+
- `URL` is implemented in `runtime/xt_url_object.c`: `new URL(input)` and
|
|
61
|
+
`new URL(input, base)` (the base may be another `URL`), resolving relative
|
|
62
|
+
references against a hierarchical `scheme://authority/path` base.
|
|
63
|
+
- A reference with **no scheme is a filesystem path**: `new URL("./model.glb")`
|
|
64
|
+
resolves against the working directory and yields a `file:` URL (an explicit
|
|
65
|
+
base is still honoured, so `new URL("./x", import.meta.url)` works too).
|
|
66
|
+
- Read-only `href`, `protocol`, `origin`, `host`, `hostname`, `port`,
|
|
67
|
+
`pathname`, `search`, `hash`, `searchParams` and `toString()` / `toJSON()`;
|
|
68
|
+
absolute inputs are kept as written, except that the Windows spellings of a
|
|
69
|
+
`file:` URL (`file://C:/dir` and `file://C:\dir`) are canonicalized to
|
|
70
|
+
`file:///C:/dir`, matching Node.
|
|
71
|
+
- Not a full WHATWG parser: path percent-encoding, IDNA, setters and live
|
|
72
|
+
`searchParams` synchronisation are not implemented — see
|
|
73
|
+
[Runtime deviations](../unimplemented/deviations.md).
|
|
74
|
+
|
|
58
75
|
## URLSearchParams
|
|
59
76
|
|
|
60
77
|
- `URLSearchParams` is implemented in `runtime/xt_url.c`.
|
|
@@ -8,6 +8,7 @@ These features **compile and run**, but the result does not fully match ECMAScri
|
|
|
8
8
|
| --- | --- |
|
|
9
9
|
| RegExp byte-oriented matching | The engine matches UTF-8 bytes: literal multi-byte text works, but `.` and character classes consume one byte per repetition (`\d` / `\w` stay ASCII). Offsets returned by `exec` / `search` / replacement callbacks are converted to UTF-16 code units |
|
|
10
10
|
| Global RegExp `lastIndex` | `test` / `exec` do not advance or honour a caller-set `lastIndex` for `/g` / `/y` regexes |
|
|
11
|
+
| `URL` parser | Read-only WHATWG subset: no path percent-encoding, IDNA, setters or live `searchParams`; a reference with no scheme resolves against the working directory as a `file:` URL |
|
|
11
12
|
| String `normalize` | Not implemented |
|
|
12
13
|
| First-class built-in methods | Implemented as unbound method values (see [Functions](./functions.md)); `fn.toString()` returns a placeholder rather than source text |
|
|
13
14
|
| `for...in` | Enumerates own keys of objects / arrays / strings; does not include prototype-chain properties |
|
package/doc/zh-CN/gui/api.md
CHANGED
|
@@ -91,8 +91,9 @@ requestAnimationFrame(frame);
|
|
|
91
91
|
| `Event`、`CustomEvent`、`ProgressEvent`、`MouseEvent`、`KeyboardEvent`、`PointerEvent`、`WheelEvent`、`FocusEvent`、`InputEvent`、`SubmitEvent` | 事件构造器;`init` 参数按原样保留(`detail`、`composed`、鼠标/键盘字段等) |
|
|
92
92
|
| `AbortController`、`AbortSignal` | 真实的 controller/signal 对,用于 `{ signal }` 移除监听器,支持 `abort()`、`aborted`、`reason`、`onabort` 与 `throwIfAborted()` |
|
|
93
93
|
| `HTMLCanvasElement`、`HTMLImageElement`、`WebGLRenderingContext`、`WebGL2RenderingContext`、`WebGLBuffer`/`WebGLTexture`/`WebGLShader`/`WebGLProgram`/`WebGLFramebuffer`/`WebGLRenderbuffer`/`WebGLUniformLocation`/`WebGLActiveInfo`/`WebGLVertexArrayObject`/`WebGLSampler`/`WebGLQuery`/`WebGLSync`/`WebGLTransformFeedback` | 真实的构造函数值。`canvas.getContext("webgl")` 与 `getContext("webgl2")` 返回 GPU 上下文(见 [WebGL](./webgl.md))。 |
|
|
94
|
-
| `ImageBitmap`、`createImageBitmap(source)` | 由已解码像素支撑的不可变位图(`<img>`、`<canvas>`、`<video>`
|
|
95
|
-
| `
|
|
94
|
+
| `ImageBitmap`、`createImageBitmap(source)` | 由已解码像素支撑的不可变位图(`<img>`、`<canvas>`、`<video>` 帧、`OffscreenCanvas` 与 `ImageData` 形状);promise 立即 settle,`texImage2D` 可直接接受该对象 |
|
|
95
|
+
| `OffscreenCanvas` | 真正的 2D 离屏位图,带 `getContext("2d")`、`width`/`height`、`transferToImageBitmap()` 与 `convertToBlob()`;`canvas.transferControlToOffscreen()` 会把元素位图交给它(见 [Canvas](./canvas.md))。离屏 WebGL 上下文不提供。 |
|
|
96
|
+
| `ImageData`、`XRWebGL*`、`Request`、`Headers`、`Response`、`ReadableStream`、`DOMParser` | `typeof` 探测返回 `"undefined"`(没有 XR/fetch/流式后端),让页面走自己的“不支持”分支,而不是调用空壳。Three.js 页面在本后端上使用真实渲染器(见[已知问题](./known-issues.md))。 |
|
|
96
97
|
| `requestAnimationFrame` / `cancelAnimationFrame` | 排入窗口的动画帧列表,见[运行时集成](./runtime.md) |
|
|
97
98
|
| `alert` / `confirm` / `prompt`、`matchMedia`、`getComputedStyle`、`escape` | 宿主实现。`matchMedia` 返回**实时**列表(按查询缓存):`matches` 在窗口尺寸变化时重新求值,翻转时触发 `change` 监听器。`confirm`/`prompt` 仅在 `XT_GUI_DIALOGS=1` 时使用原生对话框,否则把消息写到 stderr 并返回文档化的默认值(Annex B `escape` 按 UTF-16 码元编码,与 `decodeURIComponent(escape(bytes))` 一致) |
|
|
98
99
|
|
|
@@ -197,6 +198,28 @@ win.stats() // 引擎/运行时计数器(内存排
|
|
|
197
198
|
`detachedNodes` 或 `handles` 持续上升,说明程序移除节点的速度快于释放速度
|
|
198
199
|
(见[节点生命周期](./dom.md))。
|
|
199
200
|
|
|
201
|
+
同一个对象还报告上一帧做了什么以及程序累计做了多少工作,渲染开销就是靠它度量
|
|
202
|
+
(见[性能](./rendering.md#性能m20)):
|
|
203
|
+
|
|
204
|
+
| 字段 | 含义 |
|
|
205
|
+
| --- | --- |
|
|
206
|
+
| `frameMs`、`restyleMs`、`layoutMs`、`paintMs`、`uploadMs`、`drawMs` | 上一帧各阶段耗时(毫秒) |
|
|
207
|
+
| `frames`、`presents`、`exposes`、`deferred`、`skippedFrames`、`starvedFrames`、`transitionFrames` | 帧循环活动:`presents` 是提交呈现的帧数,`exposes` 是窗口系统用 expose/shown/restore 事件要求的呈现帧数,`deferred` 是取不到可绘制目标而被推迟的呈现帧数,`skippedFrames` 是无需提交的帧数,`starvedFrames` 是要求呈现却拿不到可绘制目标的帧数 |
|
|
208
|
+
| `restyles`、`restyleNodes`、`rulesEvaluated`、`mediaEvaluated`、`layouts`、`layoutBoxes` | 层叠与布局的工作量 |
|
|
209
|
+
| `textShapeCalls`、`textShapeHits`、`textCacheBytes`、`textCacheEntries` | 整形缓存的未命中/命中与占用 |
|
|
210
|
+
| `paints`、`paintOps`、`drawRuns`、`drawCalls`、`uploadSubmits`、`vertices`、`syncNodes` | 显示列表、绘制调用与上传的工作量 |
|
|
211
|
+
| `canvasDraws`、`canvasUploads`、`canvasUploadBytes` | Canvas 2D 绘制次数、纹理上传次数与拷贝字节数(局部重绘只上传脏矩形) |
|
|
212
|
+
| `webglDraws`、`webglPasses`、`webglPipelineMisses`、`webglBufferUploadBytes` | WebGL 绘制次数、打开的渲染 pass 数、新建管线数与缓冲拷贝字节数 |
|
|
213
|
+
|
|
214
|
+
除 `*Ms` 时间外,所有字段都是累计值,因此两次快照相减即得到一帧的工作量。
|
|
215
|
+
`restyleMs` 覆盖层叠**以及**它触发的布局过程(布局过程本身也单独报告为
|
|
216
|
+
`layoutMs`)。`presents - exposes - deferred` 才是仅由页面要求的呈现帧数:
|
|
217
|
+
expose/shown/restore 事件表示窗口内容已丢失、必须重绘,而具体时机由窗口服务器
|
|
218
|
+
决定;deferred 呈现则是重放某一帧在取不到交换链图像时已经构建好的内容,
|
|
219
|
+
`starvedFrames` 统计这些帧(该值持续增长说明窗口完全没有被更新)。
|
|
220
|
+
`XT_GUI_PERF=1` 会把同样的数字打印到 stderr(逐帧一行;`XT_GUI_PERF=<N>` 则每
|
|
221
|
+
N 帧取平均)。
|
|
222
|
+
|
|
200
223
|
它们被 `tests/e2e/gui-*.test.ts` 用来断言解析、选择器匹配、特异性、继承、
|
|
201
224
|
`!important` 与布局几何。它们在之后仍可用于调试。
|
|
202
225
|
|
package/doc/zh-CN/gui/canvas.md
CHANGED
|
@@ -11,9 +11,11 @@ CSS 盒中。
|
|
|
11
11
|
|
|
12
12
|
引擎侧的对象是 `xtgui::Canvas2D`(`runtime/ext_gui/canvas.{h,cpp}`)——一块 RGBA8 位图
|
|
13
13
|
加上 2D 绘制状态。光栅器位于 `canvas_raster.cpp`,描边几何(端点、连接、虚线)位于
|
|
14
|
-
`canvas_stroke.cpp`,渐变位于 `canvas_gradient.cpp
|
|
15
|
-
`
|
|
16
|
-
|
|
14
|
+
`canvas_stroke.cpp`,渐变位于 `canvas_gradient.cpp`,合成模式位于
|
|
15
|
+
`canvas_composite.cpp`,裁剪与命中测试位于 `canvas_clip.cpp`,阴影位于
|
|
16
|
+
`canvas_shadow.cpp`,绘制状态解析位于 `canvas_style.cpp`;DOM 绑定位于
|
|
17
|
+
`dom_api_canvas*.cpp` / `dom_api_offscreen.cpp`;按窗口的生命周期位于
|
|
18
|
+
`gui_canvas.cpp`。
|
|
17
19
|
|
|
18
20
|
## 获取上下文
|
|
19
21
|
|
|
@@ -31,6 +33,15 @@ const ctx = canvas.getContext("2d"); // 或 "webgl" 取得 GPU 上下文
|
|
|
31
33
|
元素上,因此每次都返回**同一个**对象,绘制状态(变换、样式、路径)按 canvas 而非按调用
|
|
32
34
|
保存。`ctx.canvas` 是所属元素的句柄。
|
|
33
35
|
|
|
36
|
+
`OffscreenCanvas` 提供同样的 2D 接口,但没有 DOM 元素:
|
|
37
|
+
`new OffscreenCanvas(width, height)` 分配一块由窗口拥有的位图,`getContext("2d")`
|
|
38
|
+
返回的上下文其 `canvas` 指向离屏对象;`canvas.transferControlToOffscreen()` 会把一个
|
|
39
|
+
活动元素的位图交给离屏对象(此后元素的 `getContext(…)` 返回 `null`)。
|
|
40
|
+
`drawImage`、`createPattern`、`createImageBitmap` 与 `texImage2D` 都接受
|
|
41
|
+
`OffscreenCanvas` 作为源。`transferToImageBitmap()` 返回 `ImageBitmap`,
|
|
42
|
+
`convertToBlob()` 解析为与 `toBlob` 相同的 PNG blob 形状(本运行时没有 worker,
|
|
43
|
+
离屏画布也不提供 WebGL 上下文)。
|
|
44
|
+
|
|
34
45
|
`getContext("2d", attributes)` 在首次调用时会尊重 `alpha`:传入 `{ alpha: false }`
|
|
35
46
|
时位图以不透明黑色初始化(清除也回到不透明黑)。其余属性由
|
|
36
47
|
`getContextAttributes()` 按规范默认值报告(`antialias: true`、`depth: true`、
|
|
@@ -42,8 +53,30 @@ const ctx = canvas.getContext("2d"); // 或 "webgl" 取得 GPU 上下文
|
|
|
42
53
|
光栅器刻意保持精简且为自研(不依赖 Skia/Cairo)。它按每条像素行 **4 条垂直子扫描线**
|
|
43
54
|
进行抗锯齿,并做一次**水平解析覆盖率**计算;路径默认采用**非零环绕规则**填充(因此当弧线
|
|
44
55
|
以相反方向环绕时能形成空洞),`fill("evenodd")` 则改用奇偶规则。轴对齐矩形(变换中
|
|
45
|
-
`b == 0 && c == 0
|
|
46
|
-
|
|
56
|
+
`b == 0 && c == 0`)走直接的覆盖率快速路径。
|
|
57
|
+
|
|
58
|
+
混合默认是非预乘 source-over。`globalCompositeOperation` 支持完整的 canvas 集合:
|
|
59
|
+
Porter-Duff 算子(`source-over`、`source-in`、`source-out`、`source-atop`、
|
|
60
|
+
`destination-over`、`destination-in`、`destination-out`、`destination-atop`、
|
|
61
|
+
`lighter`、`copy`、`xor`)以及可分离/不可分离混合模式(`multiply`、`screen`、
|
|
62
|
+
`overlay`、`darken`、`lighten`、`color-dodge`、`color-burn`、`hard-light`、
|
|
63
|
+
`soft-light`、`difference`、`exclusion`、`hue`、`saturation`、`color`、
|
|
64
|
+
`luminosity`)。抗锯齿与裁剪区域产生的部分覆盖率会在“未改动的目标”与“完整合成结果”
|
|
65
|
+
之间插值,因此每个算子在部分覆盖的像素上都正确。
|
|
66
|
+
|
|
67
|
+
`clip()` 用当前路径(或 `Path2D` 加可选的 `"nonzero"`/`"evenodd"` 规则)与当前裁剪区
|
|
68
|
+
求交。区域以设备空间覆盖率掩码保存,因此它固定在调用 `clip()` 时的变换下;
|
|
69
|
+
`save`/`restore` 会一并恢复,位图尺寸变化会清空它。所有填充、描边、文本、图像绘制与
|
|
70
|
+
`clearRect` 都遵守该掩码。
|
|
71
|
+
|
|
72
|
+
阴影(`shadowColor`、`shadowBlur`、`shadowOffsetX`、`shadowOffsetY`)由绘制操作自身的
|
|
73
|
+
alpha 生成:先把操作渲染到临时图层,对其 alpha 做偏移与盒式模糊(三次可分离模糊),用
|
|
74
|
+
`shadowColor` 着色后合成,最后把未处理的图层画在其上。偏移与模糊使用画布(设备)单位,
|
|
75
|
+
不受变换影响,符合 API 规定。`clearRect` 与 `putImageData` 从不产生阴影。
|
|
76
|
+
|
|
77
|
+
`isPointInPath(x, y[, fillRule])`、`isPointInPath(path, x, y[, fillRule])`、
|
|
78
|
+
`isPointInStroke(x, y)` 与 `isPointInStroke(path, x, y)` 在当前变换下对当前路径(或
|
|
79
|
+
传入的 `Path2D`)做命中测试;描边命中测试使用到各展平线段的设备空间距离与当前线宽。
|
|
47
80
|
|
|
48
81
|
填充或描边可以使用纯色,也可以使用 **`CanvasGradient`**。渐变的坐标属于用户空间,
|
|
49
82
|
因此光栅器会先用当前变换的逆矩阵把每个设备像素映射回用户空间再采样——形状被平移、
|
|
@@ -54,6 +87,27 @@ Canvas 像素**不参与垃圾回收**:canvas 由窗口拥有、以 DOM 节点
|
|
|
54
87
|
`<video>`/`<audio>` 解码表结构相同)。当节点被丢弃(`loadHTML`、窗口关闭/退出,或丢弃
|
|
55
88
|
子树的变更操作)时随之释放,并释放对应的 GPU 纹理。
|
|
56
89
|
|
|
90
|
+
## 性能
|
|
91
|
+
|
|
92
|
+
canvas 由脚本重绘而不是引擎重绘,因此开销按绘制调用计。多元素场景下的热路径刻意
|
|
93
|
+
保持少分支:
|
|
94
|
+
|
|
95
|
+
- 纯色不透明的 `fillRect` 直接按字节写入位图(小数边缘仍走抗锯齿路径),覆盖整张
|
|
96
|
+
位图的 `clearRect` 退化为一次 `memset`;
|
|
97
|
+
- 路径覆盖率使用逐行**活动边表**光栅化,并且只物化每一行**实际覆盖的区间**——一条
|
|
98
|
+
细对角线描边只花它自己的像素,而不是整个包围盒;
|
|
99
|
+
- 一次 `stroke()` 把所有线段四边形、连接与端点合并成一次非零环绕填充,而不是每段
|
|
100
|
+
各做一次覆盖率计算(先统一每个多边形的绕向,重叠处才能覆盖而不是抵消);
|
|
101
|
+
- `fillText` 与布局共享整形缓存(`text_cache.cpp`),并使用有界两代字形位图缓存
|
|
102
|
+
(`text_glyph_cache.cpp`);
|
|
103
|
+
- 整数偏移、无缩放的 `drawImage` 逐像素只取一个纹素,不再跑双线性采样器。
|
|
104
|
+
|
|
105
|
+
位图通过**脏矩形**到达屏幕:每次绘制把它的设备空间包围盒并入待上传区域,渲染器只
|
|
106
|
+
拷贝该区域到 canvas 纹理。一帧内所有 canvas 与视频上传共用帧的唯一上传命令缓冲,
|
|
107
|
+
因此 K 张动画 canvas 只需一次提交而不是 K 次。`getImageData` 始终读取 CPU 位图,
|
|
108
|
+
不受影响。`tests/e2e/gui-draw-perf.test.ts` 测量这些多元素路径并固定计数器行为
|
|
109
|
+
(每帧一次上传、只传脏字节)。
|
|
110
|
+
|
|
57
111
|
## 支持的 API
|
|
58
112
|
|
|
59
113
|
状态与变换:`save` / `restore`(会恢复变换、线宽、透明度、字体/文本设置**以及**
|
|
@@ -64,30 +118,37 @@ Canvas 像素**不参与垃圾回收**:canvas 由窗口拥有、以 DOM 节点
|
|
|
64
118
|
`translate(x, y); rotate(a)` 围绕平移后的原点旋转。
|
|
65
119
|
|
|
66
120
|
样式与设置:`fillStyle` / `strokeStyle`(层叠所用的颜色解析器能识别的 CSS 颜色,或
|
|
67
|
-
`CanvasGradient`)、`lineWidth`、`globalAlpha`、`
|
|
121
|
+
`CanvasGradient`)、`lineWidth`、`globalAlpha`、`globalCompositeOperation`、
|
|
122
|
+
`shadowColor` / `shadowBlur` / `shadowOffsetX` / `shadowOffsetY`、
|
|
123
|
+
`imageSmoothingEnabled` / `imageSmoothingQuality`、`lineCap`(`butt`/`round`/`square`)、
|
|
68
124
|
`lineJoin`(`miter`/`round`/`bevel`)、`miterLimit`、`lineDashOffset`、`font`
|
|
69
125
|
(CSS 字体简写,解析出字体族、字号与粗体/斜体——默认 `10px sans-serif`)、
|
|
70
126
|
`textAlign`(`start`/`left`、`center`、`right`/`end`)与 `textBaseline`
|
|
71
127
|
(`alphabetic`、`top`、`middle`、`bottom`、`hanging`、`ideographic`)。读取属性返回
|
|
72
|
-
|
|
128
|
+
上次赋予的值;赋予无法解析的颜色会保留原值。`imageSmoothingEnabled` 默认为 `true`
|
|
129
|
+
(双线性采样);`imageSmoothingQuality` 报告 `low`、`medium` 或 `high`,本后端都使用
|
|
130
|
+
同一种双线性过滤。
|
|
73
131
|
|
|
74
132
|
渐变:`createLinearGradient(x0, y0, x1, y1)` 与
|
|
75
133
|
`createRadialGradient(x0, y0, r0, x1, y1, r1)` 返回带
|
|
76
134
|
`addColorStop(offset, color)` 的 `CanvasGradient`。色标是**活的**:在
|
|
77
135
|
`fillStyle = gradient` 之后追加的色标会影响后续填充,且 `fillStyle`/`strokeStyle`
|
|
78
|
-
|
|
79
|
-
|
|
136
|
+
的读取返回同一个渐变句柄。径向渐变是通用的双圆锥:点上的颜色由插值圆
|
|
137
|
+
(`圆心 = c0 + t·(c1 − c0)`、`半径 = r0 + t·(r1 − r0)`)解出,因此内圆有偏移、两圆
|
|
138
|
+
半径不同时都与浏览器一致。内圆内部的点采样色标 0。
|
|
80
139
|
|
|
81
140
|
路径:`beginPath`、`closePath`、`moveTo`、`lineTo`、`rect`、`roundRect`、`arc`、
|
|
82
141
|
`ellipse`、`quadraticCurveTo`、`bezierCurveTo`,然后
|
|
83
|
-
`fill(fillRule?)` / `fill(path, fillRule?)`(多环;`"nonzero"` 或 `"evenodd"
|
|
84
|
-
`stroke(path?)
|
|
142
|
+
`fill(fillRule?)` / `fill(path, fillRule?)`(多环;`"nonzero"` 或 `"evenodd"`)、
|
|
143
|
+
`stroke(path?)`、`clip(path?, fillRule?)` 以及 `isPointInPath`/`isPointInStroke`
|
|
144
|
+
命中测试。贝塞尔与二次曲线段会被展平为线段;弧线与椭圆同样被展平。
|
|
85
145
|
`roundRect` 接受单个半径数字,或 CSS 半径列表(`[all]`、`[tl, br]`、
|
|
86
146
|
`[tl, tr, br]`、`[tl, tr, br, bl]`,元素为数字或 `{ x, y }` 对;圆角按圆弧绘制)。
|
|
87
147
|
|
|
88
148
|
`Path2D` 支持同一组路径命令(`moveTo`、`lineTo`、`closePath`、`rect`、
|
|
89
149
|
`roundRect`、`arc`、`ellipse`、`quadraticCurveTo`、`bezierCurveTo`、`addPath`),
|
|
90
|
-
并可传给 `fill`/`stroke
|
|
150
|
+
并可传给 `fill`/`stroke`/`clip` 以及 `isPointIn*` 命中测试,因此一次构建的路径可以在
|
|
151
|
+
不同变换下重复使用。
|
|
91
152
|
|
|
92
153
|
矩形:`clearRect`、`fillRect`、`strokeRect`。
|
|
93
154
|
|
|
@@ -103,29 +164,37 @@ Canvas 像素**不参与垃圾回收**:canvas 由窗口拥有、以 DOM 节点
|
|
|
103
164
|
|
|
104
165
|
图像:`drawImage(source, …)` 支持 3 参数(`dx, dy`)、5 参数(`dx, dy, dw, dh`)与
|
|
105
166
|
9 参数(本引擎为**目标优先**:`dx, dy, dw, dh, sx, sy, sw, sh`)。完整的仿射变换
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
167
|
+
(旋转/斜切/缩放)会被应用:目标矩形变为平行四边形,每个设备像素反查到源矩形。
|
|
168
|
+
`source` 可以是 `<img>` 元素(经图像模块解码)、另一个 `<canvas>`、
|
|
169
|
+
`OffscreenCanvas` 或 `ImageBitmap`。`imageSmoothingEnabled` 为 `true`(默认)时使用
|
|
170
|
+
双线性采样,为 `false` 时使用最近邻。
|
|
110
171
|
|
|
111
172
|
测试钩子:`getImageData(x, y, w, h)` 把区域复制为 `{ width, height, data }`,其中
|
|
112
173
|
`data` 是按行主序的 RGBA8 字节数组。越界像素为透明。
|
|
113
174
|
|
|
114
|
-
图案:`createPattern(source, repetition?)` 接受 `<img
|
|
115
|
-
|
|
116
|
-
|
|
175
|
+
图案:`createPattern(source, repetition?)` 接受 `<img>`、`<canvas>`、
|
|
176
|
+
`OffscreenCanvas` 或 `ImageBitmap`,在用户空间中平铺(`repeat`、`repeat-x`、
|
|
177
|
+
`repeat-y`、`no-repeat`),因此平铺会跟随当前变换。把结果赋给
|
|
178
|
+
`fillStyle`/`strokeStyle`;重新赋颜色会替换掉图案。
|
|
117
179
|
|
|
118
180
|
图像数据与导出:`createImageData(width, height)` 分配一个空的
|
|
119
181
|
`{ width, height, data }`,`putImageData(imageData, dx, dy[, dirtyX, dirtyY,
|
|
120
|
-
dirtyWidth, dirtyHeight])`
|
|
121
|
-
影响)。`canvas.toDataURL(type?)` 导出 PNG data URL
|
|
122
|
-
type?)` 从微任务中交付 Blob 形状的 `{ type, size }
|
|
123
|
-
`__xt_gui_blob_data` 上;本运行时没有 `URL.createObjectURL`)。
|
|
182
|
+
dirtyWidth, dirtyHeight])` 直接把字节写入位图(不受变换、混合、裁剪或
|
|
183
|
+
`globalAlpha` 影响)。`canvas.toDataURL(type?)` 导出 PNG data URL,
|
|
184
|
+
`canvas.toBlob(callback, type?)` 从微任务中交付 Blob 形状的 `{ type, size }`
|
|
185
|
+
(字节在 `__xt_gui_blob_data` 上;本运行时没有 `URL.createObjectURL`)。
|
|
186
|
+
`OffscreenCanvas.convertToBlob()` 解析为同一形状,`transferToImageBitmap()` 对位图
|
|
187
|
+
做快照。
|
|
188
|
+
|
|
189
|
+
CSS 背景:`background-image: url(...)` 接受文件路径或 `data:` URL,因此可以先把
|
|
190
|
+
canvas 导出再作为 CSS 背景使用
|
|
191
|
+
(`element.style.backgroundImage = "url(" + canvas.toDataURL() + ")"`)。图像层会拉伸
|
|
192
|
+
到元素的边框盒;`background-size`、`background-position` 与平铺尚未建模。
|
|
124
193
|
|
|
125
194
|
## 尚未实现
|
|
126
195
|
|
|
127
|
-
|
|
128
|
-
`
|
|
129
|
-
|
|
196
|
+
本后端的平滑质量只有一种过滤(双线性)。离屏画布只支持 2D 上下文(WebGL 上下文需要
|
|
197
|
+
DOM 元素来维护纹理表)。旋转的字形位图仍不会随变换旋转,`strokeText` 仍用描边样式
|
|
198
|
+
填充而非生成轮廓。
|
|
130
199
|
|
|
131
200
|
另见[媒体](./media.md):`drawImage` 与纹理上传所共享的图像/视频/音频解码栈。
|