实现已合并并验收(2026-09-10)
PR #30 合并了 NVTEGRA 直接导入、deko UI 后端、帧/GPU 资源生命周期及真实输入驱动的在线自适应选帧。用户实际游玩后确认当前实现达到可接受状态,授权提交和合并。
本单按实现主体完成关闭;不是发布 Release 或全部平台边界已验收的声明。
历史规划(以下“尚未实现”等状态为合并前记录,以以上结论及 #29 为准)
目标与规范
Switch 的视频、UI、窗口/swapchain 统一使用 deko3d C API,直接采样 NVTEGRA 硬件帧,消除 VIC/CPU 整帧中转和 SDL 视频上传。desktop 保留 SDL,共用业务状态与绘制语义。首版 H.264、8-bit、720p60;保留音频、HID、完整 UI 与同一后端的软件帧上传。
设计证据基线:a6f743465817913a965ca075276c1f231420cc98。
规范:工作区 docs/VIDEO_RENDERING_DESIGN.md(架构修订版)、docs/decisions.md D-051;职责同步于 DEVELOPMENT/UI_UX_DESIGN。文件仍在本地工作区,未作为远程代码提交发布。当前 Issue 是实施任务、审计状态和测量结果的唯一入口。
旧 #10 测量中的 decode 已包含 transfer,不能重复求和。FPS 保持 60 不代表无收益;Present 返回不代表物理扫描完成。硬件直显的 transfer/upload 必须为 0,同时验证总体 CPU、帧时间和资源使用。
修订后的架构约束
- application 主线程协调运行/暂停/停止/退出,唯一计算远端输入准入;区分 ui_request_id、session_id、video_epoch、pool_id。
- runtime 管理 IHS 连接、音频/HID 与协议事实;不承担逐帧完成回报。停止进度使用持久事实快照,不能依赖只投递一次的普通 UI 事件。
- FrameLease 携带 AVFrame 引用/元数据/ticket,无 session 裸指针;GpuBatch 从 Recording 起统一保留所有 GPU 资源。pending=1、current=1、批次=2、frame envelope=8。
- ihslib FrameTracker 统一接收、解码阶段、完成、过期和统计结算。renderer 发布有身份的 completion ticket;移除 main→通用 runtime worker 的逐帧 outbox。CPU endpoint 可在 session 销毁后安全吸收迟到结果,不再访问 session。
- 默认冻结旧 decoder domain,等 callback 结束、外部 lease=0、renderer 映射=0,再由 runtime 销毁 codec。跨 codec free 的输出帧保证不作为首版正常停止前提。
- 主线程 Stopping/Exiting 期间继续 pump/collect,最后才 join/destroy。普通 stop 等旧视频使用结束,允许健康 gfx 继续画 UI;exit 不再提交新 UI 批次。
- 用户新 session 等旧 session/资源清理完成后启动,最多保存一个待启动意图。同 session 的 video channel 重启允许受限重叠:全管线最多 2 个 decoder domain;第三个容量拒绝会按现有 IHS 语义断开会话,不能解释为可重试 busy。
- renderer 全局最多 2 个 PoolGroup,每组 32 map,导入唯一 backing ≤128 MiB;整组退役。该预算不冒充 FFmpeg 全部 pool/DPB 的硬上限,完整内存回落另测。
- 视频错误进入统一 session stop;device/queue 错误属于 application,包括菜单与旧提交导致的设备失效。单个旧视频内容错误可忽略,设备故障不能按 epoch 过滤。
- 不新增 detached/渲染线程,不修改认证/PIN 或 wire 消息定义,不运行时混用 SDL/Mesa 与 deko,不自动发布或推送设备。
前置与实施依赖
依赖:P0 → P2/P3;P1 → P3/P4;P2 → P3/P4;P3+P4 → P5 → P6。
P0 可用 desktop 假 GPU 与真实 IHS stats 实现验证,P1 可独立使用本地 fixture;不要求先完整迁移 UI 才发现所有权问题。
G0 的统计语义与停止条件、G1 的 SDK 保证未通过,不进入正式默认后端切换。
P0 — 控制/资源契约与 FrameTracker(3–5 人日)
文件:新增 common/app_lifecycle.c/.h、frame_lifetime.c/.h 与测试;修改 ihslib 的 session 回报接口、frame_stats.c/.h、callbacks.c、video 接收/统计发送接入点和 tests/session/test_frame_stats.c。
实现:
- 主线程单一控制策略;分开 UI 意图和协议事实,统一输入准入;模拟异步 stop、清理事实和最终 join 条件。
- FrameLease/GpuBatch 的固定容量与析构条件,GPU 对象只由 renderer 所在线程回收;Codec domain 的最后使用条件不依赖 decoder DPB 自己的引用归零。
- FrameTracker CPU endpoint、预分配记录(初始 256)、record identity/slot version、单 writer completion 发布和 acquire 消费。没有 runtime 逐帧队列,主线程完成/归还 ticket 不取统计锁。
- 显式 timestamps/有效位/presentation_serial;收到后序显示不立刻结算仍未完成的前序。初始结算等待 250 ms,窗口、过期和关闭规则见规范 §5.2。
- 本地结算位置与 wire 显示前沿分离,无 Displayed 时仍能回收终结记录;有界保留累计值和逐帧明细。复用条件包含 ticket 引用归零,不能用 frameId 重新 acquire 旧槽。
- 保持 wire latest_frame_id 与累计统计语义;不能用伪造 id/Displayed 或关闭统计规避兼容问题。原接口调用方通过 adapter 进入同一数据模型,不维护两份完成状态。
验收:
- 假 fence 与析构计数:A 多次重绘后换 B、Recording 取消/部分提交、逆序观察完成、slot/version 复用、pending 覆盖、stop/take/submit 交错。
- 真 stats fixture:10 的 ticket 未完成时接收 138;12→11;65535→0;非 0 首帧;已结算/关闭后的迟到完成;记录满与长期无 Displayed;零值时间戳;诊断 ON/OFF。检查发送报文和累计值,不能只看本地计数。
- session destroy 与最后 lease 释放两种顺序;endpoint 不访问旧 session;codec 析构一次且不早于 renderer 映射释放。
- fatal 后 UI 仍处串流页不能重开输入;停止期间 UI 绘制不阻止视频资源退完;exit 不新增批次;新 session 只能在 cleanup complete 后启动。
- ASan/UBSan;并发发布、domain 回收、状态快照用 TSan/受控交错。不能靠 sleep 避开竞争。
P1 — SDK/直显隔离验证(2–4 人日;底层兼容问题另估)
文件:tools/switch-video-surface-probe、合法本地 H.264/彩条 fixture、对应 CMake;从 probe 阶段接入固定 deko3d/uam 与可复现 shader 编译。
必须取得的证据:
- 固定 FFmpeg 包成功 receive 是否已完成 NVDEC;不假设有公开 decode fence。
- deko 单 queue/双输出采样原 backing,验证 block-linear/padding/UV offset、颜色、同 surface 重用、cache 可见性;暂停解码仍能重画 current/UI。
- 冻结 codec domain 后继续使用既有帧;GPU 使用结束、销毁映射、释放帧、再销毁 codec 的完整顺序。首版不依赖“先 free codec 再采样”成立。
- PoolGroup 的映射数、context 析构、实际 FFmpeg pool/DPB 内存;反复同代换 pool 后回落。明确实际包可观测统计或保守容量估算,未知字节不得冒充已限制在 128 MiB。
- 非视频 SDL audio/gamecontroller/events/TTF 与 deko 共存,链接/constructor 是否隐式触发竞争;必要时固定非视频 SDL 构建配置。
- HOME/suspend/恢复/退出时 NWindow acquire 返回性;不能把无 timeout 的 API 写成非阻塞。
- NVDEC syncpt_wait、submit timeout、数据 worker join 的关系;与 graphics fence/queue 销毁分别验证。不能以主线程还在 pump 证明 decoder 能退出。
- SDK fatal/不可恢复错误的边界;未知 DMA 状态不能强制 free 或返回 loader。
验收:源码保证、包清单、构建与实机观察分别留证。未取得保证则保留实验路径。
不默认制造 GPU hang。设备固定 10.10.10.17,使用 nxlink,不 TCP probe 28280,不自动推送。
P2 — desktop HAL 与应用控制边界(2–3 人日)
文件:platform/gfx.h、common/gfx_backend.h、desktop/gfx.c;application/runtime/media/ui_renderer/native tests;artwork 的停止接口。
实现:application 拥有 gfx;移除 sl_media_renderer 和业务 native renderer 泄漏。runtime/artwork 提供 request_stop/request_exit、持久完成快照和最终 join/destroy;主线程在停止期间继续 pump/collect。UI/事件层只提供准入条件,移除各处覆写最终 gate 的调用。视频首帧/停滞 watchdog 迁到 application,保留原协议/launch/activity 语义。
验收:保持 SDL 后端运行的 desktop 全部既有 UI/input/audio/rumble/shortcut/readback 测试;诊断 ON/OFF;用户请求在停止期间替换/取消、首次呈现事件不依赖诊断采样;无主线程提前 blocking join。
P3 — 视频租用、退役域与 NVTEGRA 导入(3–5 人日)
文件:common/video_pipeline.c/.h、frame_lifetime、switch/gfx.c、switch/video_surface.c/.h、YUV shader、media facade、runtime 回收逻辑与 CMake。
实现:
- 硬件帧 move_ref 到最新帧 mailbox,停止 prepare_vic/transfer。FrameLease 不含 session/Dk 裸对象;renderer 通过 GpuBatch 使用它。
- 回调 stop 冻结 domain、不等待主线程;runtime 在 callback 与外部 lease/映射全部结束后销毁 codec。不得把 IHS_SessionThreadedJoin 误当所有 data worker 已结束。
- 用户新会话串行准入;同 session 重启允许一个活动/一个退役 domain。start 失败自回滚,第三 domain 的失败按 session 断开处理并准确提示。
- 导入检查对齐/尺寸/偏移/pitch/溢出;禁止 ZeroFillInit。按实际 pool 分组缓存与整组退役;全 renderer 最多两组/每组 32 map。预算覆盖延后回收资源,未知 decoder 隐含内存另计。
- acquire 前预留命令/descriptor/引用表与 emergency clear;导入和 glyph miss 可失败但必须合法收尾。提交后 present 失败仍保持资源。
- 同 map 新 frame content 做 cache 可见性处理;软件 staging 和保留 current 纹理分别管理,不覆写在途或 current 像素。
验收:无新帧仍重绘;unique_present 不随 redraw 增长;NVTEGRA transfer/upload=0;稳态无反复映射/整帧堆分配;连续 pool 替换、容量拒绝和失败回滚不持续保留旧 pool;初始化/提交逐阶段失败满足析构计数。
P4 — 完整 UI 与平台事件(3–5 人日)
文件:ui_renderer、switch gfx/shaders、platform/events.h、platforms/{desktop,switch}/events.c、system/input adapter。
实现:基本形状与相邻兼容批处理、三字体/512 glyph LRU、封面、透明弹窗离屏组、截图。UI 只使用逻辑纹理句柄,Recording/current/submission 所用 atlas 子区版本均由 renderer 保护。超容量使用常驻占位,不新建无限 atlas。
单一 applet lifecycle 入口;原生 touch/实际使用的 keyboard/mouse 与 SDL 手柄合流;先处理 lifecycle/故障条件,再按统一准入转发输入。保留 PIN/IP 控件、音频、HID 协议、8ms flush/rumble;首版 1280×720。
验收:全部页面、中文/英文、缺字、同轮超 LRU 容量、封面切换、弹窗 alpha、裁剪/触摸命中、键鼠、手柄热插拔、HOME/恢复后的中立状态。无新视频仍有菜单动画;renderer 预算初值 96 MiB(UI 64 MiB)。
P5 — 真实停止/重启/退出与故障集成(2–4 人日)
文件:application/app_lifecycle、runtime/artwork、video_pipeline、renderer/events/system,生命周期集成测试。
实现:按规范 §10 的统一阶段推进,不再为每个错误单独实现 cleanup;普通 stop 只等该视频使用结束,exit 才停止全部绘制。协议 session 可在自己的使用者结束后销毁,冻结 codec/backing 保留到 GPU 使用结束。producer_closed 来自真实 data worker 完成点,完成事实不可被 UI 队列拥塞丢失。
device/queue 故障 application-wide;视频内容错误 session/epoch scoped;失败后的准入不可被 UI 覆盖。退出不等待还能被主线程工作解除的 join。应用超时不伪装成 SDK 取消能力。
验收:菜单无视频时 device 故障;旧 epoch 提交导致 queue 故障;首帧前取消;同 session channel 重启;start 失败无 stop callback;session destroy 前后分别完成 GPU;停止期间多次新请求/取消/退出;artwork I/O 收尾;睡眠超过原 watchdog 阈值;正常 hbmenu/title takeover/HOME forwarder/NSP 退出。
第二次启动测试按 AGENTS.md 先说明证据价值再安排,不以 nxlink 返回代替用户屏幕观察;未做时记录部分证据。
P6 — 性能、构建与默认切换(2–3 人日)
文件:scripts/setup-switch-deps.sh、app/CMakeLists、.github/workflows/build.yml、BuildIdentity/validate-artifacts 与 shader 规则。
实现/验收:固定 deko3d/uam,host uam 编译并嵌入 DKSH,检查缺失/陈旧 shader;desktop 不依赖 Switch SDK。SDL/deko 独立 build dir,产物只有一个图形 owner,旧 SDL NRO 作为整体回滚。
desktop 全测试/诊断 ON/OFF、Switch 两配置构建、NRO/现有 NSP 包装与资产校验。相同场景预热 10s、采样≥120s,记录版本/包清单、CPU、帧时间 median/p95/p99、输入 pump 间隔与完整内存高水位。transfer/import/software upload/acquire/record/present/fence 观察分别计时;不把 CPU 观察称纯 GPU 时间。
G0–G5 相关安全与功能验收通过才切正式默认值;这项任务不授权直接创建 release。
审计问题与当前闭合方式
以下“设计覆盖”不等于代码实现或测试通过;所有实施包仍未完成。
| 审计问题 |
修订后的处理 |
必须留下的实施证据 |
| R1 完成回报不可当日志丢、录制资源保护、acquire 后失败 |
ticket 发布;统一 GpuBatch;预留 emergency 收尾 |
P0/P3/P4 的引用计数和报文 fixture |
| R2-1 迟到回报覆盖统计 ring、cursor 回退 |
FrameTracker 统一身份/终态/过期/结算,移除通用 runtime outbox |
P0:128 槽反例、乱序/回绕/长期无显示的实际报文;通过前不接 P3 |
| R2-2 缓存保留整个旧 pool |
全局两组 PoolGroup、整组退役、冻结 codec 最后销毁;区分映射预算与完整 pool 内存 |
P1/P3:实际包的分配高水位/context 析构/内存回落 |
| R3 device 故障作用域、输入 gate 重开 |
application-wide DeviceFailed、唯一准入条件 |
P0/P5:无视频菜单、旧提交、下一轮 UI 仍 streaming |
| R3 start busy 实际断开、失败不调用 stop |
用户 session 串行 admission;同 session 重启容量失败明确断开;start 自回滚 |
P3/P5:失败注入与实际 IHS 分支 |
| NVDEC / acquire / SDK 错误退出 |
分阶段停止只消除应用互等,底层取消/返回保证单独列 G1 |
P1:实际包与 SDK 证据;未证实不得称安全 loader return |
旧“outbox 由 runtime 消费、每代 fatal、先主线程 join 再 GPU drain、默认 codec 提前 free”的方案已被本次架构修订替代,不应继续按旧工作包实现。
工作量与风险预算
按修订范围暂估 17–29 人日,另留 3–5 人日处理固定 SDK/非视频 SDL 配置差异。统计存储和主循环停止是明确的新增结构工作,不能沿用旧 15–25 人日估算,也不能认为分支减少就一定更快交付。
P0 的 FrameTracker 兼容性与 P1 的等待/内存证据取得后复核估算;如果需要改 deko/FFmpeg 底层取消或分配接口,另行估算,不用预留预算承诺未知驱动工程。
2026-09-09:本次仅更新架构规范与任务拆分;没有实现运行时代码,没有真机验证,也未推送 NRO/创建 release。
实现已合并并验收(2026-09-10)
PR #30 合并了 NVTEGRA 直接导入、deko UI 后端、帧/GPU 资源生命周期及真实输入驱动的在线自适应选帧。用户实际游玩后确认当前实现达到可接受状态,授权提交和合并。
NSL_DIAGNOSTICS中,默认 OFF;产品自适应输入/反馈不依赖诊断开关。本单按实现主体完成关闭;不是发布 Release 或全部平台边界已验收的声明。
历史规划(以下“尚未实现”等状态为合并前记录,以以上结论及 #29 为准)
目标与规范
Switch 的视频、UI、窗口/swapchain 统一使用 deko3d C API,直接采样 NVTEGRA 硬件帧,消除 VIC/CPU 整帧中转和 SDL 视频上传。desktop 保留 SDL,共用业务状态与绘制语义。首版 H.264、8-bit、720p60;保留音频、HID、完整 UI 与同一后端的软件帧上传。
设计证据基线:
a6f743465817913a965ca075276c1f231420cc98。规范:工作区
docs/VIDEO_RENDERING_DESIGN.md(架构修订版)、docs/decisions.mdD-051;职责同步于 DEVELOPMENT/UI_UX_DESIGN。文件仍在本地工作区,未作为远程代码提交发布。当前 Issue 是实施任务、审计状态和测量结果的唯一入口。旧 #10 测量中的 decode 已包含 transfer,不能重复求和。FPS 保持 60 不代表无收益;Present 返回不代表物理扫描完成。硬件直显的 transfer/upload 必须为 0,同时验证总体 CPU、帧时间和资源使用。
修订后的架构约束
前置与实施依赖
依赖:P0 → P2/P3;P1 → P3/P4;P2 → P3/P4;P3+P4 → P5 → P6。
P0 可用 desktop 假 GPU 与真实 IHS stats 实现验证,P1 可独立使用本地 fixture;不要求先完整迁移 UI 才发现所有权问题。
G0 的统计语义与停止条件、G1 的 SDK 保证未通过,不进入正式默认后端切换。
P0 — 控制/资源契约与 FrameTracker(3–5 人日)
文件:新增 common/app_lifecycle.c/.h、frame_lifetime.c/.h 与测试;修改 ihslib 的 session 回报接口、frame_stats.c/.h、callbacks.c、video 接收/统计发送接入点和 tests/session/test_frame_stats.c。
实现:
验收:
P1 — SDK/直显隔离验证(2–4 人日;底层兼容问题另估)
文件:tools/switch-video-surface-probe、合法本地 H.264/彩条 fixture、对应 CMake;从 probe 阶段接入固定 deko3d/uam 与可复现 shader 编译。
必须取得的证据:
验收:源码保证、包清单、构建与实机观察分别留证。未取得保证则保留实验路径。
不默认制造 GPU hang。设备固定 10.10.10.17,使用 nxlink,不 TCP probe 28280,不自动推送。
P2 — desktop HAL 与应用控制边界(2–3 人日)
文件:platform/gfx.h、common/gfx_backend.h、desktop/gfx.c;application/runtime/media/ui_renderer/native tests;artwork 的停止接口。
实现:application 拥有 gfx;移除 sl_media_renderer 和业务 native renderer 泄漏。runtime/artwork 提供 request_stop/request_exit、持久完成快照和最终 join/destroy;主线程在停止期间继续 pump/collect。UI/事件层只提供准入条件,移除各处覆写最终 gate 的调用。视频首帧/停滞 watchdog 迁到 application,保留原协议/launch/activity 语义。
验收:保持 SDL 后端运行的 desktop 全部既有 UI/input/audio/rumble/shortcut/readback 测试;诊断 ON/OFF;用户请求在停止期间替换/取消、首次呈现事件不依赖诊断采样;无主线程提前 blocking join。
P3 — 视频租用、退役域与 NVTEGRA 导入(3–5 人日)
文件:common/video_pipeline.c/.h、frame_lifetime、switch/gfx.c、switch/video_surface.c/.h、YUV shader、media facade、runtime 回收逻辑与 CMake。
实现:
验收:无新帧仍重绘;unique_present 不随 redraw 增长;NVTEGRA transfer/upload=0;稳态无反复映射/整帧堆分配;连续 pool 替换、容量拒绝和失败回滚不持续保留旧 pool;初始化/提交逐阶段失败满足析构计数。
P4 — 完整 UI 与平台事件(3–5 人日)
文件:ui_renderer、switch gfx/shaders、platform/events.h、platforms/{desktop,switch}/events.c、system/input adapter。
实现:基本形状与相邻兼容批处理、三字体/512 glyph LRU、封面、透明弹窗离屏组、截图。UI 只使用逻辑纹理句柄,Recording/current/submission 所用 atlas 子区版本均由 renderer 保护。超容量使用常驻占位,不新建无限 atlas。
单一 applet lifecycle 入口;原生 touch/实际使用的 keyboard/mouse 与 SDL 手柄合流;先处理 lifecycle/故障条件,再按统一准入转发输入。保留 PIN/IP 控件、音频、HID 协议、8ms flush/rumble;首版 1280×720。
验收:全部页面、中文/英文、缺字、同轮超 LRU 容量、封面切换、弹窗 alpha、裁剪/触摸命中、键鼠、手柄热插拔、HOME/恢复后的中立状态。无新视频仍有菜单动画;renderer 预算初值 96 MiB(UI 64 MiB)。
P5 — 真实停止/重启/退出与故障集成(2–4 人日)
文件:application/app_lifecycle、runtime/artwork、video_pipeline、renderer/events/system,生命周期集成测试。
实现:按规范 §10 的统一阶段推进,不再为每个错误单独实现 cleanup;普通 stop 只等该视频使用结束,exit 才停止全部绘制。协议 session 可在自己的使用者结束后销毁,冻结 codec/backing 保留到 GPU 使用结束。producer_closed 来自真实 data worker 完成点,完成事实不可被 UI 队列拥塞丢失。
device/queue 故障 application-wide;视频内容错误 session/epoch scoped;失败后的准入不可被 UI 覆盖。退出不等待还能被主线程工作解除的 join。应用超时不伪装成 SDK 取消能力。
验收:菜单无视频时 device 故障;旧 epoch 提交导致 queue 故障;首帧前取消;同 session channel 重启;start 失败无 stop callback;session destroy 前后分别完成 GPU;停止期间多次新请求/取消/退出;artwork I/O 收尾;睡眠超过原 watchdog 阈值;正常 hbmenu/title takeover/HOME forwarder/NSP 退出。
第二次启动测试按 AGENTS.md 先说明证据价值再安排,不以 nxlink 返回代替用户屏幕观察;未做时记录部分证据。
P6 — 性能、构建与默认切换(2–3 人日)
文件:scripts/setup-switch-deps.sh、app/CMakeLists、.github/workflows/build.yml、BuildIdentity/validate-artifacts 与 shader 规则。
实现/验收:固定 deko3d/uam,host uam 编译并嵌入 DKSH,检查缺失/陈旧 shader;desktop 不依赖 Switch SDK。SDL/deko 独立 build dir,产物只有一个图形 owner,旧 SDL NRO 作为整体回滚。
desktop 全测试/诊断 ON/OFF、Switch 两配置构建、NRO/现有 NSP 包装与资产校验。相同场景预热 10s、采样≥120s,记录版本/包清单、CPU、帧时间 median/p95/p99、输入 pump 间隔与完整内存高水位。transfer/import/software upload/acquire/record/present/fence 观察分别计时;不把 CPU 观察称纯 GPU 时间。
G0–G5 相关安全与功能验收通过才切正式默认值;这项任务不授权直接创建 release。
审计问题与当前闭合方式
以下“设计覆盖”不等于代码实现或测试通过;所有实施包仍未完成。
旧“outbox 由 runtime 消费、每代 fatal、先主线程 join 再 GPU drain、默认 codec 提前 free”的方案已被本次架构修订替代,不应继续按旧工作包实现。
工作量与风险预算
按修订范围暂估 17–29 人日,另留 3–5 人日处理固定 SDK/非视频 SDL 配置差异。统计存储和主循环停止是明确的新增结构工作,不能沿用旧 15–25 人日估算,也不能认为分支减少就一定更快交付。
P0 的 FrameTracker 兼容性与 P1 的等待/内存证据取得后复核估算;如果需要改 deko/FFmpeg 底层取消或分配接口,另行估算,不用预留预算承诺未知驱动工程。
2026-09-09:本次仅更新架构规范与任务拆分;没有实现运行时代码,没有真机验证,也未推送 NRO/创建 release。