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.
- package/README.md +10 -7
- package/README.zh-CN.md +10 -8
- package/dist/scripts/check-version.d.ts +27 -0
- package/dist/scripts/check-version.js +71 -0
- package/dist/scripts/check-version.js.map +1 -0
- package/dist/scripts/package-platform.d.ts +58 -0
- package/dist/scripts/package-platform.js +142 -0
- package/dist/scripts/package-platform.js.map +1 -0
- package/dist/scripts/verify-platform-package.d.ts +34 -0
- package/dist/scripts/verify-platform-package.js +161 -0
- package/dist/scripts/verify-platform-package.js.map +1 -0
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/cli/main.js +3 -1
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/driver/runtime-lib.d.ts +29 -4
- package/dist/src/driver/runtime-lib.js +68 -10
- package/dist/src/driver/runtime-lib.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/index.js +18 -5
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/extensions/media/index.js +4 -3
- package/dist/src/extensions/media/index.js.map +1 -1
- package/dist/src/parser/context.d.ts +41 -0
- package/dist/src/parser/context.js +89 -4
- package/dist/src/parser/context.js.map +1 -1
- package/dist/src/parser/literals.js +6 -12
- package/dist/src/parser/literals.js.map +1 -1
- package/dist/tests/driver/platform-package.test.d.ts +1 -0
- package/dist/tests/driver/platform-package.test.js +49 -0
- package/dist/tests/driver/platform-package.test.js.map +1 -0
- package/dist/tests/driver/runtime-lib.test.js +51 -4
- package/dist/tests/driver/runtime-lib.test.js.map +1 -1
- package/dist/tests/e2e/gui-audio.test.d.ts +14 -0
- package/dist/tests/e2e/gui-audio.test.js +287 -0
- package/dist/tests/e2e/gui-audio.test.js.map +1 -0
- package/dist/tests/e2e/gui-browser.test.js +65 -0
- package/dist/tests/e2e/gui-browser.test.js.map +1 -1
- package/dist/tests/e2e/gui-canvas-args.test.d.ts +6 -0
- package/dist/tests/e2e/gui-canvas-args.test.js +217 -0
- package/dist/tests/e2e/gui-canvas-args.test.js.map +1 -0
- package/dist/tests/e2e/gui-canvas.test.js.map +1 -1
- package/dist/tests/e2e/gui-dom.test.js +215 -0
- package/dist/tests/e2e/gui-dom.test.js.map +1 -1
- package/dist/tests/e2e/gui-events.test.js +112 -0
- package/dist/tests/e2e/gui-events.test.js.map +1 -1
- package/dist/tests/e2e/gui-helpers.d.ts +7 -0
- package/dist/tests/e2e/gui-helpers.js +1 -0
- package/dist/tests/e2e/gui-helpers.js.map +1 -1
- package/dist/tests/e2e/gui-media.test.js +78 -0
- package/dist/tests/e2e/gui-media.test.js.map +1 -1
- package/dist/tests/e2e/gui-style.test.js +55 -0
- package/dist/tests/e2e/gui-style.test.js.map +1 -1
- package/dist/tests/e2e/gui-webgl.test.js +258 -0
- package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
- package/dist/tests/e2e/gui-webgl2-3d.test.d.ts +4 -0
- package/dist/tests/e2e/gui-webgl2-3d.test.js +264 -0
- package/dist/tests/e2e/gui-webgl2-3d.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.d.ts +10 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.js +262 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.d.ts +6 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.js +395 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.d.ts +4 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.js +105 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.d.ts +8 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.js +112 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2.test.js +45 -1
- package/dist/tests/e2e/gui-webgl2.test.js.map +1 -1
- package/dist/tests/extensions/gui-index.test.js +1 -0
- package/dist/tests/extensions/gui-index.test.js.map +1 -1
- package/dist/tests/extensions/media.test.js +1 -0
- package/dist/tests/extensions/media.test.js.map +1 -1
- package/dist/tests/parser/expressions.test.js +30 -0
- package/dist/tests/parser/expressions.test.js.map +1 -1
- package/dist/tests/platform-tarball.test.d.ts +1 -0
- package/dist/tests/platform-tarball.test.js +111 -0
- package/dist/tests/platform-tarball.test.js.map +1 -0
- package/dist/tests/version.test.js +6 -0
- package/dist/tests/version.test.js.map +1 -1
- package/doc/ai/contributing.md +11 -0
- package/doc/ai/extensions.md +7 -4
- package/doc/ai/troubleshooting.md +7 -5
- package/doc/ai/zh-CN/contributing.md +9 -0
- package/doc/ai/zh-CN/extensions.md +4 -2
- package/doc/ai/zh-CN/troubleshooting.md +5 -3
- package/doc/gui/api.md +99 -22
- package/doc/gui/build.md +11 -4
- package/doc/gui/canvas.md +49 -18
- package/doc/gui/dom.md +57 -7
- package/doc/gui/media.md +4 -0
- package/doc/gui/webgl.md +105 -46
- package/doc/gui.md +7 -3
- package/doc/npm-package.md +55 -27
- package/doc/requirements.md +6 -2
- package/doc/zh-CN/gui/api.md +84 -14
- package/doc/zh-CN/gui/build.md +7 -2
- package/doc/zh-CN/gui/canvas.md +44 -19
- package/doc/zh-CN/gui/dom.md +48 -4
- package/doc/zh-CN/gui/media.md +3 -0
- package/doc/zh-CN/gui/webgl.md +22 -16
- package/doc/zh-CN/gui.md +3 -2
- package/doc/zh-CN/npm-package.md +44 -23
- package/doc/zh-CN/requirements.md +4 -1
- package/package.json +17 -1
- package/runtime/ext_gui/audio.cpp +365 -153
- package/runtime/ext_gui/audio_buffer.cpp +166 -0
- package/runtime/ext_gui/audio_buffer.h +37 -0
- package/runtime/ext_gui/audio_decode.cpp +219 -0
- package/runtime/ext_gui/audio_decode.h +39 -0
- package/runtime/ext_gui/audio_device.cpp +87 -0
- package/runtime/ext_gui/audio_effects.cpp +163 -0
- package/runtime/ext_gui/audio_effects.h +25 -0
- package/runtime/ext_gui/audio_internal.h +30 -0
- package/runtime/ext_gui/audio_mixer.cpp +494 -0
- package/runtime/ext_gui/audio_mixer.h +238 -0
- package/runtime/ext_gui/audio_param.cpp +104 -0
- package/runtime/ext_gui/audio_param_object.cpp +155 -0
- package/runtime/ext_gui/browser.cpp +45 -50
- package/runtime/ext_gui/browser_abort.cpp +249 -0
- package/runtime/ext_gui/browser_dialogs.cpp +76 -0
- package/runtime/ext_gui/browser_media.cpp +176 -0
- package/runtime/ext_gui/browser_navigation.cpp +130 -0
- package/runtime/ext_gui/canvas.cpp +88 -30
- package/runtime/ext_gui/canvas.h +74 -9
- package/runtime/ext_gui/canvas_image_data.cpp +143 -0
- package/runtime/ext_gui/canvas_internal.h +8 -5
- package/runtime/ext_gui/canvas_pattern.cpp +90 -0
- package/runtime/ext_gui/canvas_raster.cpp +31 -9
- package/runtime/ext_gui/canvas_style.cpp +4 -2
- package/runtime/ext_gui/css_selector.cpp +21 -3
- package/runtime/ext_gui/dom.h +4 -0
- package/runtime/ext_gui/dom_api.cpp +1 -0
- package/runtime/ext_gui/dom_api_canvas.cpp +120 -114
- package/runtime/ext_gui/dom_api_canvas_extra.cpp +57 -2
- package/runtime/ext_gui/dom_api_canvas_image.cpp +239 -0
- package/runtime/ext_gui/dom_api_canvas_paths.cpp +179 -0
- package/runtime/ext_gui/dom_api_computed_style.cpp +124 -0
- package/runtime/ext_gui/dom_api_dataset.cpp +154 -0
- package/runtime/ext_gui/dom_api_document.cpp +58 -5
- package/runtime/ext_gui/dom_api_events.cpp +93 -17
- package/runtime/ext_gui/dom_api_internal.h +71 -2
- package/runtime/ext_gui/dom_api_mutation.cpp +159 -0
- package/runtime/ext_gui/dom_api_node.cpp +40 -19
- package/runtime/ext_gui/dom_api_node_read.cpp +37 -1
- package/runtime/ext_gui/dom_api_path2d.cpp +358 -0
- package/runtime/ext_gui/dom_api_proto.cpp +35 -0
- package/runtime/ext_gui/dom_api_style.cpp +223 -13
- package/runtime/ext_gui/dom_api_webgl.cpp +7 -0
- package/runtime/ext_gui/dom_api_webgl2.cpp +151 -36
- package/runtime/ext_gui/dom_api_webgl2_draw.cpp +3 -3
- package/runtime/ext_gui/dom_api_webgl_buffer.cpp +6 -2
- package/runtime/ext_gui/dom_api_webgl_compressed.cpp +167 -0
- package/runtime/ext_gui/dom_api_webgl_framebuffer.cpp +160 -0
- package/runtime/ext_gui/dom_api_webgl_shader.cpp +4 -4
- package/runtime/ext_gui/dom_api_webgl_source.cpp +161 -0
- package/runtime/ext_gui/dom_api_webgl_state.cpp +169 -23
- package/runtime/ext_gui/dom_api_webgl_texture.cpp +276 -236
- package/runtime/ext_gui/dom_api_webgl_ubo.cpp +81 -0
- package/runtime/ext_gui/gui.cpp +103 -14
- package/runtime/ext_gui/gui_engine.h +56 -4
- package/runtime/ext_gui/gui_events.cpp +80 -9
- package/runtime/ext_gui/image_bitmap.cpp +28 -0
- package/runtime/ext_gui/image_bitmap.h +26 -0
- package/runtime/ext_gui/image_bitmap_browser.cpp +136 -0
- package/runtime/ext_gui/listener_options.h +43 -0
- package/runtime/ext_gui/media.cpp +32 -0
- package/runtime/ext_gui/media.h +13 -0
- package/runtime/ext_gui/media_api.h +23 -0
- package/runtime/ext_gui/paint.cpp +28 -0
- package/runtime/ext_gui/paint.h +4 -0
- package/runtime/ext_gui/webgl.h +152 -18
- package/runtime/ext_gui/webgl_blit.cpp +95 -0
- package/runtime/ext_gui/webgl_buffer.cpp +178 -263
- package/runtime/ext_gui/webgl_buffer_objects.cpp +270 -0
- package/runtime/ext_gui/webgl_clear.cpp +142 -0
- package/runtime/ext_gui/webgl_compressed.cpp +180 -0
- package/runtime/ext_gui/webgl_context.cpp +241 -156
- package/runtime/ext_gui/webgl_draw.cpp +68 -3
- package/runtime/ext_gui/webgl_draw_buffers.cpp +75 -0
- package/runtime/ext_gui/webgl_glsl.h +40 -0
- package/runtime/ext_gui/webgl_glsl_compile.cpp +6 -0
- package/runtime/ext_gui/webgl_glsl_rewrite.cpp +18 -0
- package/runtime/ext_gui/webgl_glsl_scan.cpp +20 -11
- package/runtime/ext_gui/webgl_glsl_scan_block.cpp +126 -0
- package/runtime/ext_gui/webgl_glsl_tokens.h +28 -0
- package/runtime/ext_gui/webgl_pipeline.cpp +71 -19
- package/runtime/ext_gui/webgl_program_blocks.cpp +58 -0
- package/runtime/ext_gui/webgl_program_internal.h +6 -0
- package/runtime/ext_gui/webgl_shader.cpp +13 -1
- package/runtime/ext_gui/webgl_state.cpp +228 -0
- package/runtime/ext_gui/webgl_texture.cpp +144 -17
- package/runtime/ext_gui/webgl_texture_3d.cpp +215 -0
- package/runtime/ext_gui/webgl_uniform_block.h +31 -0
- package/runtime/ext_gui/webgl_webgl2.cpp +58 -5
- package/runtime/ext_gui/window.cpp +14 -65
- package/runtime/ext_gui/window_events.cpp +125 -0
- package/runtime/ext_media/decode_audio.cpp +308 -0
- package/runtime/ext_media/media_bridge.c +2 -0
- package/runtime/rt.h +43 -0
- package/runtime/rt_internal.h +4 -0
- package/runtime/xt_array_buffer.c +30 -0
- package/runtime/xt_builtins.c +0 -21
- package/runtime/xt_containers/access.inc +94 -2
- package/runtime/xt_containers/objects.inc +29 -31
- package/runtime/xt_typed_array/elements.inc +13 -0
- package/scripts/build-media.ts +14 -6
- package/scripts/build-runtime.ts +21 -16
- package/scripts/check-version.ts +39 -9
- package/scripts/package-platform.ts +183 -0
- package/scripts/set-platform-version.ts +62 -0
- package/scripts/verify-platform-package.ts +182 -0
- package/src/cli/main.ts +5 -1
- package/src/driver/runtime-lib.ts +73 -9
- package/src/driver/version.ts +1 -1
- package/src/extensions/gui/index.ts +18 -5
- package/src/extensions/media/index.ts +4 -3
- package/src/parser/context.ts +99 -4
- package/src/parser/literals.ts +6 -12
package/doc/gui/api.md
CHANGED
|
@@ -21,19 +21,55 @@ Methods implemented on a window handle: `setTitle` / `setSize` / `loadHTML` /
|
|
|
21
21
|
`getHTML` / `setBackground` / `close` / `isOpen` / `on` / `off`, plus `driver()`,
|
|
22
22
|
which reports the SDL_GPU backend in use (`"vulkan"`, `"direct3d12"`, `"metal"`,
|
|
23
23
|
… — SDL picks the best one the device supports). The Web spellings are available
|
|
24
|
-
too: `addEventListener` / `removeEventListener`
|
|
25
|
-
`
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
`
|
|
24
|
+
too: `addEventListener` / `removeEventListener` take the standard options bag
|
|
25
|
+
(`capture`, `once`, `passive`, `signal`), and `closed` mirrors `isOpen()`, so a
|
|
26
|
+
document written against the browser API runs unchanged.
|
|
27
|
+
|
|
28
|
+
Element listeners accept the same options: `{ once }` drops the listener after
|
|
29
|
+
its first call, `{ passive: true }` makes `preventDefault()` a no-op, and
|
|
30
|
+
`{ signal }` (an `AbortSignal` from `new AbortController()`) removes it on
|
|
31
|
+
abort, including a signal that was already aborted when the listener was added.
|
|
32
|
+
`element.dispatchEvent(event)` dispatches the *same* event object (identity is
|
|
33
|
+
observable), fills in `target` / `currentTarget` / `eventPhase` and records the
|
|
34
|
+
path for `event.composedPath()`. `new Event(...)`, `new CustomEvent(...)`,
|
|
35
|
+
`new MouseEvent(...)`, `new KeyboardEvent(...)`, `new PointerEvent(...)`,
|
|
36
|
+
`new WheelEvent(...)`, `new FocusEvent(...)`, `new InputEvent(...)` and
|
|
37
|
+
`new SubmitEvent(...)` keep every field of their `init` object (`detail`,
|
|
38
|
+
`composed`, the mouse/keyboard members, …). Window listeners take the same bag
|
|
39
|
+
(capture is part of listener identity; window delivery has no capture phase).
|
|
40
|
+
|
|
41
|
+
Window navigation is real: `scrollTo(x, y)` / `scrollTo({ left, top })` /
|
|
42
|
+
`scrollBy(...)` move the page viewport (clamped to the content bounds; painting
|
|
43
|
+
and hit testing follow it), and `scrollX` / `scrollY` / `pageXOffset` /
|
|
44
|
+
`pageYOffset` report it. `focus()` raises the OS window and sets
|
|
45
|
+
`document.hasFocus()`; `blur()` clears it. Events emitted:
|
|
46
|
+
`ready` (after the first presented frame), `load`, `close`, `resize`, `scroll`,
|
|
47
|
+
`focus`, `blur` and the input events `mousemove`, `mousedown`, `mouseup`,
|
|
48
|
+
`click`, `wheel`, `keydown`, `keyup`.
|
|
49
|
+
|
|
50
|
+
`createWindow(options)` honours more than the title and size:
|
|
51
|
+
`{ title, width, height, highDensity, resizable, borderless, alwaysOnTop,
|
|
52
|
+
hidden, fullscreen, x, y, opacity, maximized, minimized }` map to the matching
|
|
53
|
+
SDL window flags/calls instead of being ignored.
|
|
54
|
+
|
|
55
|
+
`window.confirm(message)` / `window.prompt(message, defaultValue)` never drop
|
|
56
|
+
their arguments: with `XT_GUI_DIALOGS=1` and a live window they show a native
|
|
57
|
+
message box; otherwise the message goes to stderr and the documented default is
|
|
58
|
+
returned (`confirm` → `false`, `prompt` → `defaultValue` or `""`).
|
|
31
59
|
|
|
32
60
|
On Windows a GUI program is linked as a **Windows-subsystem** executable, so
|
|
33
61
|
launching it does not flash a console window. That also means stdout/stderr go
|
|
34
62
|
nowhere when it is started from Explorer; set `app.console: true` in
|
|
35
63
|
`xbintsc.config.json` to keep the console subsystem (useful while debugging).
|
|
36
64
|
|
|
65
|
+
A host without a usable GPU cannot run the GUI at all. When SDL cannot open a
|
|
66
|
+
video driver or create a Metal/Vulkan/D3D12 device, the engine prints the reason
|
|
67
|
+
and exits with status **77** — the autotools "skip" convention, so CI and scripts
|
|
68
|
+
can tell "this machine has no GUI" from "the program failed". A window that
|
|
69
|
+
cannot be opened after that (an SDL window or GPU-claim error) makes
|
|
70
|
+
`createWindow` throw a JavaScript `Error`; it no longer returns an `undefined`
|
|
71
|
+
handle that fails a line later as `Cannot read properties of undefined`.
|
|
72
|
+
|
|
37
73
|
## Browser globals
|
|
38
74
|
|
|
39
75
|
A document ported from the web rarely stops at `createWindow`. With `--ext gui`
|
|
@@ -54,12 +90,14 @@ requestAnimationFrame(frame);
|
|
|
54
90
|
| `document` | that window's document handle |
|
|
55
91
|
| `navigator` | `userAgent` (a desktop Chrome string ending in `xbintsc`), `platform`, `vendor`, `language(s)`, `hardwareConcurrency`, `maxTouchPoints`, `onLine`, `vibrate()` |
|
|
56
92
|
| `localStorage` | an in-memory `getItem` / `setItem` / `removeItem` / `clear` / `key` / `length` store |
|
|
57
|
-
| `AudioContext`, `webkitAudioContext` | a Web Audio graph
|
|
58
|
-
| `Event`, `CustomEvent`, `ProgressEvent`,
|
|
93
|
+
| `AudioContext`, `webkitAudioContext` | a Web Audio graph with a real mixer and decoder (see below) |
|
|
94
|
+
| `Event`, `CustomEvent`, `ProgressEvent`, `MouseEvent`, `KeyboardEvent`, `PointerEvent`, `WheelEvent`, `FocusEvent`, `InputEvent`, `SubmitEvent` | event constructors; the `init` argument is kept as given (`detail`, `composed`, mouse/keyboard fields, …) |
|
|
95
|
+
| `AbortController`, `AbortSignal` | a real controller/signal pair for `{ signal }` listener removal, with `abort()`, `aborted`, `reason`, `onabort` and `throwIfAborted()` |
|
|
59
96
|
| `HTMLCanvasElement`, `HTMLImageElement`, `WebGLRenderingContext`, `WebGL2RenderingContext`, `WebGLBuffer`/`WebGLTexture`/`WebGLShader`/`WebGLProgram`/`WebGLFramebuffer`/`WebGLRenderbuffer`/`WebGLUniformLocation`/`WebGLActiveInfo`/`WebGLVertexArrayObject`/`WebGLSampler`/`WebGLQuery`/`WebGLSync`/`WebGLTransformFeedback` | real constructor values. `canvas.getContext("webgl")` and `getContext("webgl2")` return GPU contexts (see [WebGL](./webgl.md)). |
|
|
60
|
-
| `ImageBitmap`, `
|
|
97
|
+
| `ImageBitmap`, `createImageBitmap(source)` | immutable bitmaps backed by decoded pixels (`<img>`, `<canvas>`, `<video>` frames and `ImageData` shapes); the promise resolves immediately and `texImage2D` accepts the result |
|
|
98
|
+
| `ImageData`, `OffscreenCanvas`, `XRWebGL*`, `Request`, `Headers`, `Response`, `ReadableStream`, `DOMParser` | `typeof` probes answer `"undefined"` (no XR/fetch/streaming backend), which lets a page take its "unsupported" branch instead of calling a no-op stub. Three.js pages use its real renderer on this backend (see [Known issues](./known-issues.md)). |
|
|
61
99
|
| `requestAnimationFrame` / `cancelAnimationFrame` | queued on the window's frame list; see [Runtime integration](./runtime.md) |
|
|
62
|
-
| `alert` / `confirm` / `prompt`, `matchMedia`, `getComputedStyle`, `escape` | host implementations (Annex B `escape` encodes per UTF-16 code unit, matching `decodeURIComponent(escape(bytes))`) |
|
|
100
|
+
| `alert` / `confirm` / `prompt`, `matchMedia`, `getComputedStyle`, `escape` | host implementations. `matchMedia` returns a **live** list (cached per query): `matches` is re-evaluated on resize and `change` listeners fire when it flips. `confirm`/`prompt` use a native dialog only with `XT_GUI_DIALOGS=1`; otherwise they report the message on stderr and return the documented default (Annex B `escape` encodes per UTF-16 code unit, matching `decodeURIComponent(escape(bytes))`) |
|
|
63
101
|
|
|
64
102
|
`getElementById` / `querySelector` are answered even before `loadHTML` runs: the
|
|
65
103
|
handle is *pending* and binds to the real node the first time it is used, so a
|
|
@@ -68,17 +106,56 @@ Once the page is parsed a miss answers `undefined`, like the browser.
|
|
|
68
106
|
|
|
69
107
|
### Web Audio
|
|
70
108
|
|
|
71
|
-
`new AudioContext()` builds a graph
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
`
|
|
80
|
-
|
|
81
|
-
|
|
109
|
+
`new AudioContext()` builds a real graph and mixes it: `runtime/ext_gui/audio_mixer.cpp`
|
|
110
|
+
renders 48 kHz stereo float in 128-frame quanta, multiple inputs are summed at
|
|
111
|
+
every node, and the first `start()`/`resume()` opens the default SDL playback
|
|
112
|
+
device (best effort — a headless host keeps rendering silently). The graph is
|
|
113
|
+
rendered on the SDL audio thread from native state; the page never has to pump
|
|
114
|
+
it, and no runtime value is touched off the main thread.
|
|
115
|
+
|
|
116
|
+
Implemented nodes: `createGain`, `createOscillator` (sine, square, sawtooth,
|
|
117
|
+
triangle), `createBufferSource` (buffer, loop, loopStart/loopEnd,
|
|
118
|
+
playbackRate/detune), `createBiquadFilter` (lowpass, highpass, bandpass,
|
|
119
|
+
lowshelf, highshelf, peaking, notch, allpass), `createDynamicsCompressor`,
|
|
120
|
+
`createStereoPanner`, `createConstantSource` and `createDelay(maxDelayTime)`.
|
|
121
|
+
`connect` returns the destination so chains chain (`src.connect(gain).connect(ctx.destination)`),
|
|
122
|
+
`disconnect()` / `disconnect(target)` update the native graph, and `start(when, offset, duration)` /
|
|
123
|
+
`stop(when)` are honoured on the graph's clock. Nodes outside this list
|
|
124
|
+
(`createAnalyser`, `createConvolver`, `createWaveShaper`, `createChannelSplitter`,
|
|
125
|
+
`createPeriodicWave`, …) are not provided, so their methods are simply absent.
|
|
126
|
+
|
|
127
|
+
Every `AudioParam` has a live `value` plus `setValueAtTime` /
|
|
128
|
+
`linearRampToValueAtTime` / `exponentialRampToValueAtTime` / `setTargetAtTime` /
|
|
129
|
+
`cancelScheduledValues` / `cancelAndHoldAtTime`. The automation timeline is
|
|
130
|
+
evaluated per sample, so a ramp keeps moving while the page is idle.
|
|
131
|
+
`currentTime` advances with the rendered quanta.
|
|
132
|
+
|
|
133
|
+
`createBuffer(channels, length, sampleRate)` returns an AudioBuffer whose
|
|
134
|
+
`getChannelData(i)` is a real `Float32Array`, so samples can be generated
|
|
135
|
+
procedurally. `decodeAudioData(bytes)` returns a promise for a decoded
|
|
136
|
+
AudioBuffer: WAV (PCM 8/16/24/32-bit, IEEE float 32/64, A-law and µ-law) is
|
|
137
|
+
decoded by the engine itself; MP3, AAC/M4A, Vorbis, Opus and FLAC need the
|
|
138
|
+
optional `media` extension (`--ext gui,media`), whose FFmpeg decoder is used as
|
|
139
|
+
the fallback. `bytes` may be an ArrayBuffer, a typed array, a Node `Buffer`, or
|
|
140
|
+
the raw byte string `fs.readFileSync` returns (this runtime has no dedicated
|
|
141
|
+
binary type, and that string's storage is exactly the file bytes). The result
|
|
142
|
+
is resampled to the context's 48 kHz, so `buffer.sampleRate` matches
|
|
143
|
+
`context.sampleRate`; the promise rejects with an `EncodingError` when nothing
|
|
144
|
+
can decode the bytes.
|
|
145
|
+
|
|
146
|
+
`resume()` / `suspend()` / `close()` control the device and the `state`
|
|
147
|
+
property; `sampleRate` is 48000, like the media decoder.
|
|
148
|
+
|
|
149
|
+
For tests and diagnostics an **offline** context renders synchronously:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
const ctx = new AudioContext({ __offline: true });
|
|
153
|
+
// … build the graph and start sources …
|
|
154
|
+
const samples = ctx.__render(480); // interleaved stereo Float32Array
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
This private hook never opens a device and is not part of the Web Audio API; it
|
|
158
|
+
is what `tests/e2e/gui-audio.test.ts` uses to pin the mixer math.
|
|
82
159
|
|
|
83
160
|
Input handlers receive a single payload object (lifecycle handlers receive none):
|
|
84
161
|
|
package/doc/gui/build.md
CHANGED
|
@@ -58,12 +58,19 @@
|
|
|
58
58
|
rather than through `make install`.
|
|
59
59
|
- CI builds `gui.a`/`gui.lib` and `media.a`/`media.lib` before assembling the
|
|
60
60
|
release archive so they ship inside `runtime/lib/<slug>/` (the release tarball
|
|
61
|
-
copies the whole `runtime/` tree). The
|
|
62
|
-
|
|
61
|
+
copies the whole `runtime/` tree). The same folders are packed into the
|
|
62
|
+
optional `@xbintsc/<slug>` npm package, so `--ext gui`/`--ext media` work from
|
|
63
|
+
an npm install on the hosts the release builds for. The archives are built and
|
|
64
|
+
the examples are run on Linux (under Xvfb, with the lavapipe software Vulkan
|
|
65
|
+
driver) and macOS;
|
|
63
66
|
the Windows x64 job builds both archives and links a `gui,media` program, so
|
|
64
67
|
the MSVC build is validated even though *running* the examples there stays
|
|
65
|
-
provisional (the hosted runner has no GPU/Vulkan driver).
|
|
66
|
-
|
|
68
|
+
provisional (the hosted runner has no GPU/Vulkan driver). The macOS run is
|
|
69
|
+
provisional too: the Intel runner's virtualised GPU lacks
|
|
70
|
+
`MTLGPUFamilyMac2`, so the engine exits **77** ("cannot run a GUI here") and
|
|
71
|
+
the job records a skip instead of compiling a suite that cannot present.
|
|
72
|
+
Windows arm64 still skips both archives: FFmpeg's MSVC arm64 build is not
|
|
73
|
+
validated yet.
|
|
67
74
|
- The Windows CI steps call `.github/actions/vs-toolchain` →
|
|
68
75
|
`scripts/setup-vs-toolchain.ps1`, which imports the same Visual Studio
|
|
69
76
|
`vcvarsall` environment and bundled LLVM a local build uses (no separate
|
package/doc/gui/canvas.md
CHANGED
|
@@ -36,12 +36,20 @@ a shared prototype. The handle is cached on the element, so every call returns
|
|
|
36
36
|
the **same** object and the drawing state (transform, styles, path) is per
|
|
37
37
|
canvas, not per call. `ctx.canvas` is the owning element handle.
|
|
38
38
|
|
|
39
|
+
`getContext("2d", attributes)` honours `alpha` on the first call: with
|
|
40
|
+
`{ alpha: false }` the bitmap starts (and clears) opaque black. The other
|
|
41
|
+
attributes are reported by `getContextAttributes()` at their specification
|
|
42
|
+
defaults (`antialias: true`, `depth: true`, `stencil: false`,
|
|
43
|
+
`premultipliedAlpha: true`, `preserveDrawingBuffer: false`, `colorSpace:
|
|
44
|
+
"srgb"`, …), so a feature test gets an honest answer.
|
|
45
|
+
|
|
39
46
|
## Drawing model
|
|
40
47
|
|
|
41
48
|
The rasteriser is deliberately small and self-written (no Skia/Cairo). It
|
|
42
49
|
anti-aliases with **4 vertical sub-scanlines** per pixel row and an **analytic
|
|
43
50
|
horizontal coverage** pass, and fills paths with the **non-zero winding rule**
|
|
44
|
-
(so `arc`-shaped holes work when wound the other way)
|
|
51
|
+
(so `arc`-shaped holes work when wound the other way) unless
|
|
52
|
+
`fill("evenodd")` selects the even-odd rule. An axis-aligned rectangle
|
|
45
53
|
(`b == 0 && c == 0` in the transform) takes a direct coverage fast path.
|
|
46
54
|
Blending is non-premultiplied source-over, scaled by `globalAlpha`.
|
|
47
55
|
|
|
@@ -63,10 +71,11 @@ matching GPU texture.
|
|
|
63
71
|
|
|
64
72
|
State and transforms: `save` / `restore` (which restore the transform, line
|
|
65
73
|
width, alpha, font/text settings **and** the fill/stroke styles),
|
|
66
|
-
`translate`, `rotate`, `scale`, `transform(a,b,c,d,e,f)
|
|
67
|
-
`
|
|
68
|
-
|
|
69
|
-
|
|
74
|
+
`translate`, `rotate`, `scale`, `transform(a,b,c,d,e,f)` / `transform(matrix)`,
|
|
75
|
+
`setTransform(a,b,c,d,e,f)` / `setTransform(matrix)`, `resetTransform`,
|
|
76
|
+
`getTransform` (returns an object with `a`…`f` and `is2D`). A matrix is a
|
|
77
|
+
`{ a, b, c, d, e, f }` object or a six-element array. Transforms compose in
|
|
78
|
+
canvas order: `translate(x, y); rotate(a)` rotates around the translated origin.
|
|
70
79
|
|
|
71
80
|
Styles and settings: `fillStyle` / `strokeStyle` (CSS colors understood by the
|
|
72
81
|
cascade's color parser, or a `CanvasGradient`), `lineWidth`, `globalAlpha`,
|
|
@@ -86,10 +95,18 @@ distance from the **outer** circle's centre (`x1, y1`), so `x0`/`y0` are
|
|
|
86
95
|
currently ignored: use concentric circles (`x0 == x1`, `y0 == y1`).
|
|
87
96
|
|
|
88
97
|
Paths: `beginPath`, `closePath`, `moveTo`, `lineTo`, `rect`, `roundRect`,
|
|
89
|
-
`arc`, `ellipse`, `quadraticCurveTo`, `bezierCurveTo`, then
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
98
|
+
`arc`, `ellipse`, `quadraticCurveTo`, `bezierCurveTo`, then
|
|
99
|
+
`fill(fillRule?)` / `fill(path, fillRule?)` (multi-loop; `"nonzero"` or
|
|
100
|
+
`"evenodd"`) or `stroke(path?)`. Bezier and quadratic segments are flattened
|
|
101
|
+
into line segments; arcs and ellipses are flattened too. `roundRect` accepts a
|
|
102
|
+
single radius number or the CSS radius list (`[all]`, `[tl, br]`,
|
|
103
|
+
`[tl, tr, br]`, `[tl, tr, br, bl]`, numbers or `{ x, y }` pairs; corners are
|
|
104
|
+
drawn circular).
|
|
105
|
+
|
|
106
|
+
`Path2D` is implemented with the same path commands (`moveTo`, `lineTo`,
|
|
107
|
+
`closePath`, `rect`, `roundRect`, `arc`, `ellipse`, `quadraticCurveTo`,
|
|
108
|
+
`bezierCurveTo`, `addPath`) and can be passed to `fill`/`stroke`, so a path can
|
|
109
|
+
be built once and replayed under different transforms.
|
|
93
110
|
|
|
94
111
|
Rectangles: `clearRect`, `fillRect`, `strokeRect`.
|
|
95
112
|
|
|
@@ -97,9 +114,12 @@ Dashes: `setLineDash(segments)` / `getLineDash()` / `lineDashOffset` implement
|
|
|
97
114
|
the standard pattern rules (an odd-length list is duplicated, negative or
|
|
98
115
|
non-finite entries are ignored, an all-zero list means a solid line).
|
|
99
116
|
|
|
100
|
-
Text: `measureText(text)` (returns `{ width }`), `fillText(text, x, y
|
|
101
|
-
`strokeText(text, x, y)`. Text is shaped with
|
|
102
|
-
FreeType (the same stack as HTML text) at the
|
|
117
|
+
Text: `measureText(text)` (returns `{ width }`), `fillText(text, x, y,
|
|
118
|
+
maxWidth?)` and `strokeText(text, x, y, maxWidth?)`. Text is shaped with
|
|
119
|
+
HarfBuzz and rasterised with FreeType (the same stack as HTML text) at the
|
|
120
|
+
transform's approximate scale. A positive `maxWidth` condenses advances and
|
|
121
|
+
glyph bitmaps horizontally so the run is at most that wide (as the canvas API
|
|
122
|
+
specifies) instead of being ignored.
|
|
103
123
|
**Only translation and uniform scale are honoured** — a rotated or skewed
|
|
104
124
|
transform still places each glyph at its transformed baseline point but does not
|
|
105
125
|
rotate the glyph bitmaps. `strokeText` currently fills with the stroke style
|
|
@@ -119,14 +139,25 @@ Test hook: `getImageData(x, y, w, h)` copies a region to
|
|
|
119
139
|
`{ width, height, data }` where `data` is a row-major RGBA8 byte array.
|
|
120
140
|
Out-of-bounds pixels are transparent.
|
|
121
141
|
|
|
142
|
+
Patterns: `createPattern(source, repetition?)` accepts an `<img>` or another
|
|
143
|
+
`<canvas>` and tiles it in user space (`repeat`, `repeat-x`, `repeat-y`,
|
|
144
|
+
`no-repeat`), so the tiles follow the current transform. Assign the result to
|
|
145
|
+
`fillStyle`/`strokeStyle`; assigning a colour replaces it.
|
|
146
|
+
|
|
147
|
+
Image data and export: `createImageData(width, height)` allocates a blank
|
|
148
|
+
`{ width, height, data }`, and `putImageData(imageData, dx, dy[, dirtyX, dirtyY,
|
|
149
|
+
dirtyWidth, dirtyHeight])` copies bytes straight into the bitmap (no transform,
|
|
150
|
+
blending or `globalAlpha`). `canvas.toDataURL(type?)` exports a PNG data URL and
|
|
151
|
+
`canvas.toBlob(callback, type?)` delivers a Blob-shaped `{ type, size }` from a
|
|
152
|
+
microtask (the bytes are on `__xt_gui_blob_data`; there is no
|
|
153
|
+
`URL.createObjectURL`).
|
|
154
|
+
|
|
122
155
|
## Not implemented
|
|
123
156
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
`
|
|
127
|
-
|
|
128
|
-
(`OffscreenCanvas`, `transferControlToOffscreen`). `roundRect` takes a single
|
|
129
|
-
radius, and radial gradients ignore the inner circle's offset.
|
|
157
|
+
Shadows, `clip()`, `globalCompositeOperation`, `isPointInPath`, `drawImage` with
|
|
158
|
+
an image smoothing toggle, `<canvas>` as a CSS `background-image`, and offscreen
|
|
159
|
+
canvases (`OffscreenCanvas`, `transferControlToOffscreen`).
|
|
160
|
+
Radial gradients ignore the inner circle's offset.
|
|
130
161
|
|
|
131
162
|
See also [Media](./media.md) for the image/video/audio decode stack that
|
|
132
163
|
`drawImage` and the texture upload share.
|
package/doc/gui/dom.md
CHANGED
|
@@ -14,9 +14,12 @@ close rules (`li`, `dt`/`dd`, `option`, `p`, headings, table cells/rows).
|
|
|
14
14
|
**CSS parser** (`runtime/ext_gui/css.{h,cpp}`): comments are stripped; `@media`
|
|
15
15
|
blocks are parsed recursively (nested queries combine with `and`, and a comma
|
|
16
16
|
list distributes over the inner query) while other at-rules are skipped. Rules
|
|
17
|
-
support multiple selectors and declarations
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
support multiple selectors and declarations; a selector list may be formatted
|
|
18
|
+
across any number of lines (line breaks are ordinary whitespace, so a wrapped
|
|
19
|
+
combinator stays a combinator), and a quoted attribute value may contain
|
|
20
|
+
commas, brackets or parentheses without splitting the list. Selectors: type,
|
|
21
|
+
`.class`, `#id`, attribute (`=`, `~=`, `|=`, `^=`, `$=`, `*=`), the four
|
|
22
|
+
combinators (descendant, child, adjacent and general sibling), the pseudo-classes
|
|
20
23
|
`:first-child`, `:last-child`, `:only-child`, `:empty`, `:root`, `:not(...)`,
|
|
21
24
|
`:nth-child(an+b)`, `:disabled`, `:checked`, plus the stateful `:hover` and
|
|
22
25
|
`:focus` (see [Input](./input.md)), and the pseudo-elements `::before`/`::after`
|
|
@@ -69,18 +72,65 @@ created.textContent = "added";
|
|
|
69
72
|
box.appendChild(created);
|
|
70
73
|
box.removeChild(created);
|
|
71
74
|
|
|
75
|
+
// Parent/child helpers accept nodes and strings in one call.
|
|
76
|
+
box.append("text", doc.createElement("span"));
|
|
77
|
+
box.prepend(doc.createElement("b"));
|
|
78
|
+
box.insertAdjacentHTML("beforeend", "<i>tail</i>"); // beforebegin/afterbegin/beforeend/afterend
|
|
79
|
+
box.replaceChildren("fresh", doc.createElement("em"));
|
|
80
|
+
|
|
81
|
+
// Namespaces are recorded; foreign (SVG) element names keep their case.
|
|
82
|
+
const gradient = doc.createElementNS("http://www.w3.org/2000/svg", "svg:linearGradient");
|
|
83
|
+
console.log(gradient.namespaceURI, gradient.localName, gradient.prefix);
|
|
84
|
+
|
|
72
85
|
inner.addEventListener("click", (e) => console.log(e.target.id, e.currentTarget.id));
|
|
73
86
|
inner.click(); // synthesise a click at the element
|
|
74
87
|
console.log(box.offsetWidth, box.offsetHeight); // rounded border box
|
|
75
88
|
console.log(box.contains(inner)); // descendant test
|
|
76
89
|
```
|
|
77
90
|
|
|
91
|
+
### Style properties, dataset, computed style and geometry
|
|
92
|
+
|
|
93
|
+
The inline `style` object is a CSSOM declaration block. Named access reads and
|
|
94
|
+
writes the same block as `setProperty` (`el.style.display = "none"`,
|
|
95
|
+
`el.style.backgroundColor = "#f00"`, camelCase mapped to the CSS spelling, and
|
|
96
|
+
`el.style["--accent"]` for custom properties); `setProperty(name, value,
|
|
97
|
+
priority)` stores `!important` when `priority` is `"important"` (any other
|
|
98
|
+
priority makes the call a no-op, and an empty value removes the property).
|
|
99
|
+
`getPropertyValue` / `getPropertyPriority` read it back, `removeProperty`
|
|
100
|
+
returns the previous value, `cssText` gets or replaces the whole block,
|
|
101
|
+
`style.length` / `style.item(i)` enumerate it, and an unset property answers
|
|
102
|
+
`""`.
|
|
103
|
+
|
|
104
|
+
`el.dataset` is a live `DOMStringMap` over the element's `data-*` attributes:
|
|
105
|
+
`dataset.fooBar` reads `data-foo-bar`, assignment writes the attribute, `delete`
|
|
106
|
+
removes it, and `Object.keys(dataset)` / `for...in` list only the data keys.
|
|
107
|
+
|
|
108
|
+
`getComputedStyle(el, pseudoElt)` answers from the **cascade** — the UA sheet,
|
|
109
|
+
author rules, inheritance and the inline declarations — rather than echoing the
|
|
110
|
+
inline block, so a stylesheet rule is visible. It exposes `getPropertyValue`,
|
|
111
|
+
`getPropertyPriority` and named properties (`cs.width`, `cs.backgroundColor`,
|
|
112
|
+
`cs["--accent"]`). Pass `"::before"`/`"::after"` to read generated content; any
|
|
113
|
+
other pseudo-element answers empty values. Every read flushes a pending restyle,
|
|
114
|
+
so a handle created early stays live after later mutations.
|
|
115
|
+
|
|
116
|
+
`getBoundingClientRect()` carries the legacy `top` / `right` / `bottom` / `left`
|
|
117
|
+
members next to `x` / `y` / `width` / `height`, and `document.title = …` writes
|
|
118
|
+
the document's `<title>` (creating the element, and a `<head>`, when the page
|
|
119
|
+
has none).
|
|
120
|
+
|
|
78
121
|
Mutations mark the document dirty; the engine restyles + relayouts lazily (before
|
|
79
122
|
the next read or frame). `inner = …`/`innerHTML = …` do **not** run scripts.
|
|
80
|
-
Element events support capture and bubble phases, `stopPropagation`, `once`,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
123
|
+
Element events support capture and bubble phases, `stopPropagation`, `once`,
|
|
124
|
+
`passive` and `signal` (an `AbortController`'s signal removes the listener), and
|
|
125
|
+
bubble up to `document`/`window`. `dispatchEvent` dispatches the same event
|
|
126
|
+
object and fills in `target`/`currentTarget`/`eventPhase`/`composedPath()`. The
|
|
127
|
+
legacy `win.on(type, fn)` payload keeps its **string** `e.target`
|
|
128
|
+
(`div#id.class`); the element `Event.target` is a handle whose descriptor
|
|
129
|
+
matches the same string.
|
|
130
|
+
|
|
131
|
+
`element.focus()` scrolls the element into view (pass `{ preventScroll: true }`
|
|
132
|
+
to keep the page offset), and `document.hasFocus()` reports the window state
|
|
133
|
+
toggled by `window.focus()` / `window.blur()`.
|
|
84
134
|
|
|
85
135
|
### Node lifetime
|
|
86
136
|
|
package/doc/gui/media.md
CHANGED
|
@@ -112,6 +112,10 @@ platform media framework in the path either way.
|
|
|
112
112
|
- **Threading.** The engine is single-threaded and not thread-aware, so all
|
|
113
113
|
decoding happens on the main thread inside `xt_gui_media_tick` — no decode
|
|
114
114
|
thread, no cross-thread GC exposure.
|
|
115
|
+
- **`decodeAudioData`.** The same FFmpeg build also decodes an in-memory audio
|
|
116
|
+
file for `AudioContext.decodeAudioData` (`runtime/ext_media/decode_audio.cpp`,
|
|
117
|
+
through the media table); WAV is handled by the GUI engine itself
|
|
118
|
+
(`audio_decode.cpp`), so only compressed formats need this extension.
|
|
115
119
|
- **DOM API.** Element handles expose the media surface directly (see
|
|
116
120
|
`runtime/ext_gui/dom_api_media.cpp` and the prototype in
|
|
117
121
|
`dom_api_proto.cpp`): `play()` / `pause()` / `load()`, and the
|
package/doc/gui/webgl.md
CHANGED
|
@@ -96,8 +96,7 @@ reflected offsets; `uniform1i` on a sampler stores the texture unit. The block
|
|
|
96
96
|
is pushed with the dynamic uniform-buffer API on every draw.
|
|
97
97
|
|
|
98
98
|
Limits of the translator: struct-typed uniforms, user uniform blocks, uniform
|
|
99
|
-
arrays whose size is not resolved by the preprocessor, sampler arrays
|
|
100
|
-
maps are rejected (the link/shader info log says so). Shader code inside
|
|
99
|
+
arrays whose size is not resolved by the preprocessor, sampler arrays are rejected (the link/shader info log says so). Shader code inside
|
|
101
100
|
`#ifdef` is fine; non-sampler uniform declarations are moved to the enclosing
|
|
102
101
|
top-level position so the block stays unconditional.
|
|
103
102
|
|
|
@@ -115,8 +114,11 @@ available from GLSL ES 3.00, which is what the instanced example uses.
|
|
|
115
114
|
|
|
116
115
|
- **Context**: `getContext("webgl")`, `canvas`, `drawingBufferWidth/Height`,
|
|
117
116
|
`getParameter`, `getError`, `getSupportedExtensions`, `getExtension`,
|
|
118
|
-
`getContextAttributes`, `isContextLost`, `getShaderPrecisionFormat`, and
|
|
119
|
-
stencil
|
|
117
|
+
`getContextAttributes`, `isContextLost`, `getShaderPrecisionFormat`, and an
|
|
118
|
+
8-bit stencil buffer (`getContextAttributes().stencil` is `true`,
|
|
119
|
+
`STENCIL_BITS` is 8). `WEBGL_lose_context`'s `loseContext`/`restoreContext`
|
|
120
|
+
really drop and rebuild the context (every object becomes invalid) and
|
|
121
|
+
dispatch `webglcontextlost`/`webglcontextrestored`.
|
|
120
122
|
- **Shaders and programs**: `createShader`, `shaderSource`, `compileShader`,
|
|
121
123
|
`getShaderParameter`, `getShaderInfoLog`, `getShaderSource`, `deleteShader`,
|
|
122
124
|
`createProgram`, `attachShader`, `detachShader`, `linkProgram`,
|
|
@@ -131,7 +133,9 @@ available from GLSL ES 3.00, which is what the instanced example uses.
|
|
|
131
133
|
`bufferSubData`, `deleteBuffer`, `getBufferParameter`,
|
|
132
134
|
`enableVertexAttribArray`, `disableVertexAttribArray`,
|
|
133
135
|
`vertexAttribPointer`, `vertexAttrib1f`…`4f` / `1fv`…`4fv`,
|
|
134
|
-
`getVertexAttrib`.
|
|
136
|
+
`getVertexAttrib`. The `usage` hint from `bufferData`
|
|
137
|
+
(`STREAM_DRAW`/`STATIC_DRAW`/`DYNAMIC_DRAW`) is recorded and reported by
|
|
138
|
+
`getBufferParameter(…, BUFFER_USAGE)`.
|
|
135
139
|
- **Drawing**: `drawArrays`, `drawElements` (UNSIGNED_BYTE, UNSIGNED_SHORT,
|
|
136
140
|
UNSIGNED_INT; `TRIANGLE_FAN` and `LINE_LOOP` are expanded to lists).
|
|
137
141
|
`POINTS`, `LINES`, `LINE_STRIP`, `TRIANGLES` and `TRIANGLE_STRIP` map to the
|
|
@@ -140,22 +144,50 @@ available from GLSL ES 3.00, which is what the instanced example uses.
|
|
|
140
144
|
`texParameteri`/`texParameterf`, `getTexParameter`, `texImage2D` (raw pixels,
|
|
141
145
|
`<img>`, `<canvas>` and `ImageData` sources), `texSubImage2D`,
|
|
142
146
|
`generateMipmap`, `deleteTexture`. `UNPACK_FLIP_Y_WEBGL` and
|
|
143
|
-
`UNPACK_PREMULTIPLY_ALPHA_WEBGL` are honoured.
|
|
144
|
-
RGBA8; `RGBA`, `RGB`, `LUMINANCE`, `ALPHA` and
|
|
145
|
-
`UNSIGNED_BYTE` are accepted.
|
|
146
|
-
|
|
147
|
-
`
|
|
148
|
-
`
|
|
149
|
-
`
|
|
150
|
-
|
|
147
|
+
`UNPACK_PREMULTIPLY_ALPHA_WEBGL` are honoured. By default source data is
|
|
148
|
+
converted to RGBA8; `RGBA`, `RGB`, `LUMINANCE`, `ALPHA` and
|
|
149
|
+
`LUMINANCE_ALPHA` with `UNSIGNED_BYTE` are accepted. Float textures
|
|
150
|
+
(`RGBA32F`, `RGBA16F`, `RGB32F`, `RGB16F`, `RG32F`/`RG16F`, `R32F`/`R16F`
|
|
151
|
+
with `FLOAT` or `HALF_FLOAT` data, plus WebGL 1's
|
|
152
|
+
`OES_texture_float`/`OES_texture_half_float`) map to SDL's RGBA32F/RGBA16F,
|
|
153
|
+
and depth textures (`DEPTH_COMPONENT16/24/32F`, `DEPTH_STENCIL`,
|
|
154
|
+
`DEPTH24_STENCIL8`) map to `D32_FLOAT`/`D32_FLOAT_S8_UINT` and can be sampled
|
|
155
|
+
or used as depth attachments. Compressed textures upload through
|
|
156
|
+
`compressedTexImage2D`/`compressedTexSubImage2D` with
|
|
157
|
+
`WEBGL_compressed_texture_s3tc` (BC1/BC2/BC3),
|
|
158
|
+
`WEBGL_compressed_texture_s3tc_srgb` and `WEBGL_compressed_texture_astc`;
|
|
159
|
+
each extension is published only when the device can sample its formats.
|
|
160
|
+
`texImage2D` also accepts the latest decoded `<video>` frame and an
|
|
161
|
+
`ImageBitmap` produced by `createImageBitmap`. Cube maps
|
|
162
|
+
(`bindTexture(TEXTURE_CUBE_MAP)` plus six `texImage2D` faces) and 3D/array
|
|
163
|
+
textures (`texImage3D`/`texStorage3D`) are sampled through `samplerCube`,
|
|
164
|
+
`sampler3D` and `sampler2DArray`; `texSubImage3D` updates a sub-volume and
|
|
165
|
+
`framebufferTextureLayer` renders one array/cube layer as a colour
|
|
166
|
+
attachment.
|
|
167
|
+
- **Framebuffers**: `createFramebuffer`, `bindFramebuffer`
|
|
168
|
+
(`FRAMEBUFFER`/`READ_FRAMEBUFFER`/`DRAW_FRAMEBUFFER`),
|
|
169
|
+
`framebufferTexture2D` (`COLOR_ATTACHMENT0`, `DEPTH_ATTACHMENT`,
|
|
170
|
+
`DEPTH_STENCIL_ATTACHMENT`), `checkFramebufferStatus`, `createRenderbuffer`,
|
|
171
|
+
`bindRenderbuffer`, `renderbufferStorage` (depth or depth-stencil),
|
|
172
|
+
`framebufferRenderbuffer`, `delete*`, and `blitFramebuffer` (colour
|
|
173
|
+
attachment; depth/stencil bits are accepted but not copied). This is enough
|
|
174
|
+
for render-to-texture, shadow maps and post-processing passes.
|
|
151
175
|
- **State**: `enable`/`disable`/`isEnabled` for `BLEND`, `DEPTH_TEST`,
|
|
152
|
-
`CULL_FACE`, `SCISSOR_TEST`
|
|
153
|
-
`clearColor`/`clearDepth`/`clearStencil`, `clear
|
|
154
|
-
|
|
155
|
-
`
|
|
156
|
-
`
|
|
176
|
+
`CULL_FACE`, `SCISSOR_TEST` and `STENCIL_TEST`,
|
|
177
|
+
`clearColor`/`clearDepth`/`clearStencil`, `clear` (with `SCISSOR_TEST` it
|
|
178
|
+
clears only the scissor rectangle, for colour, depth and stencil), `blendColor`,
|
|
179
|
+
`blendFunc`/`blendFuncSeparate`,
|
|
180
|
+
`blendEquation`/`blendEquationSeparate`, `depthFunc`,
|
|
181
|
+
`depthMask`, `colorMask`, `stencilFunc`/`stencilFuncSeparate`,
|
|
182
|
+
`stencilOp`/`stencilOpSeparate`, `stencilMask`/`stencilMaskSeparate`,
|
|
183
|
+
`cullFace`, `frontFace`, `viewport`, `scissor`,
|
|
184
|
+
`pixelStorei`, `finish`. The stencil test takes part in rasterisation and
|
|
185
|
+
`clear(STENCIL_BUFFER_BIT)` clears the stencil attachment; SDL_GPU exposes
|
|
186
|
+
one reference and one compare/write mask for both faces, so a front/back
|
|
187
|
+
pair that differs uses the front value.
|
|
157
188
|
- **Readback**: `readPixels` (RGBA/UNSIGNED_BYTE) from the default framebuffer
|
|
158
|
-
or a bound framebuffer
|
|
189
|
+
or a bound RGBA8 framebuffer; a float/depth attachment is sampled into an
|
|
190
|
+
RGBA8 target first. It blocks until the GPU finishes; use it for tests and
|
|
159
191
|
screenshots, not per frame.
|
|
160
192
|
|
|
161
193
|
WebGL 2 adds:
|
|
@@ -174,38 +206,46 @@ WebGL 2 adds:
|
|
|
174
206
|
texture's own filter/wrap state for that unit.
|
|
175
207
|
- **Immutable textures and buffer copies**: `texStorage2D` (the backend always
|
|
176
208
|
allocates the full mip chain), `copyBufferSubData`, `getBufferSubData`.
|
|
209
|
+
- **Drawing buffer**: `drawingBufferStorage(width, height)` fixes the drawing
|
|
210
|
+
buffer size; `canvas.width`/`canvas.height` no longer rebuild it afterwards.
|
|
177
211
|
- **Clears**: `clearBufferfv`, `clearBufferiv`, `clearBufferuiv`,
|
|
178
212
|
`clearBufferfi`.
|
|
213
|
+
- **Multiple render targets**: `drawBuffers` addresses up to four colour
|
|
214
|
+
attachments and `readBuffer` selects the attachment `readPixels` reads;
|
|
215
|
+
`framebufferTexture2D` accepts `COLOR_ATTACHMENT0`…`3`, and a fragment
|
|
216
|
+
shader's multiple `out` variables are written in order.
|
|
179
217
|
- **Queries and fences**: the objects and entry points exist
|
|
180
218
|
(`createQuery`/`beginQuery`/`endQuery`/`getQueryParameter`,
|
|
181
219
|
`fenceSync`/`clientWaitSync`/`waitSync`/`getSyncParameter`) and degrade
|
|
182
220
|
gracefully: a fence reports `ALREADY_SIGNALED` and query results are 0.
|
|
183
|
-
- **Uniform blocks**: `getUniformBlockIndex
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
221
|
+
- **Uniform blocks**: `getUniformBlockIndex`,
|
|
222
|
+
`getActiveUniformBlockParameter`, `getActiveUniformBlockName`,
|
|
223
|
+
`uniformBlockBinding`, `bindBufferBase`/`bindBufferRange` and
|
|
224
|
+
`getIndexedParameter`. Members are merged into the engine's single std140
|
|
225
|
+
block and copied from the bound buffer at draw time; scalars, vectors,
|
|
226
|
+
matrices and fixed-size arrays work, nested structs, sampler members and
|
|
227
|
+
blocks without an instance name are rejected.
|
|
228
|
+
- **Transform feedback**: the entry points are present as no-ops so
|
|
229
|
+
feature-probing libraries do not throw.
|
|
187
230
|
|
|
188
231
|
Textures are created with a full mip chain; sampling clamps to level 0 until
|
|
189
232
|
`generateMipmap` runs. Matrix vertex attributes consume the right number of
|
|
190
233
|
consecutive locations and read one column per location. A disabled attribute
|
|
191
234
|
is supplied from the last `vertexAttrib*f` value through a small constant
|
|
192
|
-
buffer.
|
|
235
|
+
buffer. A vertex shader can sample textures just like a fragment shader:
|
|
236
|
+
`MAX_VERTEX_TEXTURE_IMAGE_UNITS` reports 16, vertex samplers live in set 0 and
|
|
237
|
+
are bound with `SDL_BindGPUVertexSamplers` on every draw. Depth values use the
|
|
238
|
+
backend's `[0, 1]` clip space rather than GL's `[-1, 1] → [0, 1]` remap:
|
|
239
|
+
`gl_Position.z = 0.5` stores depth `0.5` (real WebGL stores `0.75`).
|
|
193
240
|
|
|
194
241
|
## Not implemented
|
|
195
242
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
static/dynamic (the hint is ignored), `texImage2D` for
|
|
203
|
-
`HTMLVideoElement`/`ImageBitmap` and scissored `clear` (a clear clears the whole
|
|
204
|
-
attachment). Shaders that use built-in blocks, user uniform blocks or
|
|
205
|
-
`gl_FragDepth` beyond the supported subset are rejected with a compile/link log.
|
|
206
|
-
|
|
207
|
-
The engine advertises zero vertex texture units, so a well-behaved WebGL 1
|
|
208
|
-
program will not try to sample a texture in a vertex shader.
|
|
243
|
+
Transform feedback (the entry points are no-ops) and MSAA/sample buffers
|
|
244
|
+
(SDL_GPU's Metal multisample resolve returns undefined pixels with scissor
|
|
245
|
+
tests and instanced draws, so contexts stay single-sampled). `blitFramebuffer`
|
|
246
|
+
copies the colour attachment only; depth/stencil bits are accepted but not
|
|
247
|
+
copied. Shaders that use built-in blocks or `gl_FragDepth` are rejected with a
|
|
248
|
+
compile/link log.
|
|
209
249
|
|
|
210
250
|
## Platform
|
|
211
251
|
|
|
@@ -225,10 +265,27 @@ uses the 2D canvas or the DOM pays no run-time cost beyond the archive size.
|
|
|
225
265
|
## Testing
|
|
226
266
|
|
|
227
267
|
- `tests/e2e/gui-webgl.test.ts` compiles GLSL, draws a textured and a
|
|
228
|
-
depth-tested quad, checks face culling and reads the pixels back
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
268
|
+
depth-tested quad, checks face culling and reads the pixels back; it also
|
|
269
|
+
covers blend constants, dynamic buffer usage, vertex-shader texture sampling,
|
|
270
|
+
`gl_FragDepth` diagnostics, context loss/restore and cube maps.
|
|
271
|
+
- `tests/e2e/gui-webgl2.test.ts` covers the WebGL 2 shader/state additions:
|
|
272
|
+
`#version 300 es` shaders, a VAO round trip, instanced draws with a divisor,
|
|
273
|
+
`texStorage2D`, `clearBufferfv`, buffer copies, sampler objects, struct-uniform
|
|
274
|
+
fields and `drawingBufferStorage`.
|
|
275
|
+
- `tests/e2e/gui-webgl2-formats.test.ts` covers stencil, float/depth textures,
|
|
276
|
+
compressed textures and `ImageBitmap`.
|
|
277
|
+
- `tests/e2e/gui-webgl2-blit.test.ts` covers scissored colour/depth clears,
|
|
278
|
+
framebuffer blits, and the pixels around a partial blit that must survive
|
|
279
|
+
(a destination load-op that discards them fails there on Metal).
|
|
280
|
+
- `tests/e2e/gui-media.test.ts` also uploads the latest decoded `<video>` frame
|
|
281
|
+
as a texture and reads it back.
|
|
282
|
+
- `tests/e2e/gui-webgl2-3d.test.ts` covers 2D-array and 3D texture layer
|
|
283
|
+
sampling, `texSubImage3D` sub-volume updates and rendering to a colour
|
|
284
|
+
attachment layer.
|
|
285
|
+
- `tests/e2e/gui-webgl2-mrt.test.ts` writes two fragment outputs to two
|
|
286
|
+
colour attachments and reads each back.
|
|
287
|
+
- `tests/e2e/gui-webgl2-ubo.test.ts` binds a std140 block, updates its members
|
|
288
|
+
and verifies the drawn colour.
|
|
232
289
|
- `examples/gui/webgl-cube` is a spinning, lit, textured cube; it runs in a
|
|
233
290
|
browser unchanged and is covered by
|
|
234
291
|
`tests/e2e/gui-example-webgl.test.ts`.
|
|
@@ -249,8 +306,10 @@ is bundled any more**. Verified with three.js r160 through SDL_GPU:
|
|
|
249
306
|
`CanvasTexture`, linear fog, ambient/directional/point/spot/hemisphere lights,
|
|
250
307
|
`Sprite`, `Line`, `Points` and `InstancedMesh`.
|
|
251
308
|
|
|
252
|
-
Features outside this engine's subset still cannot be emulated:
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
309
|
+
Features outside this engine's subset still cannot be emulated: transform
|
|
310
|
+
feedback and MSAA.
|
|
311
|
+
Depth textures, float render targets and compressed textures are available, but
|
|
312
|
+
three.js shadow maps still need comparative `sampler2DShadow` sampling, which
|
|
313
|
+
is not translated yet. A scene that needs one of those will not render
|
|
314
|
+
correctly; everything the previous Canvas2D fallback could show now goes
|
|
315
|
+
through the GPU.
|