xbintsc 0.3.69 → 0.3.79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +59 -4
  2. package/README.zh-CN.md +52 -2
  3. package/dist/tests/e2e/gui-advanced.test.d.ts +6 -0
  4. package/dist/tests/e2e/gui-advanced.test.js +261 -0
  5. package/dist/tests/e2e/gui-advanced.test.js.map +1 -0
  6. package/dist/tests/e2e/gui-canvas.test.d.ts +9 -0
  7. package/dist/tests/e2e/gui-canvas.test.js +233 -0
  8. package/dist/tests/e2e/gui-canvas.test.js.map +1 -0
  9. package/dist/tests/e2e/gui-example.test.d.ts +1 -1
  10. package/dist/tests/e2e/gui-example.test.js +2 -2
  11. package/dist/tests/e2e/gui-helpers.d.ts +3 -0
  12. package/dist/tests/e2e/gui-helpers.js +5 -0
  13. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  14. package/dist/tests/e2e/gui-layout.test.js +73 -0
  15. package/dist/tests/e2e/gui-layout.test.js.map +1 -1
  16. package/dist/tests/e2e/gui-media.test.d.ts +8 -0
  17. package/dist/tests/e2e/gui-media.test.js +119 -0
  18. package/dist/tests/e2e/gui-media.test.js.map +1 -0
  19. package/dist/tests/e2e/gui-paint.test.js +40 -0
  20. package/dist/tests/e2e/gui-paint.test.js.map +1 -1
  21. package/doc/ai/extensions.md +16 -3
  22. package/doc/ai/zh-CN/extensions.md +10 -3
  23. package/doc/gui/animation.md +46 -0
  24. package/doc/gui/api.md +92 -0
  25. package/doc/gui/build.md +91 -0
  26. package/doc/gui/canvas.md +98 -0
  27. package/doc/gui/dom.md +123 -0
  28. package/doc/gui/input.md +22 -0
  29. package/doc/gui/known-issues.md +66 -0
  30. package/doc/gui/layout.md +72 -0
  31. package/doc/gui/media.md +87 -0
  32. package/doc/gui/rendering.md +98 -0
  33. package/doc/gui/runtime.md +88 -0
  34. package/doc/gui-scripts.md +7 -3
  35. package/doc/gui.md +80 -612
  36. package/doc/implemented.md +1 -0
  37. package/doc/zh-CN/gui/animation.md +46 -0
  38. package/doc/zh-CN/gui/api.md +88 -0
  39. package/doc/zh-CN/gui/build.md +84 -0
  40. package/doc/zh-CN/gui/canvas.md +80 -0
  41. package/doc/zh-CN/gui/dom.md +114 -0
  42. package/doc/zh-CN/gui/input.md +21 -0
  43. package/doc/zh-CN/gui/known-issues.md +50 -0
  44. package/doc/zh-CN/gui/layout.md +60 -0
  45. package/doc/zh-CN/gui/media.md +71 -0
  46. package/doc/zh-CN/gui/rendering.md +81 -0
  47. package/doc/zh-CN/gui/runtime.md +76 -0
  48. package/doc/zh-CN/gui-scripts.md +6 -2
  49. package/doc/zh-CN/gui.md +64 -532
  50. package/doc/zh-CN/implemented.md +1 -0
  51. package/llms.txt +15 -2
  52. package/package.json +1 -1
  53. package/runtime/ext_gui/canvas.cpp +580 -0
  54. package/runtime/ext_gui/canvas.h +170 -0
  55. package/runtime/ext_gui/canvas_internal.h +59 -0
  56. package/runtime/ext_gui/canvas_raster.cpp +254 -0
  57. package/runtime/ext_gui/document.cpp +24 -1
  58. package/runtime/ext_gui/document.h +11 -0
  59. package/runtime/ext_gui/dom_api.cpp +3 -0
  60. package/runtime/ext_gui/dom_api_canvas.cpp +548 -0
  61. package/runtime/ext_gui/dom_api_internal.h +73 -0
  62. package/runtime/ext_gui/dom_api_media.cpp +274 -0
  63. package/runtime/ext_gui/dom_api_proto.cpp +80 -0
  64. package/runtime/ext_gui/gui.cpp +6 -0
  65. package/runtime/ext_gui/gui_canvas.cpp +95 -0
  66. package/runtime/ext_gui/gui_engine.h +49 -0
  67. package/runtime/ext_gui/gui_events.cpp +21 -5
  68. package/runtime/ext_gui/gui_media.cpp +193 -0
  69. package/runtime/ext_gui/image.cpp +31 -12
  70. package/runtime/ext_gui/image.h +20 -7
  71. package/runtime/ext_gui/layout.cpp +52 -5
  72. package/runtime/ext_gui/layout.h +33 -1
  73. package/runtime/ext_gui/layout_flex.cpp +263 -99
  74. package/runtime/ext_gui/layout_flow.cpp +85 -9
  75. package/runtime/ext_gui/layout_flow_internal.h +84 -5
  76. package/runtime/ext_gui/layout_inline.cpp +54 -37
  77. package/runtime/ext_gui/media.cpp +514 -0
  78. package/runtime/ext_gui/media.h +104 -0
  79. package/runtime/ext_gui/nanosvg.h +3278 -0
  80. package/runtime/ext_gui/nanosvgrast.h +1472 -0
  81. package/runtime/ext_gui/paint.cpp +123 -38
  82. package/runtime/ext_gui/paint.h +36 -6
  83. package/runtime/ext_gui/renderer.cpp +113 -10
  84. package/runtime/ext_gui/renderer.h +49 -9
  85. package/runtime/ext_gui/renderer_image.cpp +203 -77
  86. package/runtime/ext_gui/renderer_internal.h +6 -8
  87. package/runtime/ext_gui/renderer_text.cpp +11 -26
  88. package/runtime/ext_gui/style.h +11 -0
  89. package/runtime/ext_gui/style_print.cpp +11 -0
  90. package/runtime/ext_gui/style_ua.cpp +7 -0
  91. package/runtime/ext_gui/style_values.cpp +40 -1
  92. package/runtime/ext_gui/svg.cpp +182 -0
  93. package/runtime/ext_gui/svg.h +46 -0
  94. package/runtime/ext_gui/text.cpp +156 -25
  95. package/runtime/ext_gui/text.h +11 -2
  96. package/runtime/ext_gui/window.cpp +4 -1
  97. package/scripts/build-gui.ts +148 -6
  98. package/scripts/check-file-length.ts +5 -1
  99. package/scripts/install-gui-deps.sh +28 -0
