背景
ADR-0012 把三个 L0 内核接线收口后,P2「平台与工程」只剩 C ABI 的出口形态问题:
v1 只有离屏一脉(new_offscreen → render_to_rgba → PNG 读回),设备端(OHOS
车机/平板)的真实连续渲染仍只能走 Rust 侧的 harmony/render-host。C++ 宿主
(Drogon/ROS2)要上设备,就必须能拿原生窗口句柄直接驱动内核——否则「C ABI = 语言
边界」这句话在设备端不成立。
同时,v1 的生命周期语义只被 render-host 侧测过(tests/lifecycle.rs 的 resize /
device-lost / 重建),C ABI 侧没有对位的行为锁:C 宿主在 device lost 后拿到什么
错误码、旧帧会不会被静默回流、resize 后尺寸是否同步,都只写在文档里没人验。
选项
- A. 表面继续留在 render-host,capi 只离屏。 改动最小,但 C++ 宿主上设备仍需 一个 Rust 桥进程/库,「零 Rust 认知驱动内核」在设备端破功。
- B. capi 自建 wgpu 实例与表面,内核只认
wgpu::Surface(选定)。 内核已有Renderer::new_from_surface与 External target;平台句柄到 wgpu 的翻译留在 capi(等价于 render-host 的既有分工),内核零#[cfg(ohos)]污染。 - C. 内核查平台句柄(吃
OHNativeWindow*)。 会把平台代码搬进 L0 内核,违反 ADR-0005/0011 的「平台代码住 host 桥内」,且内核要多套平台 cfg,收益为零。
证据
- 内核就绪:
Renderer::new_from_surface(instance, surface, size, scale, font, color_space)已存在,窗口/外部表面两条路径共用encode_frame;外部表面不支持rebuild_after_device_lost(只对 winitWindowtarget 做重建),文档即 「重新挂载即恢复」。 render_to_rgba不上屏:它在 External target 上仍渲染到内部帧纹理再读回, 不 present。所以表面句柄的 submit 必须走render(),与离屏句柄分道。- wgpu 单副本:render-host 踩过「两份 wgpu 编译单元 → Surface/Device 类型不
匹配」,结论是精确锁版
=30.0.1。capi 目前经内核传递依赖 wgpu;v2 直接依赖同 版本不会产生第二份编译单元。 - GLES-only:OHOS 模拟器与车机没有 Vulkan,
Backends::GL是唯一可用后端。 - CI 无 GPU 设备:GLES 真机路径不能进 CI;可锁的是平台无关语义(resize 尺寸 同步、lost 出口、重建字节一致),与 render-host lifecycle 同一口径。
拍板
- 形参扁平、不带结构体:
new_surface(surface_kind, window_handle, display_handle, w, h, scale, font_path, font_len)。句柄用uintptr_t、kind 用int32_t——守 §7「不跨 struct/enum」;display_handle为 X11/Wayland 等后续 kind 占位,OHOS 恒 0, 追加 kind 不改签名。v2 只实现ZJ_SURFACE_OHOS_NDK(0);其余 kind 返回 NULL +zj_last_error,不在 ABI 边 panic。 - 依赖:capi 直依赖
wgpu = "=30.0.1"与raw-window-handle = "0.6"(与内核 同一编译单元);许可证 MIT/Apache-2.0,已在docs/DEPENDENCIES.md收录。 - 表面句柄的帧出口 = present:
submit/submit_delta走render(),不缓存 PNG;readback_png返回ZJ_ERR_NOT_READY、readback_png_size返回 0 (不给设备端截屏,避免每次截图双渲一帧)。 - device lost 语义统一:
zj_renderer_simulate_device_lost是演练钩子 (只置标志、不碰 GPU,镜像内核同款 API),供现场演练与 CI 行为锁。置位后submit/submit_delta/resize/readback_png一律ZJ_ERR_DEVICE_LOST,readback_png_size返回 0(旧帧不得回流);恢复 =destroy+ 重新new(外部表面不做重建,与内核一致)。 - 不做:不把平台句柄类型搬进内核;不做
zj_free/回调/asyncio;不为 surface 提供读回;不预实现桌面 kind(无真实消费者,避免锁错平台形状)。
落地与验证
- 导出:
zjchina-render-capi/src/lib.rs(zj_renderer_new_surface、zj_renderer_simulate_device_lost、zj_capi_version = 2、句柄ModeOffscreen/Surface、submit 分道、readback/resize 的 lost 守卫); - 契约:
docs/capi-contract.md§1/§5/§6/§7/§8(new_surface从预留移入权威块、ZJ_SURFACE_OHOS_NDK进 §5)、zjchina-render-capi/zjchina_render.h; - 漂移锁:
tests/capi_drift.rs(6 项,新增表面类型三处一致断言); - 行为锁:
tests/lifecycle.rs(resize 尺寸同步 + 末帧作废、lost 三出口响亮报错 且重建字节一致、new_surface拒绝前不碰 GPU);tests/smoke.rs版本锚点、tests/c_smoke.c(纯 C 链接 + 拒绝路径 + golden 比对); - 验证:capi
cargo test(drift 6 / frame_seq 1 / lifecycle 3 / smoke 4)、scripts/test-capi.sh(C 冒烟与内核 golden 逐字节一致)、内核cargo test、render-host --test lifecycle --test article(6 + 1)全绿。 - 未验:OHOS 真机 GLES 上屏(待 B2,随
scripts/verify-render.sh --device)。
日期:2026-11-07。拍板者 zj。