M97 的入口是一份 macOS M1 真机报告:探测认出了 Apple M1(adapters=1 /
hardware=true),日志却写着 `renderer = auto (wgpu -> tiny-skia)`,看起来
像在跑 CPU。读源码发现那行是**常量**:`probe.rs` 按 `ICED_BACKEND` 未设就把
`"auto (wgpu -> tiny-skia)"` 打出来,与实际后端无关。真正的失败被 iced 吞掉:
`iced_renderer-0.14.0/src/fallback.rs` 的 `Compositor::with_backend` 把 wgpu 的
错误装进 `errors` 向量,tiny-skia 一成功就整体丢弃。
## 判据改为单项,不再交给 iced 回退
`ICED_BACKEND` 现在**永远是单项**(`wgpu` / `tiny-skia`):能跑起来就必定是它;
起不来就必然是 `iced::Error::GraphicsCreationFailed` 里的具名错误。判据是纯函数
`renderer::decide(request, probe, gpu_compiled)`,落在 `renderer/decision.rs`,
表驱动测试逐行钉死 1~6 行 + 「除强制 CPU / 未编 wgpu 外必须留兜底」的守门。
判据表(条件 / 选哪个 / 日志该打什么 / 谁能验证)在 `renderer/mod.rs` 顶部。
## 安全网改成换进程
第一版是「打印错误 → 在本进程再 run() 一次」,Windows 实测直接 panic:
`Create event loop: RecreationAttempt`(winit 一个进程只能建一次事件循环)。
改成 `respawn`:子进程只换 `ICED_BACKEND` + `ULL_GPU_FALLBACK`,父进程透传它的
退出码。子进程看到 `ULL_GPU_FALLBACK` 必须跳过探测,否则会无限递归重开。
## 取证通道不许自己降级
带 `--screenshot`(含 `--screen=gallery`)时 wgpu 起不来就**不出图**、退出码 1,
因为换了后端出的图会撒谎;交互启动才允许降级。每张图的日志现在打**实际**后端
(`[shot] … renderer=wgpu`),以前打的是请求值 `auto`——同一个病(在需要结果的
地方打请求)在仓库里出现了两次,这是第二次。
## 可观测性
- `log` 桥(`renderer/log_bridge.rs`)把 `iced_wgpu` 的
`Selected: AdapterInfo{…}` 接到 stderr:这是本仓库第一次能自证「真在用 GPU」。
按 target 分级(`iced_wgpu*` Info、`wgpu*` Warn),否则 Vulkan loader 会刷 90 行噪声。
- `renderer/preflight.rs`(默认关,`ULL_PREFLIGHT=1`):开窗前拿 wgpu 的具名错误。
实测 Windows RX 6600 XT 上 took=717ms,所以不进默认路径。
- `app/backend.rs` 的档位判据跟着改成读单项;**真降级到 CPU 的那一次面板 α 现在会
正确强制 1.0**(以前静默降级后仍按 GPU 档判)。
Windows 实测:auto → wgpu + `Selected: AdapterInfo{…backend: Vulkan}` 出图;
`--renderer=cpu` 逐字不变;`WGPU_BACKEND=metal` 造失败 → 具名错误 → 交互路径降级
不崩(首帧 531ms)/ 取证路径拒绝出图 exit 1。macOS Metal 路径需真机复核,
命令与期望日志行写在 `renderer/mod.rs` 的「真机验收」一节。
61 lines
2.7 KiB
Rust
61 lines
2.7 KiB
Rust
//! # unlucky-app-iced · iced 版桌面壳(P4 / D26)
|
||
//!
|
||
//! 本 crate 是「UI 从头写」的第一步:**只做骨架**(能开窗、颜色/字体对、层级在),
|
||
//! 业务页面、组件库、i18n 全文案、玻璃 shader 都是后续 mission。
|
||
//!
|
||
//! | 模块 | 职责 | 不负责 |
|
||
//! |---|---|---|
|
||
//! | [`theme`] | 令牌(`unlucky-design`)→ iced 类型:`Color` / `Font` / `Radius` / `Theme` | 发明令牌值 |
|
||
//! | `cli` | `--renderer` / `--software-renderer` / `--theme` / `--screenshot*` 参数 | 业务开关 |
|
||
//! | `probe` | 渲染后端探测(D27)+ 写 `ICED_BACKEND`,失败自动降级 CPU | 绘制 |
|
||
//! | `fonts` | 字体栈求值(D28 前半):平台族名 → 系统字体库校验 → 启动日志 | 中文字体下载 |
|
||
//! | `renderer` | **渲染后端判据表**(M97)+ wgpu 预检 + `log` 桥:什么条件选 wgpu / 什么时候降级 CPU / 日志该长什么样 | 实际绘帧 |
|
||
//! | `app` / `shell` | Elm 三件套 + 自绘标题栏 / 导航轨 / 内容骨架 | 业务逻辑 |
|
||
//! | `shot` | `window::screenshot` → PNG(验收通道) | 图像比对 |
|
||
//! | `icons` | 令牌里的 SVG path → iced `svg` 控件 | 画新图标 |
|
||
//! | `platform` | 平台窗口能力(D36):整窗 α 的纯判据 + win32 FFI(补分层样式 + 下发 α)—— **全仓唯一** `unsafe` 落点 | 窗口创建 / 业务开关 |
|
||
//!
|
||
//! ## 消费纪律
|
||
//!
|
||
//! **组件层零硬编码色值/字号/半径/间距**:一律走 [`theme::Ui`]。守门测试在
|
||
//! `tests/no_hardcoded_tokens.rs`(扫 `src/**`,出现 `#RRGGBB` / `Color::from_rgb(` 即红)。
|
||
//!
|
||
//! ## 与 Slint 版的关系
|
||
//!
|
||
//! 并存期:`crates/app` 仍出 `unlucky-launcher`,本 crate 出 `unlucky-launcher-iced`。
|
||
//! 等价验收后翻牌(bin 改名 + 旧 crate 降级),步骤写在
|
||
//! `DevDocs/SubAgentsTalk/app-iced-骨架与翻牌.md`。
|
||
|
||
pub mod pages;
|
||
pub mod platform;
|
||
pub mod renderer;
|
||
pub mod shot;
|
||
pub mod theme;
|
||
|
||
mod app;
|
||
mod cli;
|
||
mod fonts;
|
||
mod icons;
|
||
mod probe;
|
||
mod shell;
|
||
|
||
pub use cli::{Backend, Options};
|
||
pub use probe::Probe;
|
||
|
||
/// 启动 UI(阻塞到窗口关闭)。渲染后端由 `probe` 在 `run()` 之前写进 `ICED_BACKEND`。
|
||
pub fn run(options: Options) -> iced::Result {
|
||
app::run(options)
|
||
}
|
||
|
||
/// 与 [`run`] 同义,但把命令行做成最终结论:`Options::from_env()` + 启动日志。
|
||
///
|
||
/// 二进制入口用 [`Options::from_env`] + [`run`] 自己拼(要额外处理 `--diff`),这个函数留给
|
||
/// 集成测试与将来的 CLI 复用。
|
||
///
|
||
/// # Errors
|
||
/// 参数不合法时返回人话(不 panic、不 `exit`)。
|
||
pub fn run_from_env() -> Result<iced::Result, String> {
|
||
let options = Options::from_env()?;
|
||
Ok(run(options))
|
||
}
|