@@ -0,0 +1,81 @@
1
+ # 绘制、GPU 与文本
2
+
3
+ > 语言 / Language:[English](../../gui/rendering.md) | **简体中文**
4
+
5
+ > 属于 [GUI 扩展](../gui.md) 的一部分;引擎总览、锁定决策与路线图在那里。
6
+
7
+ ## 绘制与显示列表(M4a/M4b)
8
+
9
+ `runtime/ext_gui/paint.{h,cpp}` 按画家顺序遍历布局树,生成一个与后端无关的
10
+ `DisplayList`:一个按画家顺序排列的 `ops` 列表,包含**矩形**(背景与四条实心
11
+ 边框,带 `border-radius`)、**文本 run**(每个携带其文本、颜色与已解析的
12
+ `FontSpec`)与**图像**,背后由并列的负载数组支撑。每个 op 还携带当前生效的裁剪
13
+ 矩形(最近的 `overflow: hidden` 祖先的 padding box),因此 `overflow` 裁剪与
14
+ `z-index` 顺序都能保留到渲染器。`DisplayList::dump()` 为
15
+ `paintList()`/`paintCount()` 提供数据。
16
+
17
+ `opacity` 只在两端生效:计算值为 `0` 的盒子(及其整棵子树)不绘制任何内容,
18
+ 大于 `0` 的一律按完全不透明绘制。图形只携带其颜色自身的 alpha,因此没有部分
19
+ 透明盒子的合成——CSS 解析器接受的 `opacity` 过渡因此也看不到效果。把隐藏态写成
20
+ `opacity: 0` 来隐藏/显示一层,正是 `examples/gui/pelican-bike` 游戏结束遮罩的做法。
21
+
22
+ `runtime/ext_gui/renderer.{h,cpp}` 把该列表转换为每个窗口的批处理顶点缓冲
23
+ (图形、字形四边形与图像),并把管线、裁剪与纹理都相同的连续 op 合并为
24
+ **绘制 run**。每个 run 用各自的 GPU scissor 矩形(按设备像素缩放)绘制,因此
25
+ 画家顺序与 `overflow` 裁剪按 op 生效,并通过 SDL_GPU 图形管线完成:
26
+
27
+ - 共享管线会依据设备支持的着色器格式延迟创建。SDL_GPU 的三个后端各自只接受一种
28
+ 二进制格式,且都不会在运行时编译 GLSL/HLSL,因此引擎三种都随包提供,由
29
+ `selectShader`(`renderer_shaders.h`)挑选设备可用的那一种:Metal 用 **MSL**
30
+ (SDL 从内嵌源码编译)、Vulkan 用 **SPIR-V**
31
+ (`runtime/ext_gui/spirv/*.{vert,frag}`,由 `glslc` 编译)、Direct3D 12 用
32
+ **DXIL**(`shaders.hlsl`,由 `dxc` 编译)。
33
+ `scripts/build-gui-shaders.mjs` 重新生成内嵌二进制块,产物已提交,因此构建引擎
34
+ 无需额外工具。各格式的描述符绑定不同——SPIR-V 遵循 `SDL_gpu_vulkan.c` 的布局
35
+ (uniform 在 `set=1, binding=0`,采样器在 `set=2, binding=0`,纹理在
36
+ `set=2, binding=1`)——且 glslc 会把所有入口点命名为 `main`,而 MSL/DXIL 保留
37
+ 各自的描述性名称。
38
+ 需注意:SDL 3.2.10 的 Direct3D 12 后端无法创建着色器中声明了 uniform buffer 的
39
+ 图形管线(返回 `E_INVALIDARG`);引擎正是用这种方式传视口,因此 DXIL 这条路已
40
+ 构建但在上游修复前不可用。
41
+ - 图形片元着色器中的**圆角矩形距离场**提供抗锯齿填充;顶点携带
42
+ `position`、`local`、`half extents`、`radius` 与颜色,一个视口尺寸的
43
+ push constant 负责投影。启用 alpha 混合。
44
+ - 文本管线采样一张**单一共享的灰度字形图集**(`R8_UNORM`,2048²,
45
+ shelf 打包,LINEAR 过滤),把每个字形绘制为带纹理的四边形
46
+ (`position`、`uv`、颜色),按覆盖率调制 alpha。
47
+ - 只有在文档或视口变化(`geometry.dirty`)时才上传几何数据,因此稳态帧
48
+ 只需绑定并绘制。
49
+
50
+ 窗口背景(`setBackground`)是渲染通道的清除色。
51
+
52
+ M4 仍需完成:渐变。
53
+
54
+ ## 文本技术栈(M4b)
55
+
56
+ `runtime/ext_gui/text.{h,cpp}` 封装 **HarfBuzz**(整形)与 **FreeType**(度量
57
+ 与最终光栅化)。两者都静态构建,并由 `scripts/build-gui.ts` 链接进 `gui.a`。
58
+
59
+ - 字体从常见系统路径解析(macOS 上的 Helvetica/Arial、Linux 上的
60
+ DejaVu/Liberation、Windows 上的 Segoe UI/Arial),可用 `XT_GUI_FONT`
61
+ (以及 `XT_GUI_FONT_MONO`)覆盖,并按 `(字体类别, 字号)` 缓存。目前只使用
62
+ 常规直立字面;字重/斜体选择是后续细化。
63
+ - **主字面缺少的码点会回退到其他字面。** 引擎维护一条小的回退链——先平台的
64
+ 符号字体(Segoe UI Symbol、Apple Symbols、Noto Sans Symbols、DejaVu Sans),
65
+ 再是另一字体类别——并为每个码点解析出回退链中第一个拥有该字形的字面。
66
+ run 以每个字面一段的方式整形,因此混合 run(拉丁文加箭头、制表符或几何图形,
67
+ 或 CJK 中的拉丁文)会绘制每个字形,而不是把缺失的字形当作 `.notdef` 丢弃。
68
+ 这正是让诸如 `▮`/`▯`(U+25AE/U+25AF,鹈鹕自行车速度条所用)这样的字符在
69
+ CJK 主字面没有几何图形时仍能显示的原因。每个整形后的字形携带自己的字面
70
+ (`ShapedGlyph::font`),渲染器据此从正确的图集条目光栅化;`xt_text_scale`
71
+ 把字面映射到显示的缩放系数。
72
+ - `xt_text_measure_width` 用 HarfBuzz 对 run 整形(因此字距调整与连字会生效),
73
+ `xt_text_metrics` 返回 FreeType 的 ascent / descent / 正常行高。当找不到
74
+ 字体文件时,模块回退到确定性的逐字节近似,因此布局仍可工作。
75
+ - `xt_text_shape_run` 返回定位后的字形,`xt_text_rasterize` 渲染 8 位位图;
76
+ 渲染器把它们打包进图集。在 HiDPI 显示器上,字形以
77
+ `font_size * SDL_GetWindowPixelDensity` 光栅化,而四边形以逻辑像素定位,
78
+ 因此文本保持清晰。
79
+ - 布局使用这些真实度量进行文本宽度、断行与 `line-height: normal` 计算;
80
+ `measureText`/`fontMetrics` 把它们暴露给测试。
81
+
@@ -0,0 +1,76 @@
1
+ # 运行时集成与内存
2
+
3
+ > 语言 / Language:[English](../../gui/runtime.md) | **简体中文**
4
+
5
+ > 属于 [GUI 扩展](../gui.md) 的一部分;引擎总览、锁定决策与路线图在那里。
6
+
7
+ ## 事件循环集成
8
+
9
+ 运行时改动(已落地):
10
+
11
+ - `int xt_loop_poll(int timeout_ms)` —— 一次 reactor 迭代;`0` 非阻塞轮询,
12
+ `< 0` 阻塞。
13
+ - `void xt_loop_set_main(xt_main_loop_fn fn)` —— 宿主可以接管主循环。
14
+ `xt_run_event_loop()` 会委托给它;生成的 `main` 不变。
15
+ - `xt_loop_set_main(NULL)` 恢复默认的 `select(2)` 循环。
16
+
17
+ GUI 引擎既可以通过 `xt_loop_set_main` 注册自己的循环,也可以暴露显式的
18
+ `run()`;**M2 使用显式的 `run()`**,因此窗口是 TypeScript 程序可以控制的
19
+ 普通原生调用。每次 tick 它都会:
20
+
21
+ 1. 为每个窗口泵送 SDL 窗口/输入事件,
22
+ 2. 当显示列表变化时构建/上传它,并渲染每个打开的窗口(清除通道 + 图形几何
23
+ + 文本几何),
24
+ 3. 调用 `xt_loop_poll(0)` 与 `xt_drain_microtasks()`,使 socket/定时器与
25
+ `await` 续体持续取得进展,
26
+ 4. 重复直到所有窗口关闭或调用 `quit()`。
27
+
28
+ `xt_loop_set_main` 钩子仍保留给希望自行掌控循环的宿主。
29
+
30
+ 这使网络 I/O、定时器与 `await` 在 GUI 程序内部继续工作。
31
+
32
+ ## 原生 ↔ TS 桥接
33
+
34
+ - **TS → 引擎**:直接的 `xt_gui_*` 调用 / 窗口方法。
35
+ - **引擎 → TS**:`xt_call_with_this(fn, thisValue, argc, argv)`,其中函数值
36
+ 从 TS 捕获(例如用 `win.on(...)` 注册的事件处理器)。
37
+ - **未来的页面→原生 RPC**:在没有页面 JS 时不需要;原生 TS 就是控制器。
38
+ 如果日后加入声明式层,它将使用相同的 `xt_call_*` 入口。
39
+
40
+ ## 内存与 GC
41
+
42
+ GUI 程序的大部分内存不在运行时堆里:DOM 树、分离节点池、元素句柄表、布局盒树
43
+ 与显示列表都是 C++,标记-清扫收集器看不到它们。`stats()`(见[诊断](./api.md))报告其中
44
+ 可能增长的部分:
45
+
46
+ | 计数器 | 稳定状态下的期望值 |
47
+ | --- | --- |
48
+ | `treeNodes` | 常量——即编写的文档 |
49
+ | `detachedRoots`、`detachedNodes` | 0;若脚本自己移除了节点并仍持有,则为常量 |
50
+ | `handles`、`handleSlots` | 常量——每个脚本仍引用的元素一个 |
51
+ | `listeners` | 常量——每个注册了监听器的元素一个 |
52
+ | `heapBytes`、`heapAllocations` | 在收集器阈值附近呈锯齿波动 |
53
+
54
+ 其余都是每帧用完即释放的临时数据(盒、显示列表、顶点暂存)。若某个计数器随帧数
55
+ 持续上升,那就是最该先查的泄漏特征;`tests/e2e/gui-retention.test.ts` 断言常见
56
+ 变更模式不会改变这些计数。
57
+
58
+ 读这些数字时有两处引擎细节需要注意:
59
+
60
+ - **谁会释放节点。** `innerHTML`/`inner`/`textContent`/`replaceChild` 会释放它们
61
+ 丢弃的子树;`removeChild`/`remove` 会保留(见[节点生命周期](./dom.md))。因此用
62
+ `innerHTML` 每帧重建精灵的游戏内存恒定。
63
+ - **什么仍会累积。** 用 `createElement` 创建却从未插入的节点会留在池中:收集器
64
+ 没有弱引用,引擎无法知道脚本已丢弃其句柄。脚本移除后又不再引用的节点同理。
65
+ 两者都以*存活句柄*数量为界,但每帧创建元素却不插入的程序会让池随帧数增长。
66
+ - **矢量图像按像素尺寸缓存。** 位图 `<img>` 只解码一次并按路径缓存,而 SVG 会按
67
+ 绘制设备尺寸光栅化,因此解码像素缓存与 GPU 纹理缓存会为每个不同尺寸保留一项。
68
+ 对静态布局(常见情况)是常量,对少量尺寸也很小;但每帧把同一 SVG 重排到新整数
69
+ 尺寸的程序会不断累积条目——此时宜改为动画位图,或让矢量图保持固定尺寸。
70
+
71
+ `heapBytes` 背后的收集器是 `runtime/xt_alloc.c` 中的非移动标记-清扫。它的触发
72
+ 阈值跟随存活集:每次回收后阈值变为存活堆的 2 倍(下限 1 MiB),因此存活集小
73
+ 就意味着回收频繁而轻量、常驻内存有界。用 `XT_GC_THRESHOLD=<bytes>` 可以固定
74
+ 阈值;`XT_GC_TRACE=1` 会为每次回收向 stderr 打印一行(回收前后的堆与存活分配数、
75
+ 下一次触发阈值,以及按对象类型统计的存活集)——DOM 泄漏正是这样被定位到节点池的。
76
+
@@ -226,6 +226,9 @@ b.addEventListener("click", () => { n = double(n) + 1; b.textContent = String(n)
226
226
  - 变更:`appendChild` / `insertBefore` / `removeChild` / `replaceChild` /
227
227
  `remove` / `cloneNode(deep?)`。
228
228
  - 几何/焦点:`getBoundingClientRect`、`focus`、`blur`、`click`。
229
+ - 媒体(`<video>` / `<audio>`):`play()` / `pause()` / `load()` 与
230
+ `src`、`paused`、`ended`、`duration`、`currentTime`、`volume`、`muted`、
231
+ `loop`、`autoplay`、`videoWidth`、`videoHeight`、`readyState` 属性。
229
232
  - 事件:`addEventListener(type, fn, optionsOrCapture?)`、
230
233
  `removeEventListener`、`dispatchEvent`。
231
234
 
@@ -323,7 +326,8 @@ Event 的 `target` 是句柄,其描述符(`toString`)与同一 `div#id.cla
323
326
 
324
327
  ## 需要更新的文档
325
328
 
326
- - `gui.md` —— 非目标(“执行页面 `<script>`” → “执行*运行时*页面脚本;
327
- 编译期脚本为 AOT 编译”)、锁定决策 #1、面向 TS 的 API、里程碑、进度日志。
329
+ - `gui.md`(总览)与 `gui/dom.md` —— 非目标(“执行页面 `<script>`” → “执行
330
+ *运行时*页面脚本;编译期脚本为 AOT 编译”)、锁定决策 #1、AOT 脚本章节、
331
+ 里程碑与进度日志;面向 TS 的 API 见 `gui/api.md`。
328
332
  - `implemented.md` / `unimplemented.md` —— 把新的 DOM/脚本能力相应迁移。
329
333
  - `README.md` —— 提及 DOM API + AOT 脚本。