文档 / 总览

YSU 是什么

YSU 是渲染内核。输入一段 HTML 和一个基准地址,输出一块 RGBA 像素。中间那六个阶段——词法、树构建、CSS、样式、布局、绘制——是这个项目里代码量最大的部分。

它是独立的 crate(crates/ysu),不依赖外壳,也不认识 Qt。同一份代码既被 Vexo 的外壳经 C ABI 调用,也能直接当 Rust 库用。

它不做什么

不建窗口。 窗口、事件循环、菜单、对话框全是外壳的事。内核不知道窗口长什么样,也不知道有没有窗口。

不认识鼠标和键盘。 外壳把坐标递给内核,内核回答「这个点上压着哪个链接」,就到这里为止。点下去要做什么,是外壳的决定。

不存历史。 前进后退是外壳维护的。内核只管当前这一份文档。

不执行脚本。 <script> 会被正确解析成 DOM 节点——树构建阶段照规范处理脚本数据、双重转义、</script> 的边界——但内容没人执行。脚本引擎 JSE 是另一个 crate,两者还没接上。

不记 Cookie、不做缓存、不开连接池。 每取一个资源都是一条新连接,取完就断。

不做多进程隔离。 整件事在一个进程里。

它对外露出什么

对 Rust 调用方,是 Engine 这个门面:建实例、喂 HTML、设视口、渲染一帧、查页面高度、按坐标找链接。

对 C 调用方,是 crates/ysu-capi 里的 26 个 extern "C" 函数。外壳用的就是这一套。

两个口子后面是同一套东西,C ABI 没有额外能力。

关键的几个取舍

一次渲染是离屏的。 ysu_engine_render 把当前视口范围内的内容画进一张纹理,再读回 CPU 内存,交出一块 RGBA 像素。这里没有交换链、没有 wl_surface、没有垂直同步。外壳拿到像素自己贴到部件上。

代价是每次都有一趟 GPU 到 CPU 的回读,白送一份拷贝。换来的是边界极窄:GPU 设备、字形图集、着色器这些生命周期全待在 Rust 里,外壳连 wgpu 这个名词都不需要知道。

渲染是按需的,不是每帧都画——滚动、加载完、改尺寸、改缩放才画一次。

排版按逻辑像素算,成像按物理像素出。 传进内核的是逻辑尺寸加设备像素比。传物理尺寸进去会让页面按物理宽度排版,字在屏幕上偏小。设备像素比只影响成像分辨率,不影响断行位置。

滚动量是布局坐标。 外壳按屏幕像素记滚动,交给内核前要除以缩放倍数。内核内部顶点和剪刀矩形走同一套换算,两步都不能少。

中间产物都能单独调用。 词法器、树构建、CSS 解析、层叠、布局、绘制各自是独立的模块,各自的产物是下一步的输入。写测试、查问题时可以从任意一环切进去,不用整条链跑一遍。

数字

Rust 侧内核约两万三千行,另外 C ABI 一千四百行、JSON 两千三百行、脚本引擎七千三百行。GPU 用 wgpu,文本整形与栅格化用 cosmic-text,TLS 用 rustls,字符集用 encoding_rs。除此之外的每一层都是自己写的。