文档:归档历史记录并同步云管与终端验收资料
276
MEMORY.md
@ -1,273 +1,11 @@
|
||||
# 继续开发前必读
|
||||
|
||||
## 2026-11-05 Ubuntu 深度响应调优(最新)
|
||||
- Ubuntu VM102 已在正常会话中启用 KMS 捕获 + 原生独立光标:virtio_gpu atomic 唯一 primary plane,2K geometry,cursor hotspot plane 不暴露;盒子客户端 SHA 未变。KMS 守护按 `CRTC_W/H=2560x1440` 校验,不能依赖 virtio atomic 客户端的 legacy `crtc_id/fb_id=0`。
|
||||
- KMS 45FPS 与 X11 45FPS 长按框选回显 P50/P95=195.9/246.6ms vs 250.1/311.5ms;更新约16.0/s vs10.6/s。28次原生光标点击、7种形状/热点/隐显通过,像素差0;正常退出、空闲重连、guard强杀恢复、再次重连通过。证据 `docs/evidence/ubuntu-deep-latency-20261105/`,报告 `docs/ubuntu-deep-latency-20261105.md`。
|
||||
- 真实GTK按钮/ masked entry 定量仅为当前应用内回显,不能代表真实后端认证;KMS按钮约304/378ms、字符约165/182ms。硬编和 `min_threads=4` 均失败/无收益并已撤回;Ubuntu仍软件x264,未达到Windows约90ms或零延迟。
|
||||
- 盒子 Ubuntu 卡片暂保持45FPS请求用于当前联调,2K/30Mbps;若收尾要回30FPS,先正常退出并改仅 Ubuntu 对象。当前现场应保持 `vdi-sunshine.service capture=kms` 与 guard active;检查时不要用 `fb_id/crtc_id` 作为活动判据。
|
||||
更新:2026-11-09。状态与计划统一维护在主题文档,本文件只保留导航,避免多处历史“最新”描述相互矛盾。
|
||||
|
||||
完整对话总结在:`D:\DAIMA\vdi-cloud\PROJECT_CONTEXT.md`
|
||||
1. 阅读[项目上下文](PROJECT_CONTEXT.md)与[当前状态](docs/current-status.md),再查看 `git status`,保留已有改动。
|
||||
2. 终端进出窗口、显示守护和当前客户端 SHA 见[终端说明](docs/terminal-guide.md);Linux 光标升级/回退必须匹配二进制与 profile SHA。
|
||||
3. Linux 硬编仍未打通;Intel 直通与显存 64MB 试验已回退。合成回显不等于物理端到端延迟,详见[云桌面说明](docs/desktop-guide.md)。
|
||||
4. 日常监控在独立 `D:\DAIMA\vdi-monitor`;开发证据、失败记录与回退仍保留在本仓库,见[运维说明](docs/operations.md)。
|
||||
5. VPN/Clash、软件白名单与品牌兜底画面只列[未来计划](docs/roadmap.md),目前不实施。
|
||||
|
||||
## 2026-11-05 Ubuntu框选/渲染后续(优先于旧VM102硬件描述)
|
||||
- 后续用户要求清理锁屏Sunshine卡片:已确认是Stream Started/Paused通知,非多份安装。Ubuntu sunshine.conf新增system_tray=disabled(同时移除托盘图标),正常退出/入口重连后新进程日志确认生效;仅当前4条通知均为Sunshine时,通过GNOME通知面板清除并确认0剩余。原配置备份~/vdi-terminal/backups/sunshine-notifications-20261105/sunshine.conf;不改全局通知或锁屏策略,串流/guard active,退回普通桌面。下方“Sunshine配置与原配置一致”仅指渲染调优结束时,现多此一项。
|
||||
- 用户要求先只处理Ubuntu,已明确允许正常关闭启动、A/B及必要回退。本轮VM102正常关闭启动3次,最终4核/6144MB、cpu=host、vga=virtio-gl;宿主Intel i915共享渲染,Windows/麒麟/独占NVIDIA配置未动。宿主仅补GL/EGL/Mesa依赖,0升级10新增0移除。
|
||||
- Xorg确认virgl Intel HD4600/glamor生效,x264确认AVX2/FMA3/BMI2。仍X11捕获/libx264软件编码,renderD128不代表硬件编码。最终2K30/30Mbps/limited、默认线程;45FPS及min_threads=4试验无明显延迟收益,已撤回。盒子desktops JSON结构与本轮备份完全相同,客户端SHA仍5d4330…。
|
||||
- 受控60Hz长按框选:基线回显P50/P95=398.3/634.6ms,最终224.4/308.3ms;独立编号更新2.0→12.4/s,不等于总视频FPS。有效轮次最终编号300及释放均见;中位改善约44%,短样本未随机重复,非USB/光子延迟,尚未达到Windows/原生目标。
|
||||
- 回归16编号点击+2快速点击,源18按/18放,最大坐标误差1.375px;2K及12灰阶核对通过。临时测试服务已停,最后留Ubuntu普通桌面,Sunshine/guard active。不要自动进一步改麒麟。
|
||||
- 备份PVE /root/vdi-ubuntu-render-20261105/102.conf.before,Ubuntu/盒子各自~/vdi-terminal/backups/ubuntu-render-20261105/。回退只还原cpu/删除vga字段,需正常关闭启动;别整份覆盖VM配置。
|
||||
- 报告docs/ubuntu-drag-render-20261105.md,原始证据docs/evidence/ubuntu-drag-20261105/。剩余瓶颈为捕获/GPU读回/软件编码;未部署硬编方案或更换协议。
|
||||
|
||||
## 2026-11-05 Linux原生独立光标(优先于下方旧样式/原型状态)
|
||||
- 用户要求保留原生样式并直接实施低延迟方案;Ubuntu恢复Adwaita/24、default加速,关闭动画保留。两Linux真实2560×1440/30FPS请求/30Mbps/limited/软件编码保留。
|
||||
- 已部署0005 + terminal/native-cursor:受限独立SSH密钥/固定主机公钥同步XFixes真实形状、热点、显隐,本地X11指针移动不等待视频。单座席X11/2K/绝对鼠标,最多128×128;状态2秒过期或源几何改变则结束串流恢复原服务,不做运行中帧交接。
|
||||
- 私有原版Sunshine精确SHA + 进程级adapter只清除捕获副本像素。Ubuntu原/usr/bin/sunshine的capabilities保留,租约内用同SHA无cap副本;麒麟仅host.py/私有sunshine/.so认证KYSEC。guard按需启动,ExecStopPost恢复;原服务永久文件不改。
|
||||
- 盒子最终客户端SHA 5d4330f703d7f8e053bc3795ca42bdaf231c9e9dd8ebb00a612013adbcccee03,.71/.72 profile固定该值;helper在远端租约前核验,旧备份实测拒绝且原服务PID不变。修改二进制须停用profile并重新验证。备份~/vdi-terminal/backups/native-cursor-20261105/;回退先退出等恢复,再停用两JSON。
|
||||
- 最终版两系统七种光标形状/热点一致、静态像素最大差0、视频无混入指针;各28编号点击与源事件完全匹配,Ubuntu误差最大1.055px、麒麟0.829px。较早各16次40ms短按和双击补测通过;最终版暂停Moonlight指针仍移动,较早服务器主动warp(400,600)两台读回正确。
|
||||
- 最终页面视频回显Ubuntu P50/P95 188.38/522.42ms,麒麟134.37/165.56ms;不是本地指针或物理USB延迟,未定量证明Windows等效。Ubuntu两次强制暂停恢复后首击未回显,根因未定;完整SDL失焦未验收,不可称全部场景无丢输入。
|
||||
- agent/guard SIGKILL恢复、Ubuntu bridge冻结退出、麒麟真实分辨率变化退出及恢复2K重连均已测。4项单测和清洁patch应用通过。构建器会规范CRLF但严格上下文;现场本轮未重建部署其他任务0004,不能据构建器宣称现场包含0004。
|
||||
- 普通Ubuntu初次收尾约4分42秒触发stale/mismatched退出,发现校验时间在读取前取得的原子替换竞态,改为读后取时间。该轮未区分未来/实际过期,不能认定唯一根因。仅修时序版先6分钟通过,之后9分32秒真实heartbeat超时;最终状态等待上限改2秒、主机TTL4秒,增加X11读耗时诊断。已测暂停Ubuntu代理1.1秒时指针继续移动且会话存活。旧1da552…形状/点击证据保留;最终版持续运行另记。
|
||||
- Ubuntu原Sunshine在11:12:07恢复测试阶段停止超过10秒、看门狗SIGTRAP,11:12:14恢复;未修复上游停止看门狗问题,原crash报告保留且未外发。
|
||||
- 最终2秒策略Ubuntu6分钟/65960次状态读取存活,含1.1秒暂停;读前时间出现1次合法未来值、读后0次,证实竞态检查顺序需要修正。随后同会话七形状/28点击通过;bridge冻结2.6秒后2.904秒检查客户端退出,原服务恢复。详见ubuntu-final-soak/release-probe/final-stale-state等最终证据。
|
||||
- 最终新SHA的Windows2K45/RKMPP/EGL无独立光标env回归;麒麟2K30七形状/28点击及普通桌面重连后退出,原服务active且无临时drop-in。Ubuntu保留2K30/原生指针PID299860/父235482,guard按设计active,临时GTK源/盒子探针均已停。报告docs/linux-native-cursor-20261105.md,证据docs/evidence/native-cursor-20261105/。
|
||||
|
||||
## 2026-11-05 Ubuntu画质与鼠标样式调优
|
||||
- Ubuntu .72/test2 已由1080p升级真实2560×1440,盒子只改Ubuntu对象为2K30,自动请求30Mbps。原VM虚拟VGA在当前内核可直接切2K,未改PVE硬件或重启VM/GDM。
|
||||
- 用户服务启动前newmode/addmode/output创建2K;删模式后服务启动重建通过。GNOME monitors.xml保存单屏2K/100%缩放,最终锁屏/解锁仍2K;整机重启恢复未验收。DisplayConfig持久调用曾待确认后回退,已重新保存显示配置并重新核验最终尺寸。
|
||||
- DMZ-White/24px、flat鼠标加速、关闭GNOME动画;不是复制Windows整套主题,也没有叠加本地假箭头。无GPU render节点,仍X11/libx264 ultrafast/zerolatency默认线程。
|
||||
- .72 host-color-range=0,正常kiosk最终PID240476/父235482,2K30/30Mbps、RKMPP/EGL、服务端MPEG和真实源2K核对;最终保留Ubuntu普通桌面连接,临时GTK服务已停止。
|
||||
- 正常入口2K短测:45请求full21.49FPS/F9 P50 373.2ms;limited22.83/376.7ms;单线程+无动画17.98/402.9ms(单线程已撤回);最终30请求limited/默认线程/无动画21.40/320.9ms。F9每轮24翻转无超时,但一轮客体有额外F9事件且无序号,不能证明逐键延迟。四轮启动快速点击只记录24/24/23/21且单线程有坐标重复,不能说全部通过;补做稳定正常会话24次慢速交替点击24/24、误差<1px、F8收到,见final-input-check.json。源GTK本身约15–20绘制/秒,视频含重复帧;不能声称独立运动21–23帧、稳定30/45或物理延迟降低/Windows等效。
|
||||
- limited灰阶保持黑白层次,未显著截断;单像素/彩字仍受4:2:0限制。两端备份~/vdi-terminal/backups/ubuntu-quality-20261105/,Ubuntu monitors.xml原不存在;.72 range原不存在。原Sunshine配置/服务、gsettings原值均保留。测试仅补装python3-gi-cairo,无整体升级。
|
||||
- 文档docs/ubuntu-quality-20261105.md、证据docs/evidence/ubuntu-quality-20261105/。Windows/麒麟卡片结构比较未变,未改客户端二进制、全局渲染或USB任务修改。
|
||||
|
||||
## 2026-11-05 Windows固定PCI设备不再可弹出
|
||||
- 用户要求日常体验像普通电脑、托盘只留真实U盘,授权“你自己保存然后重启”。交互会话仅设置/系统窗口,无打开文档;未强制关闭应用。
|
||||
- 09:28正常shutdown.exe /s /t 0后冷启动VM100。原args保留GPU子厂商设置,追加三个global:ICH9-LPC.acpi-pci-hotplug-with-bridge-support=off、pci-bridge.shpc=off、pcie-root-port.hotplug=off。PCI增删须关机,USB设备级热插拔保留;没改总线/设备地址/驱动,没有重启USB隧道。
|
||||
- 原托盘四项(GTX965M/VirtIO网卡/串口/附加AHCI)Capabilities从6变0,InstanceId相同,Status OK;GPU错误0,系统盘Online/Healthy,IP .70、Sunshine及Guest Agent正常。
|
||||
- PVE备份/工具/root/vdi-fixed-pci-20261105/(backup/plan.json、vm.conf.before),set-fixed-pci.py支持prepare/apply/restore且拒绝在线变更。4项单测和无磁盘/网络后端/直通GPU的隔离QEMU测试通过(PCI关闭、usb-redir添加/删除)。
|
||||
- 盒子无物理U盘,仅键鼠,USB服务active;Moonlight随关机退出,用户须重连Windows。托盘UIA未暴露传统图标子项,外观与真实U盘插拔待用户验收,不冒充目视完成。文档docs/fixed-pci-tray-20261105.md及evidence/fixed-pci-20261105。
|
||||
|
||||
## 2026-11-04 云桌面图形关机/重启按钮限制
|
||||
- 用户明确要求Windows/Ubuntu/麒麟桌面内图形关机、重启按钮也禁用,前面的快捷键补丁不足。本轮已通过PVE Guest Agent实际部署,不依赖未构建的Moonlight 0004。
|
||||
- Windows VM100/test HKU Explorer NoClose=1,交互用户任务广播Policy并SHRestricted(2)=1;无Explorer重启/注销/VM重启,任务已删除。首次原值备份C:\ProgramData\VDI\power-policy\original.clixml。
|
||||
- 麒麟VM101/test1使用polkit .pkla,Ubuntu VM102/test2使用.rules。两台均额外部署/etc/dbus-1/system.d/zz-vdi-desktop-power.conf,按客户UID拒绝login1电源方法和Can*查询,只ReloadConfig。root CanPowerOff仍yes。
|
||||
- 两台实际图形进程pkcheck基础reboot仍授权(内部原因未确定),所以不能只靠polkit宣称已屏蔽。D-Bus错误签名安全探测八项全部Access denied,七项Can*拒绝;并未发送有效关机请求。麒麟定位时仅重启过polkit一次,Sunshine保持active。
|
||||
- 三台running,Windows LastBoot及Linux boot ID与部署前一致;未逐项目视验收菜单外观、未重启验收。限制当前客户账户图形电源入口,不防管理员命令/改策略,不涉及注销/锁屏,不实现盒子开关联动VM生命周期。新用户需重新针对账户应用。
|
||||
- 工具tools/deploy-guest-power-policy.py apply/query/restore --vm 100或--vm 101 --user test1或--vm 102 --user test2;Linux首次备份/var/lib/vdi/power-policy。详见docs/guest-power-policy-20261104.md及evidence/guest-power-policy-20261104。
|
||||
|
||||
## 2026-11-04 盒子过夜重启观察(最新)
|
||||
- 用户要求今晚开机观察并记录重启/原因。19:35启用vdi-overnight-observer.service,active/enabled,每60秒fsync心跳、boot ID/uptime计数、温度/负载/内存/网络/进程/桌面几何;跨午夜分日志。当前计数0是启用后范围,不代表历史没重启。
|
||||
- 中文汇总~/vdi-terminal/logs/overnight-summary.txt;JSON行overnight-health-YYYYMMDD.log;持久状态~/vdi-terminal/state/overnight-state.json。同boot的服务重启不计为整机重启;11项状态/证据/跨日测试通过,实际分钟样本和服务重启状态保持已验证,未实际重启盒子。
|
||||
- /var/log是zram,journal链接到eMMC /var/log.hdd/journal。新增70-vdi-observer-journal.conf配置persistent/64MB/7day/30s,flush/sync确认磁盘journal;只重启journald和本观察服务,不动Moonlight/显示/网络/GPU。
|
||||
- root拥有/usr/local/libexec/vdi-observer-pstore只读采集pstore到/var/lib/vdi-observer/pstore.json(root:systemd-journal 640),观察服务低权限读取。旧归档41字节reboot行仅基线;仅内容变化作为新线索。无法确认的断电/硬复位明确标原因未确认,不能伪造重启因果。
|
||||
- 最后Windows串流PID137139、2K45和显示守护保留,boot ID afb6a02f-ba7c-478a-8704-c78a993d23c6。文档docs/overnight-box-observation-20261104.md及evidence/overnight-observer-20261104;备份~/vdi-terminal/backups/overnight-observer-20261104-*。明早读取两天日志和汇总,无明早自动通知任务。
|
||||
|
||||
## 2026-11-04 麒麟鼠标掉帧继续排查(最新)
|
||||
- 保留原版Sunshine SHA256 1c3099bf924db3aee759b3d7c05e012569ef8650c400d1479144398795decc71、单线程、暂停合成器、真实2K30/30Mbps。未改PVE或其他桌面。
|
||||
- wrapper支持~/.config/vdi-terminal/host-color-range/<host>的0/1协商;仅.71设0,消除软件full-range额外转换。同场景2K45请求:原full21.42FPS,limited28.81FPS;不代表真实45或物理延迟与Windows一致。
|
||||
- 两线程25.67、BGRA27.17、full-chroma25.61、bilinear30.79均未保留。精确SHA校验后仅创建过独立实验副本,现已删除三副本及服务drop-in、恢复原程序,无额外capability。
|
||||
- wrapper已部署,语法/三主机隔离/显式环境覆盖/非法配置拒绝检查通过。用户明确允许退出Windows切回麒麟后,已完成正常kiosk重连:PID135145/父kiosk92843,真实2K30/30Mbps,env颜色0,RKMPP/EGL和服务端MPEG日志确认;最终保留麒麟正常桌面连接。
|
||||
- 完整同参数2K30对照各24次合成F9回显,无超时:full23.35FPS、P50 150.99/P95 186.38ms;limited27.25FPS、P50 143.57/P95 173.03ms。分布重叠且网络有差异,不能称显著低延迟或Windows等效;排除物理USB及面板扫描。临时GTK和测试串流均已停。
|
||||
- 文档docs/kylin-mouse-20261104.md,证据docs/evidence/kylin-mouse-20261104/,两端备份~/vdi-terminal/backups/kylin-mouse-20261104/。
|
||||
|
||||
## 2026-11-04 麒麟真实2K已落地(最新)
|
||||
- 用户明确授权关闭并启动VM101;已正常关机、只改vga=std,memory=64并启动,PCI BAR确认64MB;配置diff仅vga,未动其他VM或宿主。
|
||||
- 麒麟Virtual-1和视频真实2560x1440;自定义241.50MHz/59.95Hz模式,Sunshine用户服务启动前自动创建/添加/切换。删模式后启动服务验证通过,未第二次整机重启验收新配置。
|
||||
- 盒子只改麒麟对象为2560x1440/30FPS,沿用2K自动30Mbps;Windows、Ubuntu对象不变,源码/客户端二进制未改。
|
||||
- 最终X11/libx264 ultrafast/zerolatency原单线程;Sunshine服务启动暂停UKUI合成、停止恢复。有效2K局部运动:开合成18.89FPS、两线程18.85、暂停合成22.97(采用),队列0.12ms。未测物理鼠标到屏幕延迟,不能称稳定30/45FPS。
|
||||
- 有效测试48/48跨屏点击误差<=1px,F8收到;正常kiosk连接/退出/重连2K验证,最终保持麒麟桌面。临时GTK/测试串流已停,未关闭长期锁屏。
|
||||
- 早期2K样本有锁屏污染,相关ultrafast/damage非valid文件排除性能结论。KMS探测失败恢复X11,最终入口及实体二进制getcap为空。
|
||||
- 备份两端~/vdi-terminal/backups/kylin-quality-20261104/;详情docs/kylin-quality-20261104.md,证据docs/evidence/kylin-quality-20261104/。此前接入文档的麒麟1080p为历史状态。
|
||||
|
||||
## 2026-11-04 Ubuntu / 麒麟已接入(最新)
|
||||
- 盒子192.168.1.188正常列表已可连接Ubuntu .72/test2、麒麟 .71/test1,两台安装并配置Sunshine,原Moonlight客户端保留。
|
||||
- Ubuntu24.04.4使用Sunshine2026.914.233613;麒麟V10 SP1/glibc2.31使用官方v0.21.0 Ubuntu20.04构建,/opt解包部署并单文件KYSEC认证,未关闭全局策略。
|
||||
- 用户明确授权Ubuntu test2自动登录+Xorg;GDM/AccountsService原配置备份/var/backups/vdi-linux-20261104-100722/;重启GDM后已生效,未整机重启验收。麒麟沿用已有图形会话,锁屏仅临时解锁。
|
||||
- 用户服务vdi-sunshine active/enabled,XDG自启导入会话环境;X11/libx264、Virtual-1 1920x1080、30FPS配置;固定虚拟音频vdi_audio,audio_sink填sink名而非.monitor,音质未验收。
|
||||
- 真实盒子RKMPP/EGL入口两台画面、F8及鼠标左键已验证;最后Ubuntu/麒麟/Windows依次连接回归通过并退回列表。Linux短测约29FPS,未验收2K/长期性能/重启恢复。
|
||||
- Windows原对象2560x1440/45FPS保留,未改源码默认地址。盒子配置备份~/vdi-terminal/backups/linux-desktops-20261104-101507/desktops.json。
|
||||
- 两台管理随机凭据只在各自~/.config/sunshine/vdi-admin.json(600),不要复制进仓库。临时输入/Xvfb测试已停。详见docs/linux-desktop-onboarding-20261104.md及evidence/linux-desktops-20261104。
|
||||
|
||||
## 2026-11-03 U 盘真实设备透传 POC
|
||||
- 用户明确要求 Windows 枚举真实 USB。已开发 Rust terminal/usb-monitor(标准库),usbredir+受限SSH隧道+PVE QMP usb-redir;盒子不调用PVE API。
|
||||
- Kingston 0951:1666 / USB序列号408D5C1653D4E44179789251,物理3-1(USB2/480M);Windows正常枚举USB Mass Storage / Kingston USBSTOR,KINGSTON F:、FAT32、Removable、Healthy。
|
||||
- 独立VDI_USB_TEST_20261103-173342目录新增1MiB文件并复制回VM,三份SHA一致;用户安全弹出后真实拔插,17:34:43断开、17:34:53重连,F:恢复且文件SHA一致。原文件未改,测试目录保留。
|
||||
- 仅固定VID/PID/serial纯存储,拒绝键鼠/复合设备/本地挂载/holder/swap;udev只禁止这只盘本地自动挂载。最初口报-71,换口后成功,不是软件修好了原口。
|
||||
- 盒子vdi-usb-monitor.service、PVE vdi-usb-tunnel-100.service active/enabled;QMP端点vdi_usb_box_100/vdi_usb_redir_100仅运行时,VM/PVE重启需管理员重新attach。未验收开机恢复/多用户生产能力。
|
||||
- 盒子备份~/vdi-terminal/backups/usb-device-20261103-172709;PVE密钥/etc/vdi-usb-poc/vm100,回环40001经SSH。未装Windows驱动、开测试签名、重启或改GPU/串流;Moonlight PID46934保留,GTX965M错误0。
|
||||
- 详见docs/usb-device-poc-20261103.md及evidence/usb-device-20261103;工具deploy-usb-device-poc.py、usb-device-qmp.py、test-usb-device-poc.ps1。
|
||||
|
||||
## 2026-11-03 Windows保留单个2K主屏(最新)
|
||||
- 用户明确授权:不影响2K才能改,否则不动。现场VDD已变DISPLAY4,原主屏2560×1440@60;Basic DISPLAY1另扩展2K64,NV DISPLAY2原本无活动输出。
|
||||
- 已只停用Basic桌面输出,VDD成为唯一活动主屏,保持2K60;未禁用/卸载任何显卡、未重启。原Moonlight PID46934/2K45继续,截图正常;GPU错误0,Sunshine重建h264_nvenc,编码10%。
|
||||
- 新工具set-single-vdi-display.ps1、run-single-vdi-display.py支持Query/Apply/Restore,有2K60主屏前置条件、XML布局备份、读回失败恢复、设备身份校验;临时任务已删除。备份C:\ProgramData\VDI\single-display-20261103\before.xml。
|
||||
- 布局已按当前用户保存,但未重启/重登验收;设备管理器仍有适配器,不能说卸载设备或保证设置界面不列未连接输出。详见docs/single-display-20261103.md及对应evidence。
|
||||
|
||||
## 2026-11-03 独立光标受控原型
|
||||
- 已实测Windows真实箭头/文字/左右缩放/忙碌静态形状导出和盒子X11本地显示,热点正确,隐藏全透明;短暂停Moonlight小于0.5秒,本地指针仍移动,已恢复SIGCONT。
|
||||
- 最后完整轮32/32跨区点击正确,归一化坐标最大差1px;10项边界测试通过,0.8秒辅助状态租约到期退出/SDL资源归还通过。未测物理输入到屏幕延迟。
|
||||
- 这是脚本形状回放和受控Sunshine全局N快捷键交接,不是自动形状同步,不是生产独立光标;没有替换正常Moonlight/Sunshine二进制。正式须改双方控制通道、帧边界ACK/租约与SDL生命周期,并适配Weston。
|
||||
- 首两轮辅助连接退出后XFixes图像不可用,修正为XFixes临时隐藏下成对切Moonlight本地C快捷键,让SDL重新设置其拥有的隐藏指针;第三轮完整通过。静止画面N切换旧箭头留旧帧也已实测,不能靠快捷键直接上线。
|
||||
- 已停删VDICursorPOC,正常PID31694/2K45/原SHA保留,VDD2K60、GPU错误0/NVENC编码11%、显示守护active;最后本地透明1×1,正常Windows单个白色远程箭头。未重启/改ROM/VFIO/主题。
|
||||
- 详见docs/independent-cursor-feasibility-20261103.md及对应evidence。新增cursor-poc工具仅实验,不自动启动;verify-cursor-poc-box.py点击仅可用于临时Windows测试窗口。
|
||||
|
||||
## 2026-11-03 GPU修复后联调与45帧落地
|
||||
- 用户确认运维修复结束,授权的鼠标优化任务已恢复。现场复核GTX965M错误码0、vBIOS84.04.2b.00.13、
|
||||
Sunshine实际h264_nvenc、NVENC会话1;正常重连后编码占用18%。本轮未改ROM/VFIO、未重启任何机器。
|
||||
- 修正的测试源实测约59.4~59.8FPS;2K30实渲染29.83FPS;2K60接收58.82但只渲染46.11,
|
||||
客户端等待队列7.54ms,GPU89~95%/800MHz。原长队列渲染仍46且排队36ms;nearest实验42;VSync24,均未采用。
|
||||
- 已落地Windows VDD2560x1440@60、正常kiosk票据45FPS;短测实渲染44.22/队列1.30ms,
|
||||
约两分半持续动态会话43.14/队列1.55ms/网络实际丢帧0。不能称稳定2K60或零延迟。
|
||||
- Windows活动VDD变为DISPLAY3,动态查找;登录脚本tools/set-2k-display.ps1默认60、支持RefreshRate30。
|
||||
已备份/部署并手动运行原VDISet2KDisplay任务,独立Query确认60Hz;未通过重启验收登录触发。
|
||||
Windows原脚本/任务XML:C:\ProgramData\VDI\backups\display-refresh-20261103-142920。
|
||||
- tools/set-terminal-fps.py支持30/45/60、原子写入和备份;当前30票据备份
|
||||
~/vdi-terminal/backups/fps-20261103-143333-297521/desktops.json。正常流程重新连接PID31694实际fps45。
|
||||
- 正常二进制仍35de7b...afa00f,RKMPP/DRM PRIME/EGL、最新帧队列、无VSync/无pacing,第三补丁不开。
|
||||
nearest实验保留terminal/moonlight/experiments/0004-vdi-egl-exact-size-sampling.patch,已撤回正常源码且不进构建器。
|
||||
- Windows临时基准任务已停止并删除,256次交替点击记录全部位置正确;用户原应用保留,恢复正常Windows串流。
|
||||
显示守护active,2560x1440输出正常。独立光标尚未实现;稳定2K60需继续验证更低开销呈现路径。
|
||||
- 详细对照、限制与回退见docs/2k60-validation-20261103.md,证据docs/evidence/2k60-20261103/。
|
||||
|
||||
## 2026-11-03 午后鼠标进一步优化(历史,已恢复联调)
|
||||
- 用户要求制定计划并执行;随后明确运维正在修复GPU Code43。Windows多次被外部qmshutdown/qmstart,
|
||||
本轮已停止Windows配置/串流测试,不能与运维并发改VM;没有发出VM/宿主/显卡重启指令。
|
||||
- 新增0003-vdi-input-priority.patch:帧前连续输入最多32条/2ms,保留焦点/窗口/退出等边界;
|
||||
VDI_INPUT_PRIORITY=1才启用;VDI_INPUT_TIMING=1才记录SDL派发年龄及主线程render调用统计。
|
||||
已在盒子编译,普通/ASAN测试通过。实际EGL使用独立PacerRender线程,主线程render样本0,未证明新调度收益。
|
||||
- 默认客户端已恢复本轮前哈希35de7b1a4ade583823b23b5f2e012307f6d0a3848c8eb97bd6d4de9660afa00f;
|
||||
候选另存moonlight.input-priority-20261103,哈希ef0707ed2053f15ad6f62a33a79ac360237b27f307777c79b379b105fabfe3a2。
|
||||
正常wrapper未改用候选,新调度未默认启用;没有添加本地/透明光标。
|
||||
- VDD30Hz已查明,临时2K60应用/读回成功;但旧WinForms基准源本身约32FPS,2K30/60初测不能证明性能提升。
|
||||
本地基准脚本已修正定时器量化,待运维结束再部署;VDD模式未持久化,原登录2K30脚本没改。
|
||||
- 新增set-vdi-display-mode.ps1、run-mouse-display-probe.py、bench-mouse-stream.sh和移动注入工具。
|
||||
现场测试串流已清理;Windows基准窗口随关机退出,VDIInputLatencyBench登记无自动触发器,
|
||||
运维结束后先用run-input-latency-bench.py stop清理,再Query VDD/必要时Restore,再做新版源配对测试。
|
||||
- 独立光标完成源码核实与交接设计,尚未实现;Sunshine全局toggle不足以可靠回退,需要双方协议/帧边界交接。
|
||||
计划/实际结果见docs/mouse-optimization-plan-20261103.md、docs/mouse-optimization-execution-20261103.md;
|
||||
后续设计见docs/independent-cursor-design-20261103.md。不能宣传本轮达到2K60或鼠标到屏幕1ms。
|
||||
|
||||
## 2026-11-03 显示器关开修复(优先于下文旧状态)
|
||||
- 关屏再开后画面变矮:盒子回退1920x1440@75(4:3),Moonlight 16:9视频上下黑边;
|
||||
MATE monitors.xml仍存旧4:3布局,原启动脚本只设置一次2K。
|
||||
- 已部署vdi-display-guard用户服务(active/enabled)和幂等启动恢复,保留297MHz定制2K时序;
|
||||
2秒检查、异常稳定2秒后恢复;断开不强设、失败退避、命令超时、单实例锁,正常不反复切换。
|
||||
修正当前GWR/0x2708显示器旧保存布局。仅部署tools/deploy_display_guard.sh,不重启串流。
|
||||
- 9个场景测试通过,CTest 3/3;两次主动回退均约14秒恢复,Moonlight PID66385和boot ID不变。
|
||||
用户真实关屏再开确认自动全屏;10:03:19断开、10:03:31正确2K模式,10:04现场2560x1440。
|
||||
- 备份~/vdi-terminal/backups/display-guard-20261103-095920/;详情/回退见
|
||||
docs/display-resume-fix-20261103.md。此修复仅限当前Xorg单HDMI 2K POC,未做隔夜老化。
|
||||
|
||||
## 2026-11-03 鼠标/点击跳转优化(优先于下文旧状态)
|
||||
- Moonlight私有客户端新增两个补丁:鼠标合并不跨点击/键盘/窗口边界,绝对鼠标按钮封住前一位置批次;
|
||||
低延迟非pacing队列可丢弃等待帧保留最新帧,wrapper默认VDI_LATEST_FRAME=1。保持单白色Windows光标。
|
||||
- 现场32次交替点击:旧客户端30/32正确(第1、13错位),补丁32/32正确;native输入测试1000轮和ASAN通过。
|
||||
配对串流补丁队列2.14ms/呈现6.12ms,原3.39ms/6.84ms;帧率约28.4/28.1,受网络/软件编码波动,不能称零延迟。
|
||||
- Windows保留DragFullWindows=1、MouseTrails=0;已在test交互会话将MinAnimate从1改0、菜单400改100ms,读回通过,
|
||||
原值备份在test的LocalAppData/VDI;临时执行/测试任务已清理。
|
||||
- 用户确认蓝框在盒子登录/选择页:QFrame:hover错误匹配QLabel。移除蓝色悬停框和登录焦点蓝框,限定卡片对象样式,
|
||||
局部编译CTest3/3,部署新版kiosk;悬停截图亮蓝像素8302→0。UI/wrapper备份input-rendering-20261103-114542。
|
||||
详情见docs/input-rendering-optimization-20261103.md和terminal/moonlight/README.md。
|
||||
|
||||
## 2026-11-02 最新实测(优先于下文旧状态)
|
||||
- 2026-11-02晚:已在用户目录完成MPP+FFmpeg-Rockchip+Moonlight6.2.0私有构建,
|
||||
实际2K串流选中h264_rkmpp/drm_prime与EGL/GLES呈现,非仅FFmpeg样本测试。
|
||||
2K20 smooth会话平均呈现18.97FPS、解码10.53ms、呈现2.54ms;2K30 low-latency
|
||||
呈现23.46FPS、解码9.11ms、队列2.39ms、呈现5.41ms。服务端仍Code43/libx264。
|
||||
2K30 smooth队列31.95ms,VSync开/帧步调关也有28.93ms,故该盒子选低延迟档。
|
||||
原始日志和限制见 `docs/rendering-execution-20261102.md`。
|
||||
通过 `~/.config/vdi-terminal/render.conf` 显式选择私有wrapper+hardware,系统客户端不覆盖;
|
||||
Windows桌面票据改30FPS,原20FPS文件备份为desktops.before-rkmpp-20261102.json。
|
||||
构建脚本 `tools/build-rkmpp-client.sh` 修Qt5 optional、EGL探测缓存与旧对象不重编;
|
||||
kiosk就绪判断兼容RKMPP独立测试解码器日志,CTest 2/2通过。
|
||||
鼠标MouseTrails=0、DragFullWindows=1;现场仍有VDIApplyTransparentCursor刷新参数任务,
|
||||
先前“任务已删除”的记录不准确,本轮未改变光标配置;截图单白光标正常。
|
||||
不能把23FPS、硬解接通解释为GPU已恢复、2K60或端到端零延迟。
|
||||
- 已部署真实2K:盒子HDMI 2560x1440约60Hz、Windows VDD 2560x1440@30、Moonlight 2560x1440@20/30Mbps。
|
||||
- RK3528旧PHY拒绝EDID的241.70MHz,实测用297MHz固定时钟和扩展消隐成功:
|
||||
`297.00 2560 2608 2640 2720 1440 1443 1448 1820 +HSync -VSync`;盒子重启已验证,link Good。
|
||||
- Windows VDD版本11.30.4.434,生效XML在 `C:\VirtualDisplayDriver\vdd_settings.xml`。
|
||||
登录任务VDISet2KDisplay动态定位Virtual Display Driver(不能写死DISPLAY编号),脚本在
|
||||
`C:\ProgramData\VDI\set-2k-display.ps1`,仓库副本 `tools/set-2k-display.ps1`;VM重启后执行成功。
|
||||
- 鼠标保留白色Windows远程光标,已删除本地黑色光标自动开启和透明光标任务;GLES2、关闭MATE合成器保留。
|
||||
- **GPU并非稳定完成**:运维曾打通582.78/NVENC,连续动画可发送59FPS;但本次VM重启后Code43复发,
|
||||
nvidia-smi失败、Sunshine当前实际为libx264软件编码。客户机重启设备/隔离VDD/VM停启均无效;
|
||||
无效VDIRecoverNvidia任务已删除。未改宿主VFIO模块、未重启宿主。ROM为gtx965m-pci.rom。
|
||||
- NVENC正常时2K20实测19.67FPS、网络丢帧0%、抖动丢帧0.94%;这些数据不能作为当前软编性能保证。
|
||||
- 详细最新状态见 `docs/moonlight-kiosk-20261028.md` 的“当前现场(2026-11-02 15:45)”。
|
||||
|
||||
## 当前状态
|
||||
- 项目目录:`D:\DAIMA\vdi-cloud`
|
||||
- 终端盒子:RK3528 PRO DDR4,Armbian Ubuntu Noble,aarch64,IP `192.168.1.188`
|
||||
- 当前图形:LightDM + Xorg;正式文档要求 Weston + Wayland + Qt,当前只是 POC
|
||||
- Proxmox:9.2.2 节点 `starhe`。VM 100 win10-test `192.168.1.70`(Sunshine/Moonlight 云桌面);
|
||||
VM 101 kylin-test `192.168.1.71`(test1)、VM 102 ubuntu-test `192.168.1.72`(test2),
|
||||
SSH 可登,**均已安装 Sunshine 并验证盒子正常串流**(2026-11-04,见顶部最新记录);登录凭据由管理员保管
|
||||
- `kiosk-shell`(Qt/C++):**开机直进桌面列表**(2026-10-30 起,设备 SN 即用户自动登录,
|
||||
登录页代码保留待平台正式认证接入)→ 选择桌面 → Moonlight 会话;**RDP 代码已于 2026-10-29
|
||||
全部删除**(git 历史可查),Moonlight 是唯一会话通道,无 freerdp 构建依赖
|
||||
- 新增退出三层与会话过场层:`Ctrl+Alt+Shift+Q` 退串流回列表;列表页"退出登录";
|
||||
登录页无退出按钮(退出终端只在管理菜单);点连接有全屏过场层,首帧后淡出。
|
||||
详见 `docs/moonlight-kiosk-20261028.md` 2026-10-29 下午节
|
||||
- 代码位置:`terminal/kiosk-shell`
|
||||
- 远程安装路径:`~/vdi-terminal/bin/`(`kiosk-shell`、`vdi-kiosk-monitor`、`vdi-moonlight`)
|
||||
|
||||
## GPU 直通进行中(2026-10-30 下午,见 moonlight-kiosk 文档当日追加节)
|
||||
- 宿主侧已就绪:nouveau 已黑名单、GPU 在 vfio-pci、IOMMU 组 2(root port+GPU)
|
||||
- 关键坑:vfio 清零 Subsystem ID(必须 `x-pci-sub-vendor-id/device-id` 注入 1558:6557);
|
||||
移动卡 vBIOS 需 sysfs 抽取(/usr/share/kvm/gtx965m.rom);NVIDIA 安装器在 SYSTEM 会话
|
||||
挂起(用 7zr 解包 + pnputil);582.78 绑定后 D3D 全灭已换 566.36(D3D 待验证)
|
||||
- **当前 VM 100 未挂 GPU**:另一并行会话在改配置(hv_vendor_id 方案),本会话已停手避撞,
|
||||
两个会话不要同时改 VM 100 配置!
|
||||
- Sunshine 配对 2026.914 改为"Web UI 批准式":`moonlight pair --pin NNNN` +
|
||||
`GET /api/csrf-token` + `POST /api/pin {pairing_id,pin}`;Web 凭据由环境管理员保管;
|
||||
删 sunshine_state.json = 全部失配;盒子 Moonlight 配置目录缓存旧 uniqueid 会卡死配对
|
||||
|
||||
## 必须遵守
|
||||
- 所有实现严格参考用户提供的两个正式 Word 和会议纪要;文档未确定的技术只能标记待调研,不得擅自定版。
|
||||
- 终端先做,后接自研管理平台 API;终端不直接调用 Proxmox API。
|
||||
- 正式状态机:`BOOT -> CONFIG -> CONNECTING -> SESSION -> RECONNECT -> ERROR`
|
||||
- 正式 Kiosk 选型:Weston + Wayland + Qt;会话通道统一 Moonlight(RDP 已删,勿在代码里复活协议分支)。
|
||||
- `/cert:ignore`、空密码仅限局域网 POC。
|
||||
- 项目经理新增待调研:RDP 是否适合 GPU/CAD/3D;研究 Moonlight/Sunshine,但不要在调研前删除 RDP、确定 Moonlight 参数或定版双协议。
|
||||
|
||||
## 已知关键结论
|
||||
- WiFi:约 -47 dBm,协商约 200 Mbps,零丢包/重传;卷帘问题不能简单归因于 WiFi。
|
||||
- HDMI 当前原生:`1920x1440@75Hz`(10-23 记录);2026-10-28 实测盒子系统已是 `1920x1080@60Hz`,
|
||||
与 Sunshine 侧虚拟显示器一致,串流 1:1 无缩放。以现场 `xrandr` 为准。
|
||||
- `/dev/mpp_service`、`/dev/rga` 存在,但 `mppvideodec` GStreamer 插件未确认;Weston/DRM 零拷贝待验证。
|
||||
- FreeRDP 3:`gdi_init` 必须在 PostConnect;NLA 空密码传 `strdup("")`;BGRX32 需转换为 Qt RGB;`/gfx` 是能力组合语义。
|
||||
- Moonlight/Sunshine(2026-10-28 实测):Windows 防火墙挡 ICMP,ping 不通不代表不通,
|
||||
可达性判断必须用 TCP 探测;`moonlight-qt` 的 app id 会随服务端重装失效,
|
||||
命令行要用应用名 `Desktop`;RK3528 上 `--video-decoder auto` 会选到不可用的
|
||||
`h264_v4l2m2m`,必须显式 `software`。
|
||||
- **21.6 FPS 上限根因(2026-10-29 修正)**:不是 x264 编码算力——ultrafast 前后帧率分毫不差,
|
||||
串流中 VM CPU 仅 0.5%、sunshine 累计 14 秒 CPU/3 分钟会话,鼠标运动也不变;
|
||||
真正瓶颈在虚拟显示/捕获节奏(MttVDD 呈现 + Desktop Duplication 脏帧 + QEMU 显示栈,
|
||||
VM CPU 型号 `QEMU Virtual CPU version 2.5+` 未透传宿主 AVX2)。
|
||||
根治 = GPU 直通(NVENC + 真实显示栈);备选 = VirtIO-GPU / cpu=host 排除 QEMU 显示栈因素。
|
||||
客户端解码 10~15 ms、网络丢包 0%、延迟 3 ms,链路健康。
|
||||
|
||||
## Web 控制台(2026-10-28 方案确认)
|
||||
- `console/` 是管理员 Web 管理控制台,不是 RK3528 盒子的用户登录页。
|
||||
- 正式技术栈:Vue3 + Vite + TypeScript 严格模式 + Element Plus;后端通过 api-gateway,JWT + RBAC。
|
||||
- 页面:仪表盘、终端管理、桌面管理、用户与绑定、GPU 池、监控、OTA、审计日志。
|
||||
- 角色:超级管理员、运维、只读。
|
||||
- 终端产品流程仍应是 `kiosk-shell 登录/首配 -> 桌面列表 -> 获取短期连接票据 -> RDP/Moonlight 会话`;
|
||||
当前开机直连 Moonlight 仅是串流技术验证,不是最终产品登录流程。
|
||||
- 开发策略:先做功能骨架、Mock API、OpenAPI 契约、RBAC 与 E2E;项目经理 UI 稿到后替换视觉层。
|
||||
- 完整方案:`docs/web-console-mvp-plan-20261028.md`。
|
||||
|
||||
## 尚未完成
|
||||
- 服务端 GPU 直通:GPU 为 CLEVO 准系统上的 GM204M/965M(10de:13d9,宿主被 nouveau 占用需解绑);
|
||||
**IOMMU 已启用**(组 2 = root port 00:01.0 + GPU 01:00.0,整组直通);VM 内需装 R580 legacy 驱动。
|
||||
操作单与 GPU 看板见 `docs/gpu-passthrough-20261030.md`,看板 `http://192.168.1.51:8090/`(systemd 自启)
|
||||
- RK3528 VPU 硬解(h264_v4l2m2m 等缺可用设备,需 MPP FFmpeg 补丁或 V4L2 request 驱动)
|
||||
- 2K 分辨率链路验证(虚拟显示器 + 盒子显示模式需同步调整)
|
||||
- Weston/DRM/mppvideodec
|
||||
- 后端 API 注册、配置、MQTT 心跳(Moonlight 路径目前是本地静态配置)
|
||||
- 终端音频出音现场确认
|
||||
- 协议最终选型定版(RDP / Moonlight / 双协议,需实测数据 + 产品方案共同确认)
|
||||
- 监控与运维脚本、OTA、USB 重定向等终端其余能力
|
||||
|
||||
先读 `PROJECT_CONTEXT.md`,再查看 `git status`,不要丢弃未提交改动。
|
||||
- 2026-11-06 Ubuntu点击/拖拽修复:根因是Moonlight会话中断或失焦丢失BUTTONUP后未清理远端鼠标按钮;新增terminal/moonlight/patches/0006-vdi-release-stuck-mouse-buttons.patch,焦点丢失/会话退出释放五个按钮。盒子新SHA a09140ef…,旧SHA 5d4330f…已备份;三项输入测试通过,Ubuntu退出后普通Sunshine active、native-cursor guard inactive。报告 docs/ubuntu-mouse-release-20261106.md。
|
||||
整理前本文件与 PROJECT_CONTEXT、README 的完整内容,以及 35 份原专题文档,都可通过[历史归档](docs/archive/README.md)恢复。历史结论按日期与当前证据核对,不直接当作现网状态。
|
||||
|
||||
@ -1,409 +1,17 @@
|
||||
# 数字员工 VDI 云终端项目上下文
|
||||
# 项目上下文入口
|
||||
|
||||
> 这是本项目后续继续开发时的必读上下文。所有实现必须优先以项目经理提供的正式文档和会议纪要为准;文档未明确的地方只能标记为待确认/待调研,不能擅自定版。
|
||||
>
|
||||
> 本文件整理自截至 2026-10-23 的完整开发对话。
|
||||
更新:2026-11-09。旧文件主要记录 10 月的 RDP、软解、1080p 和开机直连试验,已完整保存在[历史归档](docs/archive/README.md)的同名文件中;继续开发以以下主题文档核对当前状态。
|
||||
|
||||
## 1. 项目目标
|
||||
- [当前架构、环境与验证边界](docs/current-status.md)
|
||||
- [终端启动、进出、快捷键、构建](docs/terminal-guide.md)
|
||||
- [三系统显示、输入、USB 与电源策略](docs/desktop-guide.md)
|
||||
- [监控分工、日志与恢复](docs/operations.md)
|
||||
- [未来计划及暂缓范围](docs/roadmap.md)
|
||||
|
||||
项目是“云-管-端” VDI 云桌面方案:
|
||||
当前协议主线为 Moonlight/Sunshine;终端 Qt/C++,现场 LightDM/Xorg 独占 kiosk,正式目标 Weston/Wayland。盒子开机直接进入选择页;11-09 已接通真实平台认证、授权桌面和票据,现场配置见[真实接入说明](docs/cloud-live-integration.md)。平台 Go,控制台 Vue3/TypeScript/Element Plus,Agent/OTA/USB 方向为 Rust;已有 POC 和完整产品交付需区分。
|
||||
|
||||
```text
|
||||
RK3528 云终端
|
||||
-> 终端接入程序
|
||||
-> 自研管理平台 API
|
||||
-> Proxmox VE 管理的虚拟机
|
||||
-> Windows / Ubuntu / 麒麟 / 后续安卓桌面
|
||||
```
|
||||
终端不调用 PVE API、不保存 PVE 管理凭据。`console/` 是管理员 Web 控制台,和盒子客户选择页不同。盒子开关不会自动控制 VM 生命周期;客户图形电源入口限制保留管理员维护能力。
|
||||
|
||||
终端侧最终职责:
|
||||
原始需求来自 `docs/` 三份 Word 与用户提供的会议资料,出处见[索引](docs/README.md)。旧文档中的协议建议、状态机和失败实验不自动覆盖后续已确认的实现;未定事项写入计划,不能把猜测写为定版。
|
||||
|
||||
- 开机进入 Kiosk/首配登录页;
|
||||
- WiFi/有线联网;
|
||||
- 终端注册、心跳、配置下发、日志上报;
|
||||
- 连接远程桌面;
|
||||
- 显示远程画面并传递键盘、鼠标、U 盘等外设;
|
||||
- 断线重连;
|
||||
- 独立 Monitor 看门狗;
|
||||
- A/B OTA;
|
||||
- 后续使用 VPU/DRM 硬件解码与零拷贝显示。
|
||||
|
||||
项目经理新增方向:RDP 可能不适合 GPU/CAD/3D 高质量低延迟串流,周一需要调研 RDP 与 Moonlight。Moonlight 只作为待调研/待验证的协议方向,不能在未验证前替换 RDP 或擅自确定端口、认证字段、嵌入方式。
|
||||
|
||||
## 2. 权威需求来源
|
||||
|
||||
1. `E:\Desktop\数字员工\VDI云终端产品方案-定版摘要(1).docx`
|
||||
- 产品定版 v2.1。
|
||||
- 规定 RK3528 终端、2K 默认/4K 上限、FreeRDP 3、GStreamer + mppvideodec、Proxmox/KVM/VFIO、GPU 算力池、管理平台等。
|
||||
2. `E:\Desktop\数字员工\VDI云桌面软件开发方案(1).docx`
|
||||
- 软件开发 v1.0。
|
||||
- 规定 monorepo、终端/云端/控制台技术栈、20 项需求追踪矩阵、测试层次、M1-M4 里程碑。
|
||||
3. `E:\Documents\xwechat_files\wxid_q6y4us6kip0g22_aa63\msg\file\2026-10\export_1792654201375.docx`
|
||||
- 会议纪要。
|
||||
- 规定当前优先:先拿盒子、终端基础连接、Proxmox 逻辑/API、2K/4K/发热/网络验证、Monitor、OTA 后置;张帅协助 P100 GPU 验证。
|
||||
4. 会议纪要图片:项目技术要点思维导图。
|
||||
|
||||
核心纪律:如果代码实现与上述文档不一致,先指出偏差并等待确认;不能以“更快”或“更方便”为理由改用未定版技术栈。
|
||||
|
||||
## 3. 正式技术选型与边界
|
||||
|
||||
正式开发方案 2.1/2.2 节:
|
||||
|
||||
```text
|
||||
vdi-cloud/
|
||||
├─ terminal/
|
||||
│ ├─ meta-vdi/ # Yocto:BSP/内核/设备树/U-Boot/Kiosk/Agent 配方
|
||||
│ ├─ kiosk-shell/ # Weston + 全屏 Qt 壳
|
||||
│ ├─ freerdp-rk/ # FreeRDP 3 fork + MPP/VPU 硬解补丁
|
||||
│ ├─ vdt-agent/ # Rust:注册/心跳/命令/日志
|
||||
│ ├─ ota-updater/ # Rust:manifest/签名/A-B/回滚
|
||||
│ └─ usb-monitor/ # Rust:udev/挂载/重定向策略
|
||||
├─ platform/ # Go 云端服务
|
||||
├─ console/ # Vue3 + TS + Element Plus
|
||||
├─ deploy/ # compose/Helm/离线包
|
||||
├─ docs/ # OpenAPI/MQTT/DB schema
|
||||
└─ tools/ # 烧录/压测/CI
|
||||
```
|
||||
|
||||
终端技术栈定版:
|
||||
|
||||
- Kiosk 壳:C/C++ + Qt,目标显示环境 Weston/Wayland;
|
||||
- FreeRDP 插件:C/C++;
|
||||
- Agent/OTA/USB:Rust;
|
||||
- 协议主线:FreeRDP 3;SPICE 备用;自研 H.265 over QUIC 只作为后续预留;
|
||||
- 云端:Go;
|
||||
- 前端:Vue3 + TypeScript + Element Plus;
|
||||
- 数据库:PostgreSQL + VictoriaMetrics;
|
||||
- 通信:HTTPS + MQTT;
|
||||
- 云端底座:Proxmox VE + KVM/QEMU + VFIO;
|
||||
- GPU:NVIDIA CUDA、华为 CANN 等按卡型池化;终端不安装 GPU 计算组件。
|
||||
|
||||
开发方案 3.2 对 kiosk-shell 的明确要求:
|
||||
|
||||
- 技术:Weston(Wayland)+ 全屏 Qt 壳进程;
|
||||
- 状态机:`BOOT -> CONFIG -> CONNECTING -> SESSION -> RECONNECT -> ERROR`;
|
||||
- 首配页:SN 绑定、WiFi 扫码、服务器地址;
|
||||
- 状态条:网络、会话、OTA 进度;
|
||||
- FreeRDP:以 `libfreerdp3` API 内嵌,不拉起外部进程;
|
||||
- 全局热键:`Ctrl+Alt+Shift+M` 呼出管理菜单。
|
||||
|
||||
当前实现是在现有 Armbian 的 LightDM + Xorg 环境中先完成 Qt/Kiosk POC;这不是对正式 Weston 选型的替代。Weston/DRM 必须继续验证。
|
||||
|
||||
## 4. 盒子与联调环境
|
||||
|
||||
### 盒子
|
||||
|
||||
- 型号:RK3528 PRO DDR4;
|
||||
- OS:Armbian v25.05 rolling,Ubuntu Noble 用户空间;
|
||||
- Kernel:`6.1.115-vendor-rk35xx`;
|
||||
- 架构:`aarch64`;
|
||||
- 内存:约 3.83 GB;
|
||||
- 存储:约 29 GB;
|
||||
- IP:`192.168.1.188`;
|
||||
- SSH:`ssh armbian@192.168.1.188`;
|
||||
- 图形:LightDM + Xorg,当前不是 Wayland;
|
||||
- 显示器:`HDMI-1 1920x1440 @ 75Hz`,没有 2560x1440 EDID 模式;
|
||||
- DRM:`/dev/dri/card0`、`card1`、`renderD128`、`renderD129`;
|
||||
- 权限:用户已在 `video`、`render` 组;
|
||||
- 温度:空载约 53°C;
|
||||
- WiFi:`wlan0`,SSID `YXTech`,信号约 -47 dBm,协商约 200 Mbit/s,实测无丢包/重传;
|
||||
- 设备节点:`/dev/mpp_service`、`/dev/rga` 存在;
|
||||
- OpenGL:Mali450,Mesa 25.0.7;Xorg 日志显示 modesetting + glamor initialized。
|
||||
|
||||
### 已安装环境
|
||||
|
||||
- gcc/g++ 13;
|
||||
- make、cmake 3.28、ninja、pkg-config、git;
|
||||
- Qt5 5.15.13(Core/Gui/Widgets/Wayland 包);
|
||||
- Weston 13.0.0;
|
||||
- `freerdp3-dev` 3.32.1;
|
||||
- `freerdp3-x11`,实际命令:`xfreerdp3`;
|
||||
- `freerdp3-wayland`,实际命令:`wlfreerdp3`,但当前没有 Wayland 会话;
|
||||
- `mesa-utils`;
|
||||
- `xdotool`(测试用);
|
||||
- 当前未确认 `mppvideodec`,Ubuntu 官方源没有 Rockchip MPP GStreamer 插件。
|
||||
|
||||
### apt 源
|
||||
|
||||
旧 `/etc/apt/sources.list` 中普通 Ubuntu 源会造成 ARM64 404,已备份为 `sources.list.bak` 并注释。有效配置在 `/etc/apt/sources.list.d/ubuntu.sources`,使用 TUNA 的 `ubuntu-ports`。
|
||||
|
||||
不要执行:
|
||||
|
||||
- `sudo apt upgrade`:曾显示约 298 个包待升级,当前不要整体升级;
|
||||
- `do-release-upgrade`:不要升级系统版本;
|
||||
- 不要在盒子上安装 Proxmox,盒子是 ARM64 终端,不是 Proxmox 主机。
|
||||
|
||||
本机已经给盒子安装了 SSH 公钥(注释 `zcode-dev`),可以免密远程执行:
|
||||
|
||||
```bash
|
||||
ssh armbian@192.168.1.188
|
||||
```
|
||||
|
||||
SSH 中执行图形命令要带:
|
||||
|
||||
```bash
|
||||
export DISPLAY=:0
|
||||
export XAUTHORITY=/home/armbian/.Xauthority
|
||||
```
|
||||
|
||||
## 5. Proxmox/Windows 联调环境
|
||||
|
||||
- Proxmox VE:9.2.2;
|
||||
- 节点:`starhe`;
|
||||
- VM:101,名称 `win10-gongchang01`;
|
||||
- Windows:Windows 10 22H2 专业版;
|
||||
- VM IP:`192.168.1.70`;
|
||||
- RDP:TCP 3389;
|
||||
- 用户:`test1`;
|
||||
- 当前 POC 允许空密码登录,正式环境不能使用空密码。
|
||||
|
||||
边界:终端最终不应直接调用 Proxmox API。最终关系是:
|
||||
|
||||
```text
|
||||
终端 -> 自研管理平台 API -> Proxmox API -> 虚拟机
|
||||
终端 -> 管理平台返回的桌面连接地址/网关 -> RDP 或其他协议
|
||||
```
|
||||
|
||||
## 6. 已完成工作
|
||||
|
||||
### 6.1 网络与远程桌面基础验证
|
||||
|
||||
已完成:
|
||||
|
||||
- 盒子 WiFi 联通;
|
||||
- Windows 10 VM 可达;
|
||||
- 手工命令 `xfreerdp3` 成功显示 Windows 10 桌面;
|
||||
- 键盘鼠标可操作;
|
||||
- 退出全屏最可靠方式:从 SSH 执行 `pkill -f xfreerdp3`;
|
||||
- 当前 X11 环境下不要直接在 SSH 无 DISPLAY 会话中启动 GUI。
|
||||
|
||||
### 6.2 monorepo
|
||||
|
||||
项目已在:
|
||||
|
||||
```text
|
||||
D:\DAIMA\vdi-cloud
|
||||
```
|
||||
|
||||
已初始化 git。最近已提交:
|
||||
|
||||
```text
|
||||
589cde4 feat(kiosk-shell): FreeRDP 3 内嵌客户端跑通,登录页可进入远程桌面
|
||||
419630f feat(kiosk-shell): 首版登录页与状态机骨架
|
||||
```
|
||||
|
||||
当前 git 在日报暂停开发时有未提交改动,主要是 Monitor、心跳、网络显示修复等。不要丢弃这些改动。
|
||||
|
||||
### 6.3 kiosk-shell
|
||||
|
||||
位置:`D:\DAIMA\vdi-cloud\terminal\kiosk-shell`
|
||||
|
||||
已实现:
|
||||
|
||||
- Qt5 全屏登录页;
|
||||
- 深色 UI;
|
||||
- SN 显示;
|
||||
- 服务器地址、账号、密码、显示分辨率;
|
||||
- 记住连接参数;
|
||||
- 本地配置文件:`~/.config/vdi-terminal/client.conf`;
|
||||
- 状态机:`BOOT/CONFIG/CONNECTING/SESSION/RECONNECT/ERROR`;
|
||||
- `Ctrl+Alt+Shift+M` 管理菜单;
|
||||
- RDP 会话状态;
|
||||
- 连接断开后重连状态;
|
||||
- 主进程心跳文件设计:`/tmp/vdi-kiosk.heartbeat`;
|
||||
- CMake + Ninja 原生 ARM64 编译通过过多次。
|
||||
|
||||
当前盒子上的实际运行路径:
|
||||
|
||||
```text
|
||||
~/vdi-terminal/bin/kiosk-shell
|
||||
```
|
||||
|
||||
配置示例:
|
||||
|
||||
```text
|
||||
version=1
|
||||
sn=...
|
||||
deviceKey=
|
||||
server=192.168.1.70
|
||||
username=test1
|
||||
password=
|
||||
wifiSsid=
|
||||
wifiPassword=
|
||||
resolution=1920x1440
|
||||
remember=1
|
||||
```
|
||||
|
||||
### 6.4 libfreerdp3 内嵌
|
||||
|
||||
已实现 `rdp_session.cpp/h`,不拉起外部 `xfreerdp3` 进程:
|
||||
|
||||
```text
|
||||
freerdp_new()
|
||||
freerdp_context_new_ex()
|
||||
freerdp_connect()
|
||||
FreeRDP 3 AuthenticateEx
|
||||
GDI primary buffer
|
||||
Qt QImage/QLabel 显示
|
||||
```
|
||||
|
||||
已实测修复的坑:
|
||||
|
||||
1. `gdi_init` 必须放在 `PostConnect` 中;连接前调用会在 `gdi_init_ex` 段错误;
|
||||
2. 不能依赖扩展 `ContextSize` 保存自定义指针,`gdi_init_ex` 可能重新分配 context 导致段错误;当前使用按 `freerdp*` 的静态注册表和 `RdpgfxClientContext.custom`;
|
||||
3. NLA 认证回调空密码必须传 `strdup("")`,不能传 `nullptr`,否则出现 `SEC_E_NO_CREDENTIALS`;
|
||||
4. FreeRDP 3 设置键使用 `FreeRDP_ServerHostname`、`FreeRDP_Username`、`FreeRDP_DesktopWidth` 等,不要照抄 FreeRDP 2.x 键名;
|
||||
5. `PIXEL_FORMAT_BGRX32` 是 B/G/R/X 四字节布局,Qt 显示前需要 BGR -> RGB 转换;
|
||||
6. `/gfx` 命令行是能力组合语义,不能把 `AVC420`/`AVC444` 当作单个值使用。实测可解析形式包括 `progressive|RFX|AVC420|AVC444`。
|
||||
|
||||
重要:当前 POC 已成功显示 Windows 10 的真实远程桌面画面,颜色正确,进程保持运行。这是目前最重要的实测成果。
|
||||
|
||||
这部分属于早期 FreeRDP POC 历史记录;对应的临时笔记文件已从仓库移除。
|
||||
当前终端会话实现和维护说明以 `terminal/kiosk-shell/` 及其 README 为准。
|
||||
|
||||
### 6.5 显示问题
|
||||
|
||||
曾出现“从上到下卷帘式渲染”。目前确认:
|
||||
|
||||
- WiFi 信号/速率/丢包不是主要原因;
|
||||
- 显示器原生是 1920x1440,而曾请求 2560x1440,存在缩放;
|
||||
- X11/MATE/FreeRDP CPU 侧渲染路径性能有限;
|
||||
- 正式文档要求 Weston + DRM plane + GStreamer mppvideodec 零拷贝,这正是后续方向;
|
||||
- 该问题按用户要求暂时不阻断当前 GUI/登录页主线,后续处理。
|
||||
|
||||
注意:此前对“X11 无 glamor 硬件合成”的判断不准确,Xorg 日志实际显示 `glamor X acceleration enabled on Mali450`;后续不要继续引用这个错误结论。正确表述是:当前路径仍未达到目标流畅度,具体瓶颈需要后续基准测试确认。
|
||||
|
||||
### 6.6 Moonlight 云桌面开机直达(2026-10-28,历史记录)
|
||||
|
||||
本节保留当时的联调过程和取舍。2026-11-03 的实际部署状态、显示恢复、输入补丁及
|
||||
渲染参数以后续 `MEMORY.md` 和对应日期的 `docs/` 记录为准。
|
||||
|
||||
RDP/Moonlight 实测对比见 `docs/rdp-vs-moonlight-comparison-20261028.md`,
|
||||
落地记录见 `docs/moonlight-kiosk-20261028.md`。
|
||||
|
||||
当时盒子开机默认走 Moonlight 直连云桌面,链路如下:
|
||||
|
||||
```text
|
||||
~/.config/autostart/vdi-moonlight.desktop
|
||||
-> vdi-kiosk-monitor --heartbeat-file /tmp/vdi-moonlight.heartbeat --max-restart 200
|
||||
-- /home/armbian/vdi-terminal/bin/vdi-moonlight
|
||||
-> moonlight-qt stream 192.168.1.70 Desktop (fullscreen, 1920x1080, H.264, 软解)
|
||||
```
|
||||
|
||||
仓库新增:`terminal/kiosk-shell/scripts/vdi-moonlight`、`etc/moonlight.conf`、
|
||||
`vdi-moonlight.desktop`;`src/monitor.cpp` 改为按进程组结束整棵进程树。
|
||||
|
||||
实测:满屏云桌面 1:1 无缩放;杀客户端 5 秒内重连;杀启动器 3 秒内被看门狗拉起;
|
||||
性能叠图 1080p60 实际 21.59 FPS、网络丢包 0%、延迟 3 ms、解码 15.04 ms、抖动丢帧 13.64%。
|
||||
|
||||
关键坑位(后续维护必须知道):
|
||||
|
||||
1. Windows 防火墙屏蔽 ICMP,盒子 ping 不到云桌面主机,但 TCP/UDP 正常;
|
||||
可达性判断必须用 TCP 探测,ping 会误判;
|
||||
2. `moonlight-qt` 的应用 id 随服务端重装/升级失效,旧 id 会报“找不到应用程序 <id>”;
|
||||
命令行要用应用名 `Desktop`;
|
||||
3. RK3528 上 `--video-decoder auto` 会选到不可用的 `h264_v4l2m2m`,
|
||||
必须显式 `--video-decoder software`;
|
||||
4. RDP 路径的自启项已禁用但保留,可随时切回。
|
||||
|
||||
|
||||
## 7. 当前未完成/未验证
|
||||
|
||||
1. 服务端 GPU 直通未做:宿主机 `starhe` 有 GTX 965M(`0000:01:00.0`,IOMMU Group 2 独占),
|
||||
主机同时有 Intel 核显 `0000:00:02.0` 可继续驱动本机显示,具备直通条件;
|
||||
直通后 Sunshine 才能用 NVENC 硬编码,把 1080p 帧率从 ~21.6 FPS 提到 60 FPS;
|
||||
2. RK3528 VPU 硬解不可用:`h264_v4l2m2m` / `hevc_v4l2m2m` / `hevc_v4l2request` 均报
|
||||
`Could not find a valid device`,当前只能用软件解码,4K 目标必须先解决;
|
||||
3. 2K 分辨率链路未验证:盒子屏幕现为 1920x1080@60Hz,Sunshine 侧虚拟显示器也是 1920x1080@60Hz;
|
||||
4. 当前实现仍使用 X11 POC,正式方案要求 Weston/Wayland;
|
||||
5. 当前 RDP 会话使用 CPU GDI -> Qt 复制,尚未接 GStreamer/mppvideodec/VPU;
|
||||
6. `mppvideodec` 来源和可用性未确认;
|
||||
7. 终端注册、MQTT 心跳、配置下发、日志上报、OTA、USB 尚未接后端 API;
|
||||
8. 前端/后端管理平台尚未接入;Moonlight 路径目前读本地静态配置文件;
|
||||
9. 终端音频出音未现场确认(`--audio-on-host` 已开);
|
||||
10. 协议最终选型未定版:实测已证明 Moonlight 画面质量与 CPU 代价优于 RDP,
|
||||
但“办公用 RDP、GPU/CAD/3D 用 Moonlight”的双协议结论仍需产品方案确认。
|
||||
|
||||
## 8. 下一步建议(按优先级)
|
||||
|
||||
### A. 服务端 GPU 直通(当前最高价值项)
|
||||
|
||||
Sunshine 在 VM 内用 libx264 软件编码,4 核同时承担 1080p 捕获与编码,
|
||||
是 21.6 FPS 与 13.64% 抖动丢帧的唯一原因;客户端侧链路完全健康。
|
||||
|
||||
```text
|
||||
宿主机 starhe(Xeon E3-1200 v3 + Intel 核显 + GTX 965M)
|
||||
-> 确认内核 IOMMU 已启用(PCI 设备已有独立 IOMMU group)
|
||||
-> vfio-pci 绑定 0000:01:00.0,提取 vBIOS ROM
|
||||
-> VM 100 增加 hostpci,Windows 内装 GeForce 驱动
|
||||
-> nvidia-smi 正常后 Sunshine 切 NVENC 硬编码
|
||||
-> 复测 1080p60 帧率、抖动丢帧、服务端 CPU
|
||||
```
|
||||
|
||||
主机 root 密码已知(`yx66668888`),可用 `SSH_ASKPASS` 非交互登录。
|
||||
|
||||
### B. 收尾当前终端 POC
|
||||
|
||||
1. 保留当前 Moonlight 开机直达版本作为可回退基线(`docs/moonlight-kiosk-20261028.md`);
|
||||
2. 确认开机自启在真实重启后生效(当前只验证了手动拉起与被守护重启);
|
||||
3. 网络状态显示修复(此前显示错误的 `600`)尚未确认最新二进制效果;
|
||||
4. RDP 路径代码保留但自启已禁用,按需切回。
|
||||
|
||||
### C. 协议策略与代码结构
|
||||
|
||||
实测结论已经支持双协议方向,但仍未定版:
|
||||
|
||||
```text
|
||||
普通办公 -> RDP
|
||||
GPU/CAD/3D -> Moonlight
|
||||
```
|
||||
|
||||
如果确认需要双协议,代码结构应演进为:
|
||||
|
||||
```text
|
||||
DesktopSession 接口
|
||||
├─ RdpSession # 保留当前 libfreerdp3
|
||||
└─ MoonlightSession # 当前是 shell 启动器 + moonlght-qt 子进程
|
||||
SessionManager # 根据 API 下发 protocol 选择实现
|
||||
```
|
||||
|
||||
不要在定版前擅自确定 Moonlight 的嵌入方式、认证字段或删除 RDP 代码。
|
||||
|
||||
|
||||
### C. 后续 API 对接
|
||||
|
||||
与后端确认:
|
||||
|
||||
- `/api/v1/terminal/register`;
|
||||
- 配置拉取接口;
|
||||
- MQTT 心跳和命令格式;
|
||||
- 桌面连接信息字段;
|
||||
- 返回 VM 地址还是 RDP/Moonlight 网关地址;
|
||||
- 是否使用一次性票据;
|
||||
- GPU 桌面如何标记协议/资源池;
|
||||
- 终端不直接调用 Proxmox API。
|
||||
|
||||
## 9. 安全与工程注意事项
|
||||
|
||||
- `/cert:ignore` 只能用于当前局域网 POC,正式版本必须证书校验;
|
||||
- 空密码只能用于当前测试 VM,正式环境必须禁用;
|
||||
- 不要把密码提交到 git;当前本地配置仅用于 POC;
|
||||
- 终端正式版本不要长期保存 Windows 密码,应使用管理平台/网关/短期凭据方案;
|
||||
- 不要执行 `git reset --hard`、删除用户已有改动或覆盖未审阅文件;
|
||||
- 不要把推测写成“已实现”;日报和项目状态要区分“已验证/已完成/待验证/待调研”。
|
||||
|
||||
## 10. 今日日报关联
|
||||
|
||||
日报模板:`E:\Desktop\日报.txt`。
|
||||
已按 10 月 23 日实际情况填入并保存:
|
||||
|
||||
- 终端硬件与系统基线;
|
||||
- 编译环境;
|
||||
- monorepo/kiosk-shell;
|
||||
- libfreerdp3 内嵌 RDP;
|
||||
- Windows 10 远程桌面实机验证;
|
||||
- 2K 显示异常初步定位;
|
||||
- 明日 Monitor/Weston/mppvideodec/API 待办;
|
||||
- AVC444、mppvideodec、Weston、API 契约风险。
|
||||
|
||||
如果继续写日报,必须以实际工具执行结果为准,不能把未编译或未实测项目写成完成。
|
||||
开发前查看 `git status`,不丢弃未提交改动;不随手整体升级盒子系统或改变宿主/GPU 配置。部署、设备重启、回退应结合当前任务授权与现场使用情况执行。本次文档整理未执行现场操作。
|
||||
|
||||
273
README.md
@ -1,164 +1,46 @@
|
||||
# VDI 云桌面项目
|
||||
|
||||
本项目是一套面向企业云桌面场景的“云—管—端”VDI 解决方案,目标是在 RK3528 云终端上提供统一的用户登录、云桌面选择、远程会话接入、设备管理和运维能力。
|
||||
面向 RK3528 云终端的“云—管—端”VDI 项目。当前已接通 Windows、Ubuntu、麒麟的 Moonlight/Sunshine 会话,仍处于终端 POC 与产品流程验证阶段。
|
||||
|
||||
当前仓库采用 monorepo 结构,包含终端嵌入式软件、云端服务、Web 管理控制台、部署配置和配套工具。
|
||||
## 文档入口
|
||||
|
||||
> 当前开发阶段:终端 POC 与产品流程验证。仓库内已实现的功能、已验证的功能和规划功能会在本文档中分别标注。
|
||||
| 文档 | 内容 |
|
||||
| --- | --- |
|
||||
| [文档索引](docs/README.md) | 全部维护文档、历史归档与原始需求 |
|
||||
| [当前状态](docs/current-status.md) | 三系统配置、最新实施结果与验证边界 |
|
||||
| [终端说明](docs/terminal-guide.md) | 开机直达选择页、进出交接、快捷键、显示与构建 |
|
||||
| [云桌面说明](docs/desktop-guide.md) | 编码、光标、客体电源按钮、USB |
|
||||
| [运维说明](docs/operations.md) | 日志、监控分工、排障和回退 |
|
||||
| [云管开发说明](docs/cloud-platform-development.md) | Go API、Vue 控制台、本机启动、接口与联调边界 |
|
||||
| [未来计划](docs/roadmap.md) | 产品化与明确暂缓的 VPN、软件白名单、品牌画面 |
|
||||
|
||||
## 一、项目目标
|
||||
|
||||
终端产品的目标使用流程:
|
||||
## 当前流程与边界
|
||||
|
||||
```text
|
||||
盒子开机
|
||||
-> 设备身份自动登录
|
||||
-> 获取并展示可用云桌面列表
|
||||
-> 用户选择 Windows / Ubuntu / 麒麟等桌面
|
||||
-> 获取一次性连接票据
|
||||
-> 启动 Moonlight 会话
|
||||
-> 会话结束后返回桌面列表
|
||||
盒子开机 → LightDM 独占 kiosk → 选择云桌面 → Moonlight 串流 → 返回选择页
|
||||
```
|
||||
|
||||
总体系统关系:
|
||||
盒子使用私有 RKMPP 硬解/EGL,Windows 使用 NVENC,Linux 仍以软件编码为主。当前是 Xorg POC,Weston/Wayland 是后续方向;现场盒子已接入平台设备认证、授权列表和一次性连接票据,详见[真实接入说明](docs/cloud-live-integration.md)。正式边界为终端 → 自研平台 API → Proxmox,终端不直接管理 VM。
|
||||
|
||||
```text
|
||||
RK3528 云终端
|
||||
-> 自研管理平台 API
|
||||
-> Proxmox VE / KVM 虚拟化平台
|
||||
-> Windows / Ubuntu / 麒麟云桌面
|
||||
```
|
||||
11-09 已修复退出露出绿色桌面和进入左上角小窗;三系统 X11 采样通过,尚无外置逐帧采集。详细版本与证据见当前状态,不将历史性能测试当作实时健康报告。
|
||||
|
||||
终端不会直接调用 Proxmox API。桌面生命周期、用户授权、连接信息和协议策略统一由管理平台下发。
|
||||
## 仓库结构
|
||||
|
||||
## 二、仓库结构
|
||||
| 目录 | 职责 |
|
||||
| --- | --- |
|
||||
| `terminal/kiosk-shell/` | Qt 全屏入口、会话、看门狗和显示守护 |
|
||||
| `terminal/moonlight/`、`terminal/native-cursor/` | 私有硬解客户端补丁、Linux 原生光标 |
|
||||
| `terminal/usb-monitor/` | USB 存储设备透传 POC |
|
||||
| `terminal/meta-vdi/`、`terminal/vdt-agent/`、`terminal/ota-updater/` | 系统层、Agent 与 OTA 后续工作 |
|
||||
| `platform/`、`console/`、`deploy/` | Go API、Vue 管理台;演示部署与独立真实现场环境 |
|
||||
| `tools/` | 管理员构建、部署、诊断与测试工具 |
|
||||
| `docs/` | 主题说明、需求原件、历史归档与测试证据 |
|
||||
|
||||
```text
|
||||
vdi-cloud/
|
||||
├─ terminal/ # 终端嵌入式软件
|
||||
│ ├─ meta-vdi/ # Yocto 层:BSP、内核、设备树、U-Boot、配方
|
||||
│ ├─ kiosk-shell/ # Qt 全屏壳:桌面列表、Moonlight 会话、看门狗
|
||||
│ ├─ vdt-agent/ # Rust:注册、心跳、命令、日志
|
||||
│ ├─ ota-updater/ # Rust:OTA 签名校验、A/B 切换、回滚
|
||||
│ └─ usb-monitor/ # Rust:USB 监听、挂载与重定向策略
|
||||
├─ platform/ # Go 云端服务
|
||||
├─ console/ # Vue 3 + TypeScript Web 管理控制台
|
||||
├─ deploy/ # Docker Compose、Helm、离线部署包
|
||||
├─ docs/ # 方案文档、实施记录、测试证据和运维资料
|
||||
└─ tools/ # 烧录、构建、压测和 CI 工具
|
||||
```
|
||||
运行监控由独立 [vdi-monitor 项目](../vdi-monitor/README.md) 维护;本仓库保存实现、验证与回退资料。监控最新快照和 7 天指标不能替代全部历史证据。
|
||||
|
||||
当前有实现代码的模块为 `terminal/kiosk-shell`,其余模块保留目录结构,后续按里程碑逐步开发。
|
||||
## 构建与配置
|
||||
|
||||
## 三、当前功能
|
||||
|
||||
### 1. 终端登录与桌面列表
|
||||
|
||||
已实现:
|
||||
|
||||
- 开机后使用设备 SN 作为当前身份,自动进入桌面列表;
|
||||
- Windows、Ubuntu、麒麟三类桌面卡片;
|
||||
- **桌面连接可配置**:每张卡片有“设置”按钮,可配置服务器地址(支持 IP、域名或 VPN 隧道地址)、Sunshine 应用名、分辨率和帧率;
|
||||
- 连接配置持久化在 `~/.config/vdi-terminal/desktops.json`,
|
||||
地址为空的桌面显示“待分配”且不可连接;
|
||||
- 老环境自动迁移:首次运行会读取 `client.conf` 里的 server 地址填充 Windows 桌面;
|
||||
- `Ctrl+Alt+Shift+M` 管理菜单提供“桌面连接设置”统一入口;
|
||||
- 会话退出后返回桌面列表。
|
||||
|
||||
不同客户部署环境的桌面地址不同:换环境时在“设置”里改地址即可,不需要改代码。
|
||||
这里的 VPN 仅指网络层能力:盒子上的 VPN 客户端建立隧道后,桌面地址字段可以填写隧道地址。让云桌面内部浏览器和应用通过 Clash/Mihomo 访问 GPT 等外部服务属于后续规划,见“下一步计划”第 8 项。
|
||||
|
||||
设备身份自动登录是正式认证 API 上线前的临时实现。后续通过 `DesktopService` 适配层接入真实的用户认证、桌面列表和一次性连接票据接口,页面流程不需要重写。
|
||||
|
||||
### 2. Moonlight/Sunshine 会话
|
||||
|
||||
已实现:
|
||||
|
||||
- `MoonlightSession` 使用 `QProcess` 参数数组启动 `moonlight-qt`;
|
||||
- 不通过 Shell 拼接用户输入,降低命令注入风险;
|
||||
- H.264 软件解码;
|
||||
- 绝对鼠标模式;
|
||||
- 点击连接后立即锁定操作,保证任何时刻最多一个 Moonlight 会话;
|
||||
- 解析 Moonlight 输出,只有测试解码成功、渲染器选定且全屏串流窗口已激活后才淡出过场;
|
||||
- ARM/X11 环境通过 `GL_IS_SLOW=0` 缩短不可用 DRM 路径的探测时间;
|
||||
- 连接阶段有结构化耗时日志和 30 秒就绪超时,失败时自动关闭错误窗口并返回列表;
|
||||
- 会话进程退出后自动返回桌面列表;
|
||||
- `vsync`、`frame-pacing`、性能叠图等低延迟参数可配置;
|
||||
- 独立 Moonlight 启动脚本保留作调试用途,但不再作为产品开机入口。
|
||||
|
||||
2026-11-04 现场已接通 Windows、Ubuntu、麒麟三种桌面的正常选择与连接流程。Ubuntu 与麒麟现均为真实 2K / 30 FPS 请求配置与30Mbps,使用 `libx264` 软件编码和逐主机limited范围协商,盒子使用 RKMPP 硬解。2026-11-05 两套 Linux 已启用受限 X11 原生独立光标:同步实际形状/热点/显隐,由盒子显示指针位置,移动不再等待视频;Ubuntu恢复Adwaita/24px及默认鼠标加速,保留关闭动画。Windows保持原视频光标路径。形状、编号点击、断线恢复已实测,完整失焦与物理输入延迟尚未验收;点击页面及拖窗内容仍受软件编码限制,不能称定量等同Windows。方案和边界见 [原生独立光标实施记录](docs/linux-native-cursor-20261105.md);历史画质证据见 [Linux接入](docs/linux-desktop-onboarding-20261104.md)、[Ubuntu调优](docs/ubuntu-quality-20261105.md)、[麒麟2K调优](docs/kylin-quality-20261104.md)。
|
||||
|
||||
2026-11-05 后续只对 Ubuntu VM102 启用 `cpu=host`(AVX2)及 `virtio-gl`(共享Intel核显渲染)。受控长按框选的回显中位数约398→224ms,2K与原生光标保留;45FPS和4编码线程未继续改善延迟,最终仍用30FPS及默认线程。尚未达到Windows/原生目标,仍需解决捕获和软件编码瓶颈,详见 [Ubuntu框选与渲染调优](docs/ubuntu-drag-render-20261105.md)。
|
||||
|
||||
### 3. 状态机
|
||||
|
||||
当前终端状态机:
|
||||
|
||||
```text
|
||||
BOOT
|
||||
-> CONFIG / LOGIN
|
||||
-> AUTHENTICATING
|
||||
-> DESKTOP_LIST
|
||||
-> CONNECTING
|
||||
-> SESSION
|
||||
-> RECONNECT
|
||||
-> DESKTOP_LIST / ERROR
|
||||
```
|
||||
|
||||
### 4. Monitor 看门狗
|
||||
|
||||
已实现独立 `vdi-kiosk-monitor` 进程:
|
||||
|
||||
- 监控 kiosk-shell 进程是否退出;
|
||||
- 通过心跳文件检测主进程假死;
|
||||
- 异常退出后自动重启;
|
||||
- 连续启动失败后熔断;
|
||||
- 子进程独立进程组,结束时清理整棵进程树,避免遗留孤儿串流进程。
|
||||
|
||||
## 四、技术栈
|
||||
|
||||
### 终端
|
||||
|
||||
- C++17
|
||||
- Qt 5 Widgets / Qt Test
|
||||
- Moonlight Qt
|
||||
- 当前开发环境:Armbian + LightDM + Xorg
|
||||
- 正式目标环境:Weston + Wayland
|
||||
|
||||
### 云端(规划)
|
||||
|
||||
- Go
|
||||
- PostgreSQL
|
||||
- VictoriaMetrics
|
||||
- HTTPS + MQTT
|
||||
- Proxmox VE / KVM / VFIO
|
||||
|
||||
### Web 控制台(规划)
|
||||
|
||||
- Vue 3
|
||||
- TypeScript
|
||||
- Vite
|
||||
- Element Plus
|
||||
|
||||
## 五、构建 kiosk-shell
|
||||
|
||||
### 1. 依赖
|
||||
|
||||
目标设备或 ARM64 构建环境需要:
|
||||
|
||||
```bash
|
||||
sudo apt install \
|
||||
build-essential cmake ninja-build pkg-config \
|
||||
qtbase5-dev libqt5widgets5 libqt5test5
|
||||
```
|
||||
|
||||
Moonlight 会话还需要安装:
|
||||
|
||||
```bash
|
||||
sudo apt install moonlight-qt
|
||||
```
|
||||
|
||||
### 2. 编译
|
||||
在具备 Qt5 开发依赖的 ARM64 环境:
|
||||
|
||||
```bash
|
||||
cd terminal/kiosk-shell
|
||||
@ -167,101 +49,10 @@ cmake --build build
|
||||
QT_QPA_PLATFORM=offscreen ctest --test-dir build --output-on-failure
|
||||
```
|
||||
|
||||
编译产物:
|
||||
产物为 `kiosk-shell`、`vdi-kiosk-monitor`。产品连接配置在盒子 `~/.config/vdi-terminal/desktops.json`,基础配置为 `client.conf`;`moonlight.conf` 仅供旧独立调试入口使用。完整部署和客户端构建入口见[终端说明](docs/terminal-guide.md#build)。
|
||||
|
||||
```text
|
||||
build/kiosk-shell
|
||||
build/vdi-kiosk-monitor
|
||||
```
|
||||
## 开发约定
|
||||
|
||||
### 3. 运行
|
||||
保留用户已有改动;事实、计划和待验收结论分开记录。正式需求来源保留于 `docs/` 的三份 Word,后续明确确认的实现与当前用户要求优先于旧历史描述。账号、密码、私钥和连接票据通过安全配置提供,不新增到文档或提交。
|
||||
|
||||
在当前 X11 开发环境中:
|
||||
|
||||
```bash
|
||||
export DISPLAY=:0
|
||||
export XAUTHORITY="$HOME/.Xauthority"
|
||||
|
||||
./build/vdi-kiosk-monitor -- ./build/kiosk-shell
|
||||
```
|
||||
|
||||
产品自启入口为:
|
||||
|
||||
```text
|
||||
vdi-kiosk-monitor -> kiosk-shell
|
||||
```
|
||||
|
||||
Moonlight 只能在用户登录并选择桌面后由 kiosk-shell 启动,不应与 kiosk-shell 分别设置开机自启。
|
||||
|
||||
## 六、本地配置
|
||||
|
||||
终端基础配置路径:
|
||||
|
||||
```text
|
||||
~/.config/vdi-terminal/client.conf
|
||||
```
|
||||
|
||||
Moonlight 调试配置路径:
|
||||
|
||||
```text
|
||||
~/.config/vdi-terminal/moonlight.conf
|
||||
```
|
||||
|
||||
当前配置文件仅用于 POC。正式版本中,终端身份、配置版本和连接策略应由 `vdt-agent` 从管理平台同步。
|
||||
|
||||
不要将以下信息提交到仓库:
|
||||
|
||||
- Windows 或平台账号密码;
|
||||
- Proxmox root 密码;
|
||||
- Gitea 密码或访问令牌;
|
||||
- 终端设备密钥;
|
||||
- Sunshine 管理账号密码;
|
||||
- 私钥、证书和一次性连接票据。
|
||||
|
||||
## 七、当前联调环境
|
||||
|
||||
| 项目 | 当前环境 |
|
||||
|---|---|
|
||||
| 终端硬件 | RK3528 PRO DDR4 |
|
||||
| 终端架构 | ARM64 |
|
||||
| 终端系统 | Armbian / Ubuntu Noble |
|
||||
| 图形环境 | LightDM + Xorg(POC) |
|
||||
| 云平台 | Proxmox VE 9 |
|
||||
| Windows 云桌面 | Windows 10 22H2 |
|
||||
| Moonlight 服务端 | Sunshine |
|
||||
| 当前串流分辨率 | 1920×1080@60Hz |
|
||||
|
||||
具体 IP、测试账号和密码不写入 README,统一通过本地配置或部署环境变量管理。
|
||||
|
||||
## 八、已知限制
|
||||
|
||||
1. 终端用户认证、桌面列表和连接票据目前是本地演示实现,尚未接管理平台 API;
|
||||
2. Ubuntu 和麒麟桌面尚未配置实际连接地址;
|
||||
3. 当前 Moonlight 使用软件解码,RK3528 的 VPU/MPP 硬解尚未打通;
|
||||
4. Sunshine 当前使用软件编码,未启用 NVENC;
|
||||
5. 当前仍是 X11 POC,尚未完成 Weston/Wayland/DRM 零拷贝路径;
|
||||
6. USB 重定向、OTA、Agent、管理平台和 Web 控制台尚未开发完成。
|
||||
|
||||
## 九、下一步计划
|
||||
|
||||
按优先级:
|
||||
|
||||
1. 保持终端、Sunshine 和虚拟显示器三端分辨率及色彩空间一致;
|
||||
2. 将 GTX 965M 直通 Windows VM,使 Sunshine 使用 NVENC;
|
||||
3. 定义并联调终端用户认证、桌面列表和一次性连接票据 API;
|
||||
4. 实现 Ubuntu、麒麟桌面连接配置;
|
||||
5. 适配 RK3528 MPP/VPU 硬解;
|
||||
6. 切换到 Weston/Wayland 正式显示路径;
|
||||
7. 开发 `vdt-agent`、OTA、USB、云端服务和 Web 控制台;
|
||||
8. **云桌面内上网代理(规划)**:在终端登录首页提供代理配置入口,支持导入/更新 Clash 或 Mihomo 订阅、选择节点、启停和连通性测试;由云端服务将策略分别下发到 Windows、Ubuntu 和麒麟云桌面,使桌面内的浏览器和应用可以访问 GPT 等外部服务,同时为 Moonlight 串流、管理平台和必要内网地址保留稳定的直连/专用路由。该功能当前不实现、不部署;
|
||||
9. **云桌面软件白名单与商业合规(规划)**:建立按客户/桌面生效的软件允许列表、版本校验、策略下发、审计和回滚流程。Windows 与麒麟计划通过签名的系统插件或代理组件执行白名单策略;Ubuntu 计划在系统底层实施,并在定版前完成权限模型、更新机制和例外流程评审。该功能当前不实现、不部署,正式销售前必须完成授权范围和合规审查;
|
||||
10. **退出闪帧防护与盒子品牌安全画面(规划)**:解决云桌面退出、断流、客户端异常退出和 kiosk 重启时可能短暂暴露盒子底层桌面的问题。主方案是完善独立安全遮罩、X11/SDL 窗口交接并最终采用 Weston/Wayland 独占 kiosk;兜底方案是将盒子底层改为全屏公司 Logo、隐藏面板和桌面内容。该功能当前仅列入计划,不实现、不部署。
|
||||
|
||||
## 十、开发规范
|
||||
|
||||
- 已验证、已实现、待验证和规划中必须明确区分;
|
||||
- 终端不能直接调用 Proxmox API;
|
||||
- Moonlight 是当前唯一远程会话协议,连接参数由管理平台连接票据决定;
|
||||
- 所有密钥和密码只能通过安全配置注入,不得提交到 Git;
|
||||
- 正式代码变更应通过编译、真机测试和会话恢复测试;
|
||||
- 本 README 随功能、接口、目录和验证结论持续更新。
|
||||
日常直接更新对应主题,计划统一维护在 `docs/roadmap.md`;测试输出放入 `docs/evidence/<专题-日期>/`。2026-11-09 以前的零散记录及本次整理前的三份根说明完整保留在[历史归档](docs/archive/README.md)。
|
||||
|
||||
@ -1,55 +0,0 @@
|
||||
# GPU修复后刷新率与鼠标画面优化验收(2026-11-03)
|
||||
|
||||
## 结论
|
||||
|
||||
Windows虚拟屏已调整为2560×1440、60Hz,登录脚本已备份并更新;客户端默认已设为45FPS,并通过正常选择桌面入口重连。当前盒子的Xorg/EGL路径在请求60FPS时实际只能渲染约46FPS,因此不宣称稳定2K60。45FPS短测实际44.22FPS,持续测试43.14FPS;相对30FPS基线29.83FPS提高约45%~48%,排队约1.3~1.6ms。
|
||||
|
||||
## GPU复核
|
||||
|
||||
本轮实际查询NVIDIA错误码0,驱动582.78,vBIOS 84.04.2b.00.13。动态串流期间Sunshine实际创建h264_nvenc,NVIDIA有1个编码会话,检查时编码引擎约24%。运维报告的重启测试作为其提供的验收记录,本轮未自行重启VM、宿主或GPU,也未修改直通和ROM配置。
|
||||
|
||||
## 同一修正测试源对照
|
||||
|
||||
修正后的Windows测试窗口实测约59FPS,避免了原WinForms定时器将源限制到约32FPS的问题。所有测试保持2560×1440、H.264、30Mbps、RKMPP硬解;默认客户端为已有两个输入补丁的版本,第三补丁调度未启用。除同步实验外,均关闭VSync与帧步调。
|
||||
|
||||
| 档位 | 接收/渲染FPS | 主机处理平均 | 解码平均 | 帧队列平均 | 客户端队列丢弃指标 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| VDD30 / 客户端30 | 29.95 / 29.83 | 7.5ms | 6.81ms | 0.60ms | 0.41% |
|
||||
| VDD60 / 客户端60、最新帧队列 | 58.82 / 46.11 | 7.5ms | 7.37ms | 7.54ms | 21.61% |
|
||||
| VDD60 / 客户端60、原队列 | 58.81 / 46.10 | 7.6ms | 7.13ms | 36.13ms | 10.28% |
|
||||
| VDD60 / 客户端60、精确尺寸nearest实验 | 58.20 / 42.01 | 7.5ms | 6.94ms | 7.66ms | 27.77% |
|
||||
| VDD60 / 客户端60、VSync开/帧步调关 | 57.88 / 24.39 | 7.5ms | 7.26ms | 8.12ms | 57.80% |
|
||||
| VDD60 / 客户端45、最新帧队列 | 44.84 / 44.22 | 7.6ms | 8.54ms | 1.30ms | 1.38% |
|
||||
| VDD60 / 客户端45、持续测试 | 43.98 / 43.14 | 7.6ms | 7.32ms | 1.55ms | 1.91% |
|
||||
|
||||
上表“客户端队列丢弃指标”来自上游标成`Frames dropped due to network jitter`的统计,但实际字段是`pacerDroppedFrames`,本项目最新帧队列也计入其中。不能把它全部归因于网络,原队列溢出统计也并不完整;应结合接收/解码/渲染FPS及帧队列时间评估。上述会话真正的网络丢帧字段均为0%,网络延迟约2~7ms;这些是现场顺序测试,不是严格实验室基准。
|
||||
|
||||
60FPS会话采样盒子图形核心约89%~95%负载、800MHz;DDR频率已在786MHz最高公布档位,MATE合成器关闭。测试温度约62~65℃,没有通过提高频率/电压或更改CPU/GPU调速器来制造结果。原队列没有提高帧率却增加排队,nearest采样和VSync实验均未显示收益,未部署。
|
||||
|
||||
nearest实验保留在`terminal/moonlight/experiments/0004-vdi-egl-exact-size-sampling.patch`供复查,已从盒子正常源码撤回,不进入默认构建流程。正常二进制SHA256仍为`35de7b1a4ade583823b23b5f2e012307f6d0a3848c8eb97bd6d4de9660afa00f`。
|
||||
|
||||
## 配置和回退
|
||||
|
||||
- Windows实际活动VDD编号已变为DISPLAY3,工具动态查找,未写死设备号。旧DISPLAY2备份不能直接作为新设备的恢复依据,因此探测器增加`--backup-tag`区分验收批次。
|
||||
- `tools/set-2k-display.ps1`默认60Hz,支持`-RefreshRate 30`回退;已更新原VDISet2KDisplay登录任务使用的脚本,未修改任务动作或用户。
|
||||
- Windows部署前原脚本和任务XML备份:`C:\ProgramData\VDI\backups\display-refresh-20261103-142920`。部署工具按本次新生成的结果日志和任务退出码确认成功,随后独立Query确认60Hz。本轮没有重启验证登录触发。
|
||||
- `tools/run-mouse-display-probe.py Apply --hz 30 --backup-tag nvenc-20261103`可临时恢复30Hz;要保持30Hz应恢复原登录脚本,或将登录任务脚本参数设为`-RefreshRate 30`。
|
||||
- `tools/set-terminal-fps.py --fps 45 --apply`只更改盒子Windows桌面票据FPS,自动备份和原子写入,不更改其他桌面;回退用`--fps 30 --apply`。
|
||||
|
||||
部署工具前两次因Windows任务调度器LastRunTime与墙上时钟不同步而误判成功任务为失败,已自动恢复旧脚本;随后改为新结果日志证明执行新鲜度,部署成功。受配置切换影响的`nvenc45-sustained`不纳入持续测试结论,重新执行`nvenc45-sustained-final`。
|
||||
|
||||
## 持续测试和现场收尾
|
||||
|
||||
`nvenc45-sustained-final`在视频就绪后注入20秒鼠标移动和32次点击,再持续120秒,正常退出。接收43.98/渲染43.14FPS,主机平均7.6ms、解码7.32ms、排队1.55ms、呈现1.55ms,实际网络丢帧0%。这是约两分半钟的动态会话结果,不是长期稳定性保证。
|
||||
|
||||
三组Windows点击记录共256次左右交替点击,脚本校验全部符合预期,包含被排除的持续性能试验中的32次;点击正确性可核验,但其帧率数据不纳入持续性能结论。修正测试源在保存的状态中约59.4~59.8FPS,足够进行本轮对照。
|
||||
|
||||
已执行`set-terminal-fps.py --fps 45 --apply`,原30帧票据备份于盒子`~/vdi-terminal/backups/fps-20261103-143333-297521/desktops.json`。只改变Windows卡片FPS,其余桌面、分辨率和编码配置保持原值。
|
||||
|
||||
已停止并删除VDIInputLatencyBench临时任务;最终Windows查询确认`benchmark_task_present=False`。通过正常kiosk页面点击Windows连接,实际新进程PID31694带`--resolution 2560x1440 --fps 45`,RKMPP/DRM PRIME/EGL路径正常。再次查询错误码0、NVENC会话1、编码占用18%,Sunshine本次实际创建h264_nvenc。显示守护active,正常客户端二进制未换成实验版本。
|
||||
|
||||
证据目录:`docs/evidence/2k60-20261103/`,包含逐会话日志、点击记录、最终GPU/编码查询和页面截图。实际拖窗与鼠标的主观体验仍需用户现场确认;本轮自动点击和统计不能代替物理输入到屏幕测量。
|
||||
|
||||
## 后续边界
|
||||
|
||||
独立光标尚未实现,仍保留单个Windows远程白色光标。下一阶段要进一步达到稳定2K60,需要验证更低开销的显示路径及正式Weston/Wayland架构,或评估终端硬件;当前不能通过提高FPS参数或安装所谓流畅插件绕过呈现瓶颈。独立光标双方协议与单光标交接设计见`docs/independent-cursor-design-20261103.md`。本轮未测量物理输入到屏幕的P50/P95,不能称零延迟。
|
||||
37
docs/README.md
Normal file
@ -0,0 +1,37 @@
|
||||
# 项目文档索引
|
||||
|
||||
整理日期:2026-11-09。日常阅读以下 5 份正文;历史过程放入归档,避免把早期失败或临时配置误当成当前状态。
|
||||
|
||||
| 文档 | 查什么 |
|
||||
| --- | --- |
|
||||
| [真实云管接入](cloud-live-integration.md) | 正式环境启动、现场 PVE/盒子、凭据与回退 |
|
||||
| [当前状态](current-status.md) | 当前架构、三种桌面配置、已完成与未验收事项 |
|
||||
| [终端说明](terminal-guide.md) | 开机直达选择页、进入/退出交接、快捷键、显示恢复、构建入口 |
|
||||
| [云桌面说明](desktop-guide.md) | Windows/Linux 编码与画质、原生光标、电源按钮、U 盘 |
|
||||
| [运维说明](operations.md) | vdi-monitor 分工、日志位置、排障顺序、备份回退 |
|
||||
| [未来计划](roadmap.md) | 后续产品化、性能路线,以及暂不实施的 VPN、白名单、品牌画面 |
|
||||
|
||||
另保留正在讨论的[云端服务与 Web 控制台功能设计](cloud-console-functional-design-20261109.md),用于产品/UI/后端任务拆分;它是讨论稿,不属于本次清理的历史实施记录。
|
||||
|
||||
[云管开发说明](cloud-platform-development.md)记录第一阶段代码、启动方式、接口与当前限制;本机控制台使用 Go API 和 PostgreSQL;已另行完成[真实 PVE 与盒子接入](cloud-live-integration.md),正式入口为 5182。
|
||||
|
||||
[控制台 UI 接入记录](cloud-console-ui-20261109.md)记录按 `ui/` 设计稿接入的深浅主题、页面范围、验证方式与实际数据边界。
|
||||
|
||||
## 监控项目与本仓库的分工
|
||||
|
||||
`D:\DAIMA\vdi-monitor` 负责日常监控页面、指标采集与日志解读,具体维护方法见该项目的 [README](../../vdi-monitor/README.md) 和 [运维说明与已知限制](../../vdi-monitor/docs/运维说明与已知限制.md)。
|
||||
|
||||
监控当前约每 30 秒采集一次盒子数据,保存最新原始快照和状态,并保留约 7 天指标历史。这不等于完整历史日志备份,也不覆盖开发决策、测试场景、失败实验和回退配置。`vdi-cloud` 保留这些资料,监控页面的使用说明以后在 `vdi-monitor` 维护。
|
||||
|
||||
## 历史记录与原始资料
|
||||
|
||||
- [归档索引](archive/README.md):35 份原专题记录,以及整理前的根目录 README、MEMORY、PROJECT_CONTEXT,共 38 份文件,逐字节保存在 ZIP 中;索引包含原文件到新文档的对应关系和校验方式。
|
||||
- [测试证据目录](evidence/):截图、JSON、原始日志、配置快照和失败实验保留原路径,仍可供脚本输出和复查。
|
||||
- 原始需求保留为本目录的 [产品方案](VDI云终端产品方案-定版摘要(1).docx)、[软件开发方案](VDI云桌面软件开发方案(1).docx)、[云管端完整方案](VDI云桌面完整方案-云管端.docx)。它们是原始需求来源,早期协议和环境描述需结合后续已确认的实现理解。
|
||||
- [虚拟显示器配置](vdd_settings.xml) 与 [历史驱动安装辅助脚本](idd-install-helper.bat) 保留作参考,不能直接当作当前环境的一键部署方案。
|
||||
|
||||
本次清理删除的是归档后散落的 35 份 Markdown 副本,没有删除唯一测试证据,也没有因为监控项目存在而停用盒子服务。完整性结果见归档索引。
|
||||
|
||||
## 后续维护方式
|
||||
|
||||
日常变更更新对应主题文档,注明日期、实际部署状态、证据位置和限制;计划统一写入 `roadmap.md`。测试原始输出放在 `evidence/<专题-日期>/`,一般不再新增一份顶层日报文档。发生配置冲突时,以较新的部署证据核对现场,不把历史测试成功直接视为当前状态。
|
||||
85
docs/archive/README.md
Normal file
@ -0,0 +1,85 @@
|
||||
# 历史归档
|
||||
|
||||
归档日期:2026-11-09。日常查看[维护文档](../README.md),历史文件中的配置、密码提示、失败结论和“最新”字样均按原日期理解,不直接当作当前部署说明。
|
||||
|
||||
## 内容与完整性
|
||||
|
||||
[history-20261109.zip](history-20261109.zip) 按仓库相对路径保存 38 份原件:35 份 docs 专题 Markdown,以及整理前根目录 README、PROJECT_CONTEXT、MEMORY。内容未经改写,ZIP 中保留原路径。三份需求 Word、XML、BAT 和全部测试证据仍在原位置。
|
||||
|
||||
[manifest.json](manifest.json) 列出每份原件的字节数、SHA-256、新文档对应位置与 ZIP 自身 SHA-256。归档创建后已检查 ZIP CRC,并逐文件读取验证字节数及 SHA-256;删除散落副本前再次核对原文件与归档完全一致。
|
||||
|
||||
这次减少的是散落的文档副本与重复阅读入口,没有删除唯一历史结论。547 个 evidence 文件保留原路径,整理前后逐文件 SHA-256 一致。证据约 169.7 MiB,不属于此次空间清理范围。
|
||||
|
||||
## 恢复或读取
|
||||
|
||||
在仓库根目录执行以下命令,将原文解压到临时目录查阅,不覆盖当前维护文档:
|
||||
|
||||
```powershell
|
||||
Expand-Archive -LiteralPath .\docs\archive\history-20261109.zip -DestinationPath (Join-Path $env:TEMP 'vdi-cloud-docs-history-20261109')
|
||||
```
|
||||
|
||||
归档内的相对路径以原仓库为基准,单独解压的文档中部分代码/证据链接需回到本仓库查看。查历史时先按下表定位原文件,再从 ZIP 读取;新文档保留主要结论与操作入口,完整逐步过程在原件中。
|
||||
|
||||
在仓库根目录复核归档(Python):
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
import hashlib, json, zipfile
|
||||
base = Path('docs/archive')
|
||||
manifest = json.loads((base / 'manifest.json').read_text(encoding='utf-8'))
|
||||
archive = base / manifest['archive']
|
||||
assert hashlib.sha256(archive.read_bytes()).hexdigest() == manifest['sha256']
|
||||
with zipfile.ZipFile(archive) as z:
|
||||
assert z.testzip() is None
|
||||
assert len(z.namelist()) == manifest['file_count']
|
||||
for row in manifest['files']:
|
||||
data = z.read(row['path'])
|
||||
assert len(data) == row['size']
|
||||
assert hashlib.sha256(data).hexdigest() == row['sha256']
|
||||
print('38 files verified')
|
||||
```
|
||||
|
||||
## 原文件对应表
|
||||
|
||||
表中原文件名是 ZIP 内的路径,原 Markdown 副本已从 docs 顶层移除。每条都可以在 manifest 中查到独立校验值。
|
||||
|
||||
| 归档内原路径 | 日常查阅位置 |
|
||||
| --- | --- |
|
||||
| `docs/2k60-validation-20261103.md` | [云桌面说明](../desktop-guide.md#windows) |
|
||||
| `docs/display-resume-fix-20261103.md` | [终端说明](../terminal-guide.md#display) |
|
||||
| `docs/entry-surface-fix-20261109.md` | [终端说明](../terminal-guide.md#session) |
|
||||
| `docs/fixed-pci-tray-20261105.md` | [云桌面说明](../desktop-guide.md#usb) |
|
||||
| `docs/gpu-passthrough-20261030.md` | [云桌面说明](../desktop-guide.md#windows) |
|
||||
| `docs/gpu-restart-diagnosis-20261102.md` | [云桌面说明](../desktop-guide.md#windows) |
|
||||
| `docs/guest-power-policy-20261104.md` | [云桌面说明](../desktop-guide.md#power) |
|
||||
| `docs/independent-cursor-design-20261103.md` | [云桌面说明](../desktop-guide.md#cursor) |
|
||||
| `docs/independent-cursor-feasibility-20261103.md` | [云桌面说明](../desktop-guide.md#cursor) |
|
||||
| `docs/input-rendering-optimization-20261103.md` | [云桌面说明](../desktop-guide.md#cursor) |
|
||||
| `docs/kylin-mouse-20261104.md` | [云桌面说明](../desktop-guide.md#linux) |
|
||||
| `docs/kylin-quality-20261104.md` | [云桌面说明](../desktop-guide.md#linux) |
|
||||
| `docs/kylin-quality-20261106.md` | [云桌面说明](../desktop-guide.md#linux) |
|
||||
| `docs/linux-desktop-onboarding-20261104.md` | [云桌面说明](../desktop-guide.md#linux) |
|
||||
| `docs/linux-native-cursor-20261105.md` | [云桌面说明](../desktop-guide.md#cursor) |
|
||||
| `docs/moonlight-kiosk-20261028.md` | [终端说明](../terminal-guide.md#session) |
|
||||
| `docs/moonlight-rendering-profile-20261102.md` | [云桌面说明](../desktop-guide.md#windows) |
|
||||
| `docs/mouse-optimization-execution-20261103.md` | [云桌面说明](../desktop-guide.md#cursor) |
|
||||
| `docs/mouse-optimization-plan-20261103.md` | [云桌面说明](../desktop-guide.md#cursor) |
|
||||
| `docs/overnight-box-observation-20261104.md` | [运维说明](../operations.md#monitor) |
|
||||
| `docs/rdp-vs-moonlight-comparison-20261028.md` | [未来计划](../roadmap.md) |
|
||||
| `docs/rendering-execution-20261102.md` | [云桌面说明](../desktop-guide.md#windows) |
|
||||
| `docs/return-surface-fix-20261109.md` | [终端说明](../terminal-guide.md#session) |
|
||||
| `docs/single-display-20261103.md` | [云桌面说明](../desktop-guide.md#windows) |
|
||||
| `docs/sunshine-vm-setup-for-colleague.md` | [云桌面说明](../desktop-guide.md#windows) |
|
||||
| `docs/ubuntu-deep-latency-20261105.md` | [云桌面说明](../desktop-guide.md#linux) |
|
||||
| `docs/ubuntu-drag-render-20261105.md` | [云桌面说明](../desktop-guide.md#linux) |
|
||||
| `docs/ubuntu-latency-plan-20261106.md` | [未来计划](../roadmap.md#performance) |
|
||||
| `docs/ubuntu-mouse-release-20261106.md` | [云桌面说明](../desktop-guide.md#cursor) |
|
||||
| `docs/ubuntu-quality-20261105.md` | [云桌面说明](../desktop-guide.md#linux) |
|
||||
| `docs/ubuntu-vram-20261106.md` | [云桌面说明](../desktop-guide.md#linux) |
|
||||
| `docs/usb-any-storage-20261104.md` | [云桌面说明](../desktop-guide.md#usb) |
|
||||
| `docs/usb-device-poc-20261103.md` | [云桌面说明](../desktop-guide.md#usb) |
|
||||
| `docs/web-console-mvp-plan-20261028.md` | [未来计划](../roadmap.md) |
|
||||
| `docs/windows-unexpected-exit-20261104.md` | [运维说明](../operations.md#diagnosis) |
|
||||
| `README.md` | [当前入口](../../README.md) |
|
||||
| `PROJECT_CONTEXT.md` | [当前入口](../../PROJECT_CONTEXT.md) |
|
||||
| `MEMORY.md` | [当前入口](../../MEMORY.md) |
|
||||
BIN
docs/archive/history-20261109.zip
Normal file
238
docs/archive/manifest.json
Normal file
@ -0,0 +1,238 @@
|
||||
{
|
||||
"created": "2026-11-09",
|
||||
"archive": "history-20261109.zip",
|
||||
"sha256": "2630dbf89e358dbd42790de1765b2a285d295cac9f9214f9cc34f7baf839278c",
|
||||
"file_count": 38,
|
||||
"files": [
|
||||
{
|
||||
"path": "docs/2k60-validation-20261103.md",
|
||||
"size": 6363,
|
||||
"sha256": "df2aec506b0f604d56b6b26196ec393d85d188dce7a52c708fe4d9991f93f6eb"
|
||||
},
|
||||
{
|
||||
"path": "docs/display-resume-fix-20261103.md",
|
||||
"size": 5221,
|
||||
"sha256": "e3368c4c74cea6e8d99108f013f455f31613d24486c9ac653a02561876bb8a1c"
|
||||
},
|
||||
{
|
||||
"path": "docs/entry-surface-fix-20261109.md",
|
||||
"size": 4093,
|
||||
"sha256": "8a6b50cb40e1362a42032004858084f10ba04ce12ab817077154a84c05026016"
|
||||
},
|
||||
{
|
||||
"path": "docs/fixed-pci-tray-20261105.md",
|
||||
"size": 5222,
|
||||
"sha256": "6c14d19b89131893c20994a944d0ef42c9a2743c37f0d0846ce1e3806b34cdf1"
|
||||
},
|
||||
{
|
||||
"path": "docs/gpu-passthrough-20261030.md",
|
||||
"size": 5657,
|
||||
"sha256": "5c3fbd00141ac7540db76e45e28f6a8c65ff8f3005f4edb8ad640ff99b06d239"
|
||||
},
|
||||
{
|
||||
"path": "docs/gpu-restart-diagnosis-20261102.md",
|
||||
"size": 3566,
|
||||
"sha256": "a09f0c92e58890056573b9f59faebce4084dc779d5125d5e8f77093208cf72a8"
|
||||
},
|
||||
{
|
||||
"path": "docs/guest-power-policy-20261104.md",
|
||||
"size": 4569,
|
||||
"sha256": "aca2a267c349ee42ff6f44ee2cb8be44f739d7bfac222160e1368eb03360d4f8"
|
||||
},
|
||||
{
|
||||
"path": "docs/independent-cursor-design-20261103.md",
|
||||
"size": 6189,
|
||||
"sha256": "b0c5ea8d7178609da9c778cea63c3f612bd659b557ec4d752084503b43aebe7c"
|
||||
},
|
||||
{
|
||||
"path": "docs/independent-cursor-feasibility-20261103.md",
|
||||
"size": 7960,
|
||||
"sha256": "1124adb59d9d392a4bcbe52749d92c748c248cb3c9da98388985ad05bdb6efde"
|
||||
},
|
||||
{
|
||||
"path": "docs/input-rendering-optimization-20261103.md",
|
||||
"size": 6356,
|
||||
"sha256": "cd292c5a1e4b122857fa51bfda66c08496faf9186a39f68acdd42b3d75a53bea"
|
||||
},
|
||||
{
|
||||
"path": "docs/kylin-mouse-20261104.md",
|
||||
"size": 6394,
|
||||
"sha256": "93c2737b197cb6c65b7af3ff51a10a5e94d34e754bae67f12483346c20943dc1"
|
||||
},
|
||||
{
|
||||
"path": "docs/kylin-quality-20261104.md",
|
||||
"size": 6553,
|
||||
"sha256": "d1d145dcb6244b8d4414e4fef28c499fddc47f242582583a74504da83331f38e"
|
||||
},
|
||||
{
|
||||
"path": "docs/kylin-quality-20261106.md",
|
||||
"size": 1910,
|
||||
"sha256": "e6ccc96fee07089bebd58e1c5af575aa81561bfbffab91e07ce2d14f15fd2957"
|
||||
},
|
||||
{
|
||||
"path": "docs/linux-desktop-onboarding-20261104.md",
|
||||
"size": 8501,
|
||||
"sha256": "bcc8400f20231f6cedaebf83d7b163a162cfe3df16310de278a17811ea7f7f5c"
|
||||
},
|
||||
{
|
||||
"path": "docs/linux-native-cursor-20261105.md",
|
||||
"size": 12273,
|
||||
"sha256": "b57101a8dee2faccc5bb52b5a0e434645530f4d792c8f0b76d4ffc5a75f47e62"
|
||||
},
|
||||
{
|
||||
"path": "docs/moonlight-kiosk-20261028.md",
|
||||
"size": 22237,
|
||||
"sha256": "af481932ed9e2151081ef187981a788669526277190bde93615aa45753cfcbf4"
|
||||
},
|
||||
{
|
||||
"path": "docs/moonlight-rendering-profile-20261102.md",
|
||||
"size": 3595,
|
||||
"sha256": "b6fbd7b64730593b03f32a242f9ac04089385eb78d8f8c9a0a5b330c4e043bd6"
|
||||
},
|
||||
{
|
||||
"path": "docs/mouse-optimization-execution-20261103.md",
|
||||
"size": 6735,
|
||||
"sha256": "7e4a97c3a64f7e333c8c22f97b4c0ee82b3bf61a5358764e8e0454dbbb918d8d"
|
||||
},
|
||||
{
|
||||
"path": "docs/mouse-optimization-plan-20261103.md",
|
||||
"size": 4473,
|
||||
"sha256": "ef979b20d8c234ab0756888fd8eb7eef3e70d619bcfe401e41902cf2424f4a9c"
|
||||
},
|
||||
{
|
||||
"path": "docs/overnight-box-observation-20261104.md",
|
||||
"size": 4899,
|
||||
"sha256": "1b6f16a052d08373e774499844e45a92c7db700c7be731ba2f2c89bcc1970643"
|
||||
},
|
||||
{
|
||||
"path": "docs/rdp-vs-moonlight-comparison-20261028.md",
|
||||
"size": 4218,
|
||||
"sha256": "096c675df33fe269c1ebc6b22ac458e9c60c9009ee0a01c0e71ddf44f22a8226"
|
||||
},
|
||||
{
|
||||
"path": "docs/rendering-execution-20261102.md",
|
||||
"size": 10440,
|
||||
"sha256": "8e1dba5a7c9e0753907cfad882084d1d1f1a2861b0a4300bdbaaeeb6b5df4ada"
|
||||
},
|
||||
{
|
||||
"path": "docs/return-surface-fix-20261109.md",
|
||||
"size": 3100,
|
||||
"sha256": "0646f6f09185e6f5ea67d007dd89e64ade35e4fd2ad0c6d40396984ece43bb01"
|
||||
},
|
||||
{
|
||||
"path": "docs/single-display-20261103.md",
|
||||
"size": 3293,
|
||||
"sha256": "a0739849c641997b10599a153c829cbb2d6c756f19fb5cb42a42adb517ce4392"
|
||||
},
|
||||
{
|
||||
"path": "docs/sunshine-vm-setup-for-colleague.md",
|
||||
"size": 2979,
|
||||
"sha256": "d0fc2d0f32e538b01331311545aeb0fd0e0ab92b8b82a0e3082404453163a1f5"
|
||||
},
|
||||
{
|
||||
"path": "docs/ubuntu-deep-latency-20261105.md",
|
||||
"size": 2498,
|
||||
"sha256": "09cd5591762d05762455a1cf9566cad84f6949cb1a59da969f25e7e08c8c09ec"
|
||||
},
|
||||
{
|
||||
"path": "docs/ubuntu-drag-render-20261105.md",
|
||||
"size": 7696,
|
||||
"sha256": "5731bbe457794575d9ef2d451ab9a9840e2fc8be8af2e73bb43f39bb34d04ed7"
|
||||
},
|
||||
{
|
||||
"path": "docs/ubuntu-latency-plan-20261106.md",
|
||||
"size": 6157,
|
||||
"sha256": "27cdc6907e45f9086572fb4f0b4b7ced19d6a5b222dd364276b27ee321038098"
|
||||
},
|
||||
{
|
||||
"path": "docs/ubuntu-mouse-release-20261106.md",
|
||||
"size": 1725,
|
||||
"sha256": "73b76b19e6f01282f3c2e4b74c3fe2fb3743f901abef0438c024497d18fcdd3e"
|
||||
},
|
||||
{
|
||||
"path": "docs/ubuntu-quality-20261105.md",
|
||||
"size": 7334,
|
||||
"sha256": "fa44aa64b99ded46dfc2dbba8275ed1c48299ff1800a5d7872b65a3bc24b80a8"
|
||||
},
|
||||
{
|
||||
"path": "docs/ubuntu-vram-20261106.md",
|
||||
"size": 1553,
|
||||
"sha256": "d8afa6409c6407adee9b47540ac1d9c270dcab607292bae9003be570601b47b8"
|
||||
},
|
||||
{
|
||||
"path": "docs/usb-any-storage-20261104.md",
|
||||
"size": 6219,
|
||||
"sha256": "912e60cd9f590b89d7a6236116f142e5a4f0d5a8ee60f759d20090fb4238fde9"
|
||||
},
|
||||
{
|
||||
"path": "docs/usb-device-poc-20261103.md",
|
||||
"size": 6604,
|
||||
"sha256": "63052ea7f4df222fa8093c1c85afbd532ce3b509fcf0f8d4a1a0de1ae76cdc9d"
|
||||
},
|
||||
{
|
||||
"path": "docs/web-console-mvp-plan-20261028.md",
|
||||
"size": 10122,
|
||||
"sha256": "ef129f7dab233af52b1beb57b8853a620679f3d2622d334dc9cd17443c0c1196"
|
||||
},
|
||||
{
|
||||
"path": "docs/windows-unexpected-exit-20261104.md",
|
||||
"size": 2269,
|
||||
"sha256": "9721d09d718fbc76762d8ccd31c54d4318ee8124c845861b06072f5839d4b2ff"
|
||||
},
|
||||
{
|
||||
"path": "README.md",
|
||||
"size": 11694,
|
||||
"sha256": "4824cb7311cc8180b0d1fc2755899c524f48c1fe44d4fc4389d32a99b9d04316"
|
||||
},
|
||||
{
|
||||
"path": "PROJECT_CONTEXT.md",
|
||||
"size": 16479,
|
||||
"sha256": "a27159a82e5cc236f242eb0b88cfb894cadfb6e44204643e94bcafe5c2687745"
|
||||
},
|
||||
{
|
||||
"path": "MEMORY.md",
|
||||
"size": 37521,
|
||||
"sha256": "7cd14ff3b7b86716ae3db629cefb834024064d81632841400cd30b7d3b8399f1"
|
||||
}
|
||||
],
|
||||
"consolidated_to": {
|
||||
"docs/2k60-validation-20261103.md": "docs/desktop-guide.md#windows",
|
||||
"docs/display-resume-fix-20261103.md": "docs/terminal-guide.md#display",
|
||||
"docs/entry-surface-fix-20261109.md": "docs/terminal-guide.md#session",
|
||||
"docs/fixed-pci-tray-20261105.md": "docs/desktop-guide.md#usb",
|
||||
"docs/gpu-passthrough-20261030.md": "docs/desktop-guide.md#windows",
|
||||
"docs/gpu-restart-diagnosis-20261102.md": "docs/desktop-guide.md#windows",
|
||||
"docs/guest-power-policy-20261104.md": "docs/desktop-guide.md#power",
|
||||
"docs/independent-cursor-design-20261103.md": "docs/desktop-guide.md#cursor",
|
||||
"docs/independent-cursor-feasibility-20261103.md": "docs/desktop-guide.md#cursor",
|
||||
"docs/input-rendering-optimization-20261103.md": "docs/desktop-guide.md#cursor",
|
||||
"docs/kylin-mouse-20261104.md": "docs/desktop-guide.md#linux",
|
||||
"docs/kylin-quality-20261104.md": "docs/desktop-guide.md#linux",
|
||||
"docs/kylin-quality-20261106.md": "docs/desktop-guide.md#linux",
|
||||
"docs/linux-desktop-onboarding-20261104.md": "docs/desktop-guide.md#linux",
|
||||
"docs/linux-native-cursor-20261105.md": "docs/desktop-guide.md#cursor",
|
||||
"docs/moonlight-kiosk-20261028.md": "docs/terminal-guide.md#session",
|
||||
"docs/moonlight-rendering-profile-20261102.md": "docs/desktop-guide.md#windows",
|
||||
"docs/mouse-optimization-execution-20261103.md": "docs/desktop-guide.md#cursor",
|
||||
"docs/mouse-optimization-plan-20261103.md": "docs/desktop-guide.md#cursor",
|
||||
"docs/overnight-box-observation-20261104.md": "docs/operations.md#monitor",
|
||||
"docs/rdp-vs-moonlight-comparison-20261028.md": "docs/roadmap.md",
|
||||
"docs/rendering-execution-20261102.md": "docs/desktop-guide.md#windows",
|
||||
"docs/return-surface-fix-20261109.md": "docs/terminal-guide.md#session",
|
||||
"docs/single-display-20261103.md": "docs/desktop-guide.md#windows",
|
||||
"docs/sunshine-vm-setup-for-colleague.md": "docs/desktop-guide.md#windows",
|
||||
"docs/ubuntu-deep-latency-20261105.md": "docs/desktop-guide.md#linux",
|
||||
"docs/ubuntu-drag-render-20261105.md": "docs/desktop-guide.md#linux",
|
||||
"docs/ubuntu-latency-plan-20261106.md": "docs/roadmap.md#performance",
|
||||
"docs/ubuntu-mouse-release-20261106.md": "docs/desktop-guide.md#cursor",
|
||||
"docs/ubuntu-quality-20261105.md": "docs/desktop-guide.md#linux",
|
||||
"docs/ubuntu-vram-20261106.md": "docs/desktop-guide.md#linux",
|
||||
"docs/usb-any-storage-20261104.md": "docs/desktop-guide.md#usb",
|
||||
"docs/usb-device-poc-20261103.md": "docs/desktop-guide.md#usb",
|
||||
"docs/web-console-mvp-plan-20261028.md": "docs/roadmap.md",
|
||||
"docs/windows-unexpected-exit-20261104.md": "docs/operations.md#diagnosis",
|
||||
"README.md": "README.md",
|
||||
"PROJECT_CONTEXT.md": "PROJECT_CONTEXT.md",
|
||||
"MEMORY.md": "MEMORY.md"
|
||||
}
|
||||
}
|
||||
165
docs/cloud-console-functional-design-20261109.md
Normal file
@ -0,0 +1,165 @@
|
||||
# VDI 云端服务与 Web 控制台功能设计
|
||||
|
||||
版本:v0.1 讨论稿 · 日期:2026-11-09 · 用途:产品范围讨论、UI 页面设计和后端任务拆分。
|
||||
|
||||
本稿保留设计时的现状基线。11-09 后续已开始第一阶段实现,最新代码能力、启动方式和未完成边界见[云管开发说明](cloud-platform-development.md)。
|
||||
|
||||
建议将产品定位为“基于 Proxmox 的云桌面业务管理平台”。管理员在我们的控制台完成桌面发放、终端管理和运维,平台在后端调用 Proxmox 等组件。画质和延迟优化继续独立推进,本轮不改串流链路。
|
||||
|
||||
## 1. 文档依据与当前状态
|
||||
|
||||
- `VDI云桌面软件开发方案(1).docx`:第 4 章七个云端服务,第 5 章控制台页面与三角色,以及 R8–R20 中的平台相关需求。
|
||||
- `VDI云桌面完整方案-云管端.docx`:第 3 章虚拟化、模板、桌面池与 GPU,第 4 章管理平台。
|
||||
- `VDI云终端产品方案-定版摘要(1).docx`:第 8 章“管终端 + 管桌面”的最小闭环。
|
||||
- `web-console-mvp-plan-20261028.md`:既有控制台 MVP、接口和 Mock 策略,原件见[历史归档](archive/README.md),日常计划见[未来计划](roadmap.md)。
|
||||
- 仓库现状:`platform/`、`console/` 目前仅有占位文件;终端已有桌面列表和 Moonlight 接入,平台认证、注册、授权和连接票据尚未接通。
|
||||
|
||||
以下是基于原文档整理的功能建议,不代表已经实现或替换定版需求。页面原型全部采用演示数据。
|
||||
|
||||
## 2. “类似 Proxmox”具体做什么
|
||||
|
||||
```text
|
||||
管理员浏览器 → 自研 Web 控制台 → 自研 Go 平台 → Proxmox API → VM / 模板 / 节点
|
||||
↕
|
||||
PostgreSQL / 指标存储
|
||||
↕ HTTPS + MQTT
|
||||
终端 Agent 与 Kiosk
|
||||
|
||||
实际桌面画面:终端 Moonlight ↔ VM 内 Sunshine
|
||||
```
|
||||
|
||||
控制台提供面向云桌面的资源视图和管理操作。虚拟机运行、克隆、节点资源查询复用 Proxmox;用户能用哪些桌面、哪台盒子归谁、终端配置、命令执行、会话记录由平台维护。管理员控制台与盒子的桌面选择页是两个产品界面。
|
||||
|
||||
| 能力 | 现成组件提供什么 | 我们需要开发什么 |
|
||||
| --- | --- | --- |
|
||||
| 节点与 VM | Proxmox 提供资源查询、开关机、配置、模板和克隆等 API | 接入配置、资源同步、桌面业务映射、任务跟踪和页面 |
|
||||
| 桌面池 | Proxmox 提供 VM、模板、克隆、快照等基础能力 | 公共/专属池、容量、分配、占用、回收和还原编排 |
|
||||
| 用户与授权 | 可选 AD/LDAP 提供身份来源 | 平台账号、管理权限、用户/终端/桌面授权关系 |
|
||||
| 终端管理 | MQTT 提供消息传输 | Agent 注册、设备认证、心跳、分组、配置、命令回执 |
|
||||
| GPU 管理 | Proxmox 提供 PCI 配置;驱动工具提供设备信息 | 卡清单、独占占用、绑定校验、失败处理、健康状态 |
|
||||
| 监控 | Proxmox 提供节点/VM 指标;VictoriaMetrics 保存时序数据 | Agent 会话采样、采集汇总、告警规则和查询权限 |
|
||||
| OTA | 对象存储提供包下载 | 签名元信息、版本、灰度批次、Agent 安装和 A/B 回滚闭环 |
|
||||
| 桌面连接 | Moonlight/Sunshine 提供串流 | 授权检查、端点发现、会话跟踪及配对/连接凭据集成 |
|
||||
|
||||
文档中 `/api/v1/...` 是自研平台的接口设计,不是 Proxmox 已经提供的同名接口。现有其他外部系统如能提供其中某部分,应以其真实接口清单、认证方式、版本和测试环境为准;目前仓库未见完整清单。
|
||||
|
||||
Proxmox 的 REST/JSON API 支持第三方管理系统集成,依据:[Proxmox 官方管理手册](https://pve.proxmox.com/pve-docs/pve-admin-guide.pdf)。具体端点和权限以现场版本 API Viewer 为准。
|
||||
|
||||
## 3. 第一版先交付什么
|
||||
|
||||
第一版验收场景:**接入 Proxmox → 纳管已有桌面 → 注册终端 → 授权桌面 → 盒子获取列表并连接 → 控制台查看状态 → 执行操作并追踪结果。**
|
||||
|
||||
| 阶段 | 功能范围 | 完成标准 |
|
||||
| --- | --- | --- |
|
||||
| A 基础闭环 | 管理员登录与三角色;接入一个 Proxmox 集群;同步节点和已有 VM;终端注册心跳;桌面授权;桌面启停;任务与审计;总览 | 一台真实盒子通过平台获得授权桌面并连接;管理员操作有真实最终回执 |
|
||||
| B 发放与运维 | 模板登记与检查;克隆发放;桌面池;配置版本下发;日志收集;监控告警;GPU 清单和经验证的独占绑定 | 从模板发放一台可用桌面;执行配置下发;可查故障原因和 GPU 占用 |
|
||||
| C 完整交付 | OTA 签名包与灰度/A-B 回滚;CSV 导入;公共池还原;可选 AD/LDAP;跨网段接入 | OTA 失败可回滚、公共池按策略回收;各可选能力单独完成联调验收 |
|
||||
|
||||
这是建议的实施顺序,不覆盖原文档 M1–M4 的项目里程碑。阶段 A 先纳管现有 Windows/Ubuntu/麒麟 VM,减少等待模板自动化的依赖;B/C 在首轮页面原型中保留功能入口。
|
||||
|
||||
## 4. 控制台页面清单
|
||||
|
||||
整体采用“左侧导航 + 顶部账号栏 + 主内容区 + 全局任务入口”。资源列表优先,详情使用详情区或抽屉;UI 同事负责视觉稿、品牌、颜色和组件规范。
|
||||
|
||||
| 导航 | 页面展示的核心信息 | 主要操作 | 阶段 |
|
||||
| --- | --- | --- | --- |
|
||||
| 总览 | 在线终端/总数、运行桌面/总数、活跃会话、待处理告警、节点水位、最近失败任务 | 点击指标进入对应列表;进入任务详情 | A |
|
||||
| 计算资源 | 集群、节点状态、CPU/内存/存储水位、最近同步时间、接口连通性 | 接入 Proxmox、验证连接、同步资源;节点详情 | A |
|
||||
| 桌面管理 / 桌面 | 名称、OS、VMID、节点、池、所有者、VM 电源状态、连接服务状态、当前会话 | 纳管已有 VM、从模板创建、授权、开机、正常关机、重启、还原镜像 | A/B/C |
|
||||
| 桌面管理 / 桌面池 | 公共/专属、模板版本、总量/空闲/占用、最大容量、回收方式 | 新建、扩容、发放、回收、维护 | B/C |
|
||||
| 桌面管理 / 镜像模板 | Windows/Ubuntu/麒麟、版本、源 VM、适用节点/存储、连接服务检查 | 登记已有模板、验证、发布/停用 | B |
|
||||
| 终端管理 | SN、名称、分组、网络在线状态、会话状态、IP、系统/Agent 版本、最近心跳 | 激活/停用、改分组、授权、重启盒子、重置会话、配置下发、收集日志 | A/B |
|
||||
| 用户与授权 | 终端用户、启用状态、可用桌面、关联终端;管理员账号与角色另设子页 | 创建/停用、分配/撤销桌面、终端关联;CSV 导入 | A/C |
|
||||
| GPU 资源 | 厂商/型号、节点、PCI 地址、健康、可分配状态、占用桌面 | 查询、绑定、解绑、维护;实际温度/显存按采集能力展示 | B |
|
||||
| 监控告警 | 会话数、FPS、网络 RTT、终端温度;节点水位;告警级别/对象/时间 | 时间范围筛选、查看会话、确认告警、规则配置 | B |
|
||||
| OTA 中心 | 版本/签名状态/适配机型、批次、目标终端、成功/失败/回滚数量 | 登记升级包、测试批次、灰度发布、暂停/继续、查失败设备 | C |
|
||||
| 任务与审计 | 任务类型、目标、操作者、提交/开始/结束时间、状态、错误;审计记录 | 查询、展开执行日志、按权限重试可重试任务 | A |
|
||||
| 系统设置 | 平台管理员、角色、Proxmox 接入、配置模板;可选身份源 | 维护权限和接入;检查依赖服务 | A/B/C |
|
||||
|
||||
补充基础页面:管理员登录、登录过期、无权限、接口异常、空列表。PVE 不可达时显示最后同步时间和“数据可能过期”,不能继续展示为实时正常。
|
||||
|
||||
### 4.1 桌面列表布局
|
||||
|
||||
```text
|
||||
桌面管理 [纳管已有 VM] [从模板创建]
|
||||
[名称/VMID 搜索] [OS] [电源状态] [桌面池] [查询]
|
||||
名称 OS 节点 电源 连接服务 授权用户 操作
|
||||
设计桌面 Windows node-a 运行中 可连接 张三 详情 / 更多
|
||||
办公桌面 Ubuntu node-a 已关机 未就绪 李四 详情 / 开机
|
||||
|
||||
详情:基本信息 | 资源配置 | 用户与终端 | 会话 | 任务记录
|
||||
底部/全局任务入口:克隆 / 启停 / 配置下发的执行状态和错误原因
|
||||
```
|
||||
|
||||
“重置会话”只结束终端连接;“重启桌面”重启 VM;“还原镜像”会回退系统盘数据,三者分别命名。PVE `status/reset` 是硬重启,不等于还原镜像。还原实现依赖已经验证的快照或重建流程,并明确用户数据盘是否保留。
|
||||
|
||||
### 4.2 创建与发放桌面
|
||||
|
||||
向导字段:桌面名称 → OS/模板版本 → 目标节点/存储 → CPU/内存/磁盘规格 → 专属/公共池 → 可选 GPU → 可选授权用户 → 确认创建。
|
||||
|
||||
检查项:模板能否在目标存储克隆、容量是否充足、GPU 是否空闲、连接服务是否已预装。完成克隆之后,还需要 VM 内机器标识/账号/IP 初始化、Sunshine 服务可用性与配对准备检查;不能把“VM 克隆成功”直接显示成“桌面可连接”。
|
||||
|
||||
### 4.3 用户 终端 桌面的关系
|
||||
|
||||
- 平台管理员账号与最终桌面使用者分开管理,管理员角色不自动获得桌面使用权限。
|
||||
- 用户可以获得多个桌面;终端显示当前身份有权使用的列表,保留现有 Windows/Ubuntu/麒麟选择能力。
|
||||
- 第一版建议保留设备绑定模式:终端注册认证后按设备授权取得列表;后续可启用账号登录模式。SN 是标识,预置密钥等才用于设备身份验证。
|
||||
- 一个盒子同时只允许一个活动桌面会话;专属桌面同一时刻的占用规则由平台检查。
|
||||
- 修改授权后刷新列表;撤销授权至少阻止后续连接,是否立即中断现有会话需形成明确策略。
|
||||
|
||||
## 5. 云端服务怎么落地
|
||||
|
||||
沿用正式文档的 Go、PostgreSQL、VictoriaMetrics、HTTPS/MQTT 和七个服务职责。首期优先实现 A 阶段路径,部署数量按原方案执行;本稿不将微服务部署形态另行改成定版结论。
|
||||
|
||||
| 服务 | 首期开发职责 |
|
||||
| --- | --- |
|
||||
| api-gateway | 统一 API、管理员认证/权限、请求 ID、审计、错误码 |
|
||||
| auth-svc | 管理账号、终端用户、授权关系;设备/用户连接授权检查 |
|
||||
| terminal-svc | 注册、在线判断、设备列表、配置版本、命令与回执 |
|
||||
| desktop-svc | PVE 接入、资源同步、VM 纳管与启停、异步任务;随后增加模板和桌面池 |
|
||||
| mqtt-bus | 心跳消费、命令投递、seq/commandId 去重、回执关联 |
|
||||
| metric-svc | 首期基础状态汇总;随后增加会话指标和告警 |
|
||||
| gpu-broker | 后续接卡清单、独占分配、直通配置和健康探测 |
|
||||
|
||||
业务表沿用原文档的 terminal、desktop、pool、user、binding、gpu_card、config_template、ota_batch、session_log;补充平台接入记录、task、command、audit_log。desktop 保存 PVE 集群/节点/VMID 映射;GPU 锁定和公共池占用必须在后端保证原子性。
|
||||
|
||||
状态分别存储:终端网络状态、终端会话状态、VM 电源状态、VM 内连接服务状态、任务状态;避免“VM 开机 = Sunshine 就绪 = 用户已连接”的错误推断。
|
||||
|
||||
## 6. 接口先定哪些
|
||||
|
||||
原文档已有接口语义优先沿用,下面的“建议补充”路径是讨论用契约,实施前统一到 OpenAPI。旧文档中 singular/plural、监控路径和方法存在差异,需要统一,不能同时做成多套业务逻辑。
|
||||
|
||||
| 接口组 | 原文档已有 | 建议补充的能力 |
|
||||
| --- | --- | --- |
|
||||
| 身份与权限 | `/api/v1/auth/login`、用户 CRUD | 当前账号/退出、角色、终端身份验证、授权查询 |
|
||||
| PVE 接入 | PVE client 职责 | 接入配置、验证连接、资源同步与纳管;接入凭据仅后端保管 |
|
||||
| 终端 | `/terminal/register`、`/terminal/{id}/config`、`/terminal/{id}/status` | 管理列表/详情、远程命令、回执、配置版本 |
|
||||
| 桌面和池 | `/desktop/assign`、`/pool`、`/pool/{id}/action` | 桌面列表/详情、VM 纳管、模板、单桌面动作 |
|
||||
| GPU | `/gpu/cards`、`/gpu/bind` | 独占校验、解绑、健康状态 |
|
||||
| 任务和审计 | 原文档有审计要求 | `GET /api/v1/tasks/{id}`、任务列表、审计列表 |
|
||||
| 终端连接 | 旧 MVP 提出终端登录/列表/票据 | 当前身份可用桌面、连接申请、短期凭据及会话事件 |
|
||||
| 监控和 OTA | `/monitor/query`、`/ota/manifest` | 受权限约束的指标查询、告警、版本/批次管理 |
|
||||
|
||||
命令型请求返回 `taskId` 或 `commandId`。页面先显示“已提交”,随后显示“排队/执行中/成功/失败/超时”;按 PVE 任务或 Agent 回执确认最终结果。超时表示结果尚未确认,需要核对实际状态后再决定能否重试;不自动重放克隆、还原等操作。MQTT QoS1 仍需业务去重。
|
||||
|
||||
连接票据是平台授权的一部分,不能凭空替代 Moonlight/Sunshine 配对和协议认证。需要单独验证“平台放行 → 终端可安全连接”的桥接方式。Guacamole/RDP 的 443 网关也不能直接作为当前 Moonlight 的网关方案。本轮先以局域网的现有串流链路接平台。
|
||||
|
||||
监控界面把指标写清楚:网络 RTT、解码耗时、FPS 分开;未采集的显示“暂无数据”,不填 0,不把 RTT 标成整体输入延迟。SN、令牌和配对信息不出现在审计正文。
|
||||
|
||||
## 7. UI 同事可以据此出什么稿
|
||||
|
||||
建议先出:应用框架/登录、桌面列表与详情、终端列表与详情、授权弹窗、创建桌面向导、任务详情六组主稿,再复用表格/表单扩展其他模块。
|
||||
|
||||
通用组件:筛选栏、状态标签、资源表格、详情抽屉、批量操作条、确认弹窗、步骤表单、任务进度、空态/错误态。规格包括正常/加载/无数据/失败/无权限,以及桌面运行中/关机/创建中/故障等状态。
|
||||
|
||||
权限沿用三角色:超级管理员负责接入、用户授权与 GPU;运维负责常规设备/桌面操作和升级;只读账号只查看。最终按服务端 RBAC 执行,原型的角色切换仅用于交互演示。
|
||||
|
||||
附带功能原型展示十个导航模块及桌面操作、终端命令、授权和任务回执。所有变更只影响演示数据,UI 可整体替换视觉;阶段 B/C 的原型页面不代表对应后台已经可用。
|
||||
|
||||
## 8. 开工前需要形成结论的三个问题
|
||||
|
||||
1. 外部接口清单:除了 PVE,是否还有既有账号、设备管理、OTA 或 GPU 调度服务?提供方和责任边界是什么?
|
||||
2. 身份模式:首发保持“盒子绑定后自动显示桌面”,还是增加“使用者输入账号登录”?两者页面和认证接口不同。
|
||||
3. 首发范围:建议 A 阶段作为首轮联调,B/C 作为后续功能包;是否有客户必须首发验收的模板、公共池或 OTA 功能?
|
||||
|
||||
这些问题不影响当前功能稿和原型评审;真实接口联调和最终排期以结论为准。
|
||||
47
docs/cloud-console-ui-20261109.md
Normal file
@ -0,0 +1,47 @@
|
||||
# 管理控制台 UI 接入记录
|
||||
|
||||
日期:2026-11-09。设计来源为仓库提交 `8276da0` 的 `ui/index.html` 和 `ui/design-spec.md`。本次将后台设计接入 `console/` 的真实 Vue 应用;`ui/terminal/` 仍是终端设计参考,本次没有部署终端界面改动。
|
||||
|
||||
## 页面与样式
|
||||
|
||||
复用设计稿品牌标识、蜂蜜金橙主色、深浅背景色、18px 圆角、卡片、左侧导航及账号区域。补充页面使用同一套 token 与 Element Plus 图标。Ubuntu、麒麟图标复用设计资源;品牌资源的源文件仍保留在 `ui/`。
|
||||
|
||||
| 页面 | 当前接入 |
|
||||
| --- | --- |
|
||||
| 登录 | 品牌区域、管理员表单、错误提示、主题切换 |
|
||||
| 总览 | 实际运行桌面、活动连接、已上报 RTT、异常任务、节点水位、最近任务;导出当前汇总 CSV |
|
||||
| 桌面管理 | 按设计稿使用系统卡片,保留列表视图、筛选、详情、纳管和电源操作 |
|
||||
| 远程会话 | 在线设备的连接预约和已连接会话、已上报 FPS/RTT、终端信息、远程断开确认和任务回执 |
|
||||
| 终端管理 | 登记、激活信息、列表、详情、远程命令弹窗统一主题 |
|
||||
| 计算资源 | 节点表格、水位与过期状态统一主题 |
|
||||
| 用户与授权 | 用户列表、创建、绑定与授权弹窗统一主题 |
|
||||
| 任务与审计 | 列表、筛选、任务详情和诊断下载统一主题 |
|
||||
| 系统设置 | 分类导航、平台接入、权限矩阵、已接入与待开发能力 |
|
||||
|
||||
首次访问默认深色,可切换为浅色;选择保存在本浏览器的 `vdi.theme` 中。窄屏使用折叠侧栏,表格在组件内横向滚动。主题覆盖表格、表单、下拉菜单、弹窗、详情抽屉、确认框和错误状态。
|
||||
|
||||
## 数据和操作边界
|
||||
|
||||
设计稿中的 128 台桌面、18ms、60FPS、趋势图与 GPU 使用率没有复制到真实应用。未采集指标显示“暂无数据”,历史使用趋势尚无存储来源,因此总览相应位置展示真实节点水位。帧率请求参数与实测 FPS 分开展示。
|
||||
|
||||
远程会话页复用现有 `/terminals`、`/desktops`、`/users` 和任务接口。仅在线设备的 `connecting` / `connected` 状态显示在会话页;连接预约不计为实际已连接。断开操作使用现有 `reset-session`,提交后等待设备回执。管理端没有接入远程画面采集,因此预览区明确显示“画面预览未接入”。
|
||||
|
||||
本次没有新增桌面池、克隆、GPU 调度、策略下发或 OTA 后端;系统设置的功能范围显示这些待开发项。没有增加浏览器串流连接按钮或可保存但不生效的策略开关。
|
||||
|
||||
## 验证
|
||||
|
||||
- 前端类型检查、ESLint、3 项已有单元测试、生产构建通过。
|
||||
- 4 项隔离的 Playwright UI 测试通过:会话指标为空、取消与确认断开、只读角色、连接预约、主题持久化和窄屏导航。命令由浏览器测试接口拦截,不发送到真实盒子。
|
||||
- 使用现场真实账号验证 8 个后台页面的深浅主题,在 1440px 和 390px 下无整页横向溢出;检查卡片/列表、空态、详情抽屉、登记弹窗、权限页、快照下载和退出登录。测试没有提交 PVE 电源或设备操作。
|
||||
- 页面截图保存在 `docs/evidence/cloud-console-ui-20261109/`。完整矩阵与检查输出保存在本机忽略提交的 `.local/ui-review/`。含活动连接的测试截图由 `e2e/ui.spec.ts` 使用隔离数据生成,不能作为现场串流验收证据。
|
||||
|
||||
## 启动和维护
|
||||
|
||||
现场入口仍为 `http://127.0.0.1:5182`,账号与数据库沿用当前真实环境。正常启动见[真实接入说明](cloud-live-integration.md)。只更新前端时可执行:
|
||||
|
||||
```powershell
|
||||
docker compose --env-file .local/live/.env -f deploy/compose.live.yml build console
|
||||
docker compose --env-file .local/live/.env -f deploy/compose.live.yml up -d --no-deps console
|
||||
```
|
||||
|
||||
样式集中在 `console/src/styles/tokens.css`,主题逻辑为 `useTheme.ts`,公共导航为 `ConsoleLayout.vue`。本次未提交或推送本机所有开发改动。
|
||||
89
docs/cloud-live-integration.md
Normal file
@ -0,0 +1,89 @@
|
||||
# 现场真实接入
|
||||
|
||||
更新:2026-11-09。与原演示环境分开,当前 API 使用 `production` 模式和独立 PostgreSQL,不生成演示节点、桌面或用户。服务运行在开发机 Docker Desktop;这是接入现场设备的联调环境,尚未迁到独立云服务器。
|
||||
|
||||
## 日常启动与入口
|
||||
|
||||
打开 Docker Desktop,在项目根目录运行:
|
||||
|
||||
```powershell
|
||||
.\tools\start-cloud-live.ps1
|
||||
```
|
||||
|
||||
代码修改后加 `-Build`。控制台为 `http://127.0.0.1:5182`,初始管理员账号密码保存在本机忽略提交的 `.local/live/admin-login.txt`。原 `5181` 仍是独立演示环境,不作为现场入口。正式数据库命名卷 `vdi-cloud-live_vdi-live-postgres`;停止容器不会删除数据,勿执行 `down -v`。
|
||||
|
||||
盒子通过 `https://192.168.1.159:8443` 访问平台,校验专用 CA;HTTP 控制台端口只映射本机。开发机必须保持运行、内网地址可达,IP 变化时需更新证书、Compose 地址和盒子配置。浏览器从其他机器访问 HTTPS 时,需要信任本机生成的 CA;本轮没有将 CA 安装进系统证书库。
|
||||
|
||||
## 当前现场映射
|
||||
|
||||
| 对象 | 接入方式 |
|
||||
| --- | --- |
|
||||
| PVE `192.168.1.51` / 节点 `starhe` | 专用 `vdi-cloud@pve!console` Token;CA 验证开启,证书 DNS 名 `starhe` 通过容器 extra_hosts 映射实际 IP |
|
||||
| Windows VM100 / `.70` | 纳管已有 VM,2560×1440 / 45FPS 请求 |
|
||||
| 麒麟 VM101 / `.71` | 纳管已有 VM,2560×1440 / 30FPS 请求 |
|
||||
| Ubuntu VM102 / `.72` | 纳管已有 VM,2560×1440 / 45FPS 请求 |
|
||||
| 盒子 `.188` / SN `ee621cae7199a2e9` | Qt 平台适配器 `qt-platform/0.2.0`,设备 Token、HTTPS 心跳和命令轮询 |
|
||||
|
||||
FPS 是串流请求参数,不是实际性能验收值。桌面与设备 ID 由数据库生成;本机 `.local/live/site-state.json` 保存本轮映射,不把固定演示 ID 用于真实环境。
|
||||
|
||||
PVE Token 继承专用用户权限:根路径 `PVEAuditor` 负责只读发现,只有 `/vms/100`、`/vms/101`、`/vms/102` 授予自定义 `VDICloudPower`(`VM.Audit VM.PowerMgmt`)。子路径 ACL 会覆盖继承权限,因此该角色同时包含 VM.Audit。平台没有 PVE root 密码、宿主重启、VM 创建、删除、改硬件权限。
|
||||
|
||||
## 终端实际流程
|
||||
|
||||
1. 管理员登记 SN,生成一次性激活密钥。盒子读取 `~/.config/vdi-terminal/platform.json`,通过注册接口取得设备 Token,以 0600 原子保存并删除激活密钥。
|
||||
2. 每 30 秒发送真实心跳;每 15 秒在选择页刷新授权桌面;每 3 秒领取远程命令。平台配置存在时不回退到本地授权或本地票据,列表与连接参数由平台维护。
|
||||
3. 点击连接后申请、消费单次票据。消费只登记“连接中”,Moonlight 实际画面就绪后才上报 connected,计入活动连接。
|
||||
4. 自然退出、连接失败、管理员重置都会按 sessionId 释放预约。结束事件和回执写入待发队列,网络恢复后重试;已执行的命令不自动重放。旧会话事件或迟到的重置回执不能清理新会话。
|
||||
5. “重置会话”等待 Moonlight 进程真正退出后回执。“收集日志”上传受限诊断信息与脱敏日志尾部;管理员/运维可在成功任务详情下载,只读角色不可读取。
|
||||
6. “重启盒子”通过固定 `sudo -n /usr/bin/systemctl reboot` 调用,记录原 boot ID,下一次启动确认 boot ID 改变后才回执;未做现场重启验收。没有远程任意命令或 shell 参数接口。
|
||||
|
||||
本轮由 Qt 直接接入已有设备 API;未将它称为 Rust Agent 或 MQTT。Moonlight/Sunshine 继续使用原有配对;平台票据不是 Sunshine 的新认证方式。没有修改串流编码、光标、显示守护、USB 隧道配置。
|
||||
|
||||
## 初次配置其他现场
|
||||
|
||||
安装 Python 的 `paramiko`、`cryptography` 后,先确认 SSH known_hosts 中存在核验过的 PVE 主机公钥,再执行:
|
||||
|
||||
```powershell
|
||||
python tools/provision-pve-access.py --host <PVE-IP> --vmids <VM-ID...> --output .local/live/pve-access.json
|
||||
python tools/prepare-live-config.py --ip <平台内网-IP> --pve-hostname <PVE证书内DNS名称> --pve-ip <PVE-IP>
|
||||
.\tools\start-cloud-live.ps1 -Build
|
||||
```
|
||||
|
||||
PVE SSH 密码通过交互提示或 `VDI_PVE_SSH_PASSWORD` 读取;工具不在控制台打印 Token。已存在配置或账号时会停止,防止覆盖已有身份。不要将本轮现场的 `.local/live` 文件复制给其他客户。
|
||||
|
||||
从控制台发现并纳管 VM、登记设备和创建用户授权。盒子配置格式如下,路径按现场用户调整:
|
||||
|
||||
```json
|
||||
{
|
||||
"url": "https://平台内网地址:8443",
|
||||
"caFile": "/home/armbian/.config/vdi-terminal/platform-ca.pem",
|
||||
"activationSecret": "控制台生成的一次性密钥"
|
||||
}
|
||||
```
|
||||
|
||||
配置和证书通过受信 SSH 安装;配置权限 0600。注册成功后由终端维护 deviceToken、待发事件和回执,不再手工覆盖。备份时同时保留数据库、PVE Token、平台 CA 私钥和终端身份文件,均按凭据管理。
|
||||
|
||||
## 盒子回退
|
||||
|
||||
本轮初始备份路径在盒子 `~/vdi-terminal/backups/platform-live-current-backup` 指向的目录。备份包括接入前 kiosk-shell、client.conf、desktops.json。维护时先结束串流,再原子恢复该 kiosk-shell 并将 platform.json 移至受限备份目录,由原有监控进程重启 kiosk。恢复后是原本的本地 POC 模式,不再从平台授权。无需回退 Moonlight 二进制或重启 VM。
|
||||
|
||||
只撤销 PVE 接入时,可禁用 `vdi-cloud@pve` 用户或移除 `console` Token;不要删除 root 或其他账号。独立部署、用户管理与密码轮换、登录限流、备份恢复演练、设备撤销/重新激活、OTA 和多租户仍属于后续产品化范围。
|
||||
|
||||
## 本轮现场验收
|
||||
|
||||
- PVE 严格 CA/DNS 验证通过,发现并纳管三台真实 VM;节点/VM 状态每 30 秒自动同步。
|
||||
- 盒子实际注册,心跳上报 `qt-platform/0.2.0`;控制台显示 1 台在线,串流请求使用现场原有配对。
|
||||
- Windows、Ubuntu、麒麟分别从真实授权列表申请并消费票据,实际画面就绪事件被平台记录;控制台分别下发 reset-session,Moonlight 实际退出后收到成功回执,最终活动连接均归零。
|
||||
- 首次 Windows 重置因同步检查异步退出而错误返回 failed,已保留任务;修复为异步等待后重测通过,未删除失败记录。
|
||||
- Go 测试与专用 PostgreSQL 集成测试、go vet 通过,新增覆盖会话事件隔离、预约不冒充活动串流、日志读取权限与迟到回执不清理新会话。盒子 CTest 3/3、前端静态检查、类型检查、单元测试和 Docker 构建通过。
|
||||
- 现场 Playwright 页面流程通过:正式登录、3 台真实桌面、真实设备在线、下发日志采集、收到成功回执并下载实际 boot ID/uptime/日志内容。截图见 [真实总览](evidence/cloud-live-20261109/overview.png)、[真实桌面](evidence/cloud-live-20261109/desktops.png)。
|
||||
- 现场 kiosk-shell SHA256:`2cca9d2d183c2f34f0f6172db207649decb2799414f9443043d175bbb19d33a1`。Moonlight 二进制未更换。
|
||||
|
||||
浏览器现场用例独立执行,避免运行演示库用例操作真实 VM:
|
||||
|
||||
```powershell
|
||||
cd console
|
||||
npx playwright test --config=playwright.live.config.ts
|
||||
```
|
||||
|
||||
该用例读取本机私有初始账号文件,验证正式登录、真实桌面/设备以及日志收集下载,不发 VM 电源操作。完整演示 E2E 仍要求 demo 模式;数据库集成测试仍只指向专用 vdi_test。
|
||||
144
docs/cloud-platform-development.md
Normal file
@ -0,0 +1,144 @@
|
||||
# 云端服务与控制台开发说明
|
||||
|
||||
更新:2026-11-09。当前交付是第一阶段本地可运行版本。Vue 控制台通过真实 HTTP 调用 Go API,账号、授权、设备、任务和审计保存在 PostgreSQL;演示 VM 的电源操作由明确标记的演示计算适配执行。本文件主要描述演示开发环境;随后已接通[独立真实环境](cloud-live-integration.md),控制台端口 5182、设备 HTTPS 8443。
|
||||
|
||||
## 启动
|
||||
|
||||
### Windows 本机开发
|
||||
|
||||
需要 Node.js 22.12+、Go 1.25+、Docker Desktop。仓库根目录执行:
|
||||
|
||||
```powershell
|
||||
.\tools\start-cloud-dev.ps1
|
||||
```
|
||||
|
||||
已安装前端依赖时可加 `-SkipInstall`。脚本启动专用 PostgreSQL 容器,编译并启动本机 API,再启动 Vite。默认控制台地址 `http://127.0.0.1:5180`,API 地址 `http://127.0.0.1:8088`,数据库仅映射本机 `55438`。可用 `-ApiPort` 和 `-ConsolePort` 改开发端口。不会停止其他占用端口的程序。
|
||||
|
||||
演示账号 `admin`、`operator`、`viewer`,统一演示密码 `Demo@2026`。对应超级管理员、运维、只读角色,仅 `VDI_MODE=demo` 会创建这些账号。首次数据库初始化后,重新启动保留业务数据。
|
||||
|
||||
进程 PID 和日志在忽略提交的 `.local/` 中。关闭本机开发进程时根据 `.local/cloud-dev.json` 确认 PID 和命令路径,只停止本次启动的 API 与 Vite。数据库可通过 `docker compose -f deploy/compose.dev.yml stop postgres` 停止;不删除卷即可保留数据。
|
||||
|
||||
### 全容器开发
|
||||
|
||||
与本机模式二选一,避免占用同一端口:
|
||||
|
||||
```powershell
|
||||
docker compose -f deploy/compose.dev.yml up --build -d
|
||||
```
|
||||
|
||||
控制台仍是 `http://127.0.0.1:5180`。Nginx 代理同源 `/api` 到 Go 服务,PostgreSQL 使用命名卷。该 compose 是开发演示配置,不是客户生产部署包。
|
||||
|
||||
端口被其他开发进程占用时,可先设置 `$env:VDI_CONSOLE_PORT='5181'`、`$env:VDI_API_PORT='8089'` 再执行 compose。当前本机验收使用该端口组合,预览地址为 `http://127.0.0.1:5181`。
|
||||
|
||||
### 仅前端模拟
|
||||
|
||||
```powershell
|
||||
cd console
|
||||
npm ci
|
||||
$env:VITE_API_MODE='mock'
|
||||
npm run dev
|
||||
```
|
||||
|
||||
MSW 在浏览器层模拟 HTTP,只有开发构建允许启用,不需要 Go 或数据库。模拟操作不控制真实资源,刷新页面会重置模拟业务数据。正常的 `api` 模式不注册模拟 Worker。
|
||||
|
||||
## 当前可用范围
|
||||
|
||||
| 模块 | 当前实现 |
|
||||
| --- | --- |
|
||||
| 登录 | bcrypt 密码校验、JWT、服务端会话记录、退出撤销、刷新页面恢复、三角色服务端授权 |
|
||||
| 总览 | 终端在线、VM 电源、实际画面就绪连接、异常任务;连接预约单独记录 |
|
||||
| 计算资源 | 节点列表;配置 PVE 后每 30 秒同步节点/已纳管 VM,显示同步时间与过期标记 |
|
||||
| 桌面 | 演示桌面;发现 PVE VM、纳管、查看详情、开机/正常关机/重启;PVE UPID 跟踪 |
|
||||
| 终端 | 管理员登记、一次性激活、设备独立令牌、心跳、桌面授权列表、命令领取与回执 |
|
||||
| 授权 | 创建最终使用者、每设备关联一位使用者、每使用者多个桌面、停用和撤销后阻止新连接 |
|
||||
| 任务 | 持久化排队/执行/成功/失败/结果未确认、请求去重、同对象任务互斥、最终回执审计 |
|
||||
| 连接契约 | 短期单次票据、终端隔离、消费时重新校验授权、同桌面活动占用检查 |
|
||||
|
||||
当前连接票据仅授权并返回 Moonlight 端点与参数;终端仍需既有 Sunshine 配对。消费票据登记连接预约,不证明串流已建立;现场 Qt 已上报实际画面就绪与进程结束事件,详情见真实接入说明。不要把 `credentialMode=existing-pairing` 当作免配对认证。
|
||||
|
||||
本轮将 API 入口与基础业务模块放在一个可运行的 Go 网关进程中,数据库和 Proxmox client 独立封装。原方案的七个服务职责继续作为后续拆分边界;当前尚未交付七个独立部署的服务。MQTT 总线、Rust Agent、VictoriaMetrics 指标接入、模板发放、桌面池、GPU 绑定、OTA、AD/LDAP 尚未完成;Qt 适配现已实际部署。控制台仅显示已接通的第一阶段功能。
|
||||
|
||||
## 真实 Proxmox 接入
|
||||
|
||||
使用新的独立数据库,按 `platform/.env.example` 注入配置。正式模式要求至少 32 字符的 JWT 密钥、管理员账号和至少 12 字符的初始密码。管理员环境变量仅在账号首次创建时生效,不会在每次启动时覆盖已有密码。数据库记录初始化模式,拒绝直接混用演示与正式数据库。
|
||||
|
||||
```text
|
||||
VDI_MODE=production
|
||||
DATABASE_URL=postgres://.../vdi?sslmode=require
|
||||
VDI_JWT_SECRET=...
|
||||
VDI_ADMIN_USERNAME=...
|
||||
VDI_ADMIN_PASSWORD=...
|
||||
PVE_URL=https://pve.example.internal:8006
|
||||
PVE_TOKEN_ID=vdi@pve!console
|
||||
PVE_TOKEN_SECRET=...
|
||||
PVE_CA_FILE=/run/secrets/pve-ca.pem
|
||||
```
|
||||
|
||||
PVE 凭据由后端持有,前端和终端不会接收;证书校验始终开启。PVE_URL 只填 HTTPS origin,不附 `/api2/json`。由运维给 API Token 配置实际需要的节点/VM/存储查询和 VM 电源操作权限。本轮不自动修改 PVE 账号、ACL、VM 或主机配置。
|
||||
|
||||
启动后到“计算资源”手动同步;再到“桌面管理”选择“纳管已有 VM”。纳管仅登记已存在的 VM,不改 VM 配置。连接地址可在纳管表单填写。首次真实接入应使用专门测试 VM 核验 API 版本、证书、任务结果和关机行为,再对业务 VM 开放操作。
|
||||
|
||||
正式环境的 HTTPS 入口、运维凭据轮换、账号维护、登录限流、独立数据库迁移流程与备份恢复仍属于部署产品化工作;当前 compose 不作生产就绪声明。
|
||||
|
||||
## 设备接口联调
|
||||
|
||||
接口契约在 `docs/openapi/platform.v1.json`,API 根路径 `/api/v1`。控制台通过生成的类型和统一请求适配调用接口,页面不拼接 API 地址。
|
||||
|
||||
1. 管理员在终端页登记 SN,取得仅显示一次的 `activationSecret`。
|
||||
2. 终端 `POST /terminal/register` 提交 SN、activationSecret、agentVersion,取得设备令牌;激活密钥随即作废。
|
||||
3. 设备以独立 `Authorization: Bearer <device-token>` 发送心跳、拉配置和获取授权桌面。
|
||||
4. 管理员创建用户并关联终端、勾选多个桌面;设备再次请求 `/terminal/desktops` 即获得更新列表。
|
||||
5. 设备轮询 `GET /terminal/commands` 领取命令,执行后 `POST /terminal/commands/{id}/ack` 报告 success/failed。没有回执的命令约两分钟后显示结果未确认;不会自动重放。
|
||||
6. 申请 `/terminal/connect-ticket`,再用 `/terminal/connect-ticket/consume` 单次消费;消费时再次检查授权与桌面占用。之后仍使用已有 Moonlight/Sunshine 配对。
|
||||
|
||||
`tools/platform-terminal-simulator.py --sn <SN>` 可在本机演示平台注册、发心跳和读取授权桌面。激活密钥通过交互输入或 `VDI_DEVICE_ACTIVATION_SECRET` 传入;模拟器对所有设备操作回失败,避免假装执行了重启或日志收集。它不是可部署的 Rust Agent。
|
||||
|
||||
当前 HTTPS 轮询是 MQTT 接入前的可验证通道;没有声称已提供消息总线。终端令牌的撤销/重新激活、配置热更新将继续扩展契约;Qt 串流事件、固定命令执行和诊断日志上传已在真实环境接通。
|
||||
|
||||
## 检查命令
|
||||
|
||||
```powershell
|
||||
cd console
|
||||
npm run lint
|
||||
npm run typecheck
|
||||
npm run test
|
||||
npm run build
|
||||
npm run test:e2e
|
||||
npm run api:generate
|
||||
```
|
||||
|
||||
E2E 使用已经启动的本机平台,只允许对 `/healthz` 返回 demo 模式的服务运行;Windows 默认使用已安装的 Edge。其他环境使用 `npx playwright install chromium` 准备测试浏览器。`VDI_E2E_URL` 可覆盖测试地址。测试会创建演示用户和待激活终端、切换演示 VM 电源,不会操作真实 PVE。
|
||||
|
||||
```powershell
|
||||
cd platform
|
||||
go test ./...
|
||||
go vet ./...
|
||||
```
|
||||
|
||||
数据库集成测试需要 `VDI_TEST_DATABASE_URL` 指向**专用可清空测试数据库**。测试会清空该数据库内平台表,不要指向开发业务库或生产库。
|
||||
|
||||
```powershell
|
||||
docker exec vdi-cloud-dev-postgres-1 psql -U vdi -d postgres -c 'CREATE DATABASE vdi_test'
|
||||
$env:VDI_TEST_DATABASE_URL='postgres://vdi:vdi-local-demo@127.0.0.1:55438/vdi_test?sslmode=disable'
|
||||
go test ./...
|
||||
```
|
||||
|
||||
后端测试覆盖服务端 RBAC、JWT 退出失效、幂等任务、授权变更事务回滚、设备隔离、一次性票据、回执和超时;PVE client 使用本地测试服务器验证 TLS 和 UPID,不向现场 PVE 发送请求。
|
||||
|
||||
## 本轮验收记录(2026-11-09)
|
||||
|
||||
- Go 单元测试、专用 PostgreSQL 集成测试和 `go vet` 通过;包含模式隔离、重启后撤权保留、PVE 明确失败与提交结果未确认的区分。
|
||||
- 前端 ESLint、TypeScript、3 项登录状态单元测试和生产构建通过。
|
||||
- 容器版 `http://127.0.0.1:5181` 的 7 项 Playwright 用例全部通过:登录失败/恢复/退出、桌面命令最终结果、用户多桌面授权持久化、终端激活信息、两种权限角色、空列表/异常/401。
|
||||
- 单独启动 MSW 模式,验证模拟登录和授权页导航;检查 1440px 桌面及 390px 窄屏。
|
||||
- 页面截图:[总览](evidence/cloud-console-20261109/overview.png)、[桌面管理](evidence/cloud-console-20261109/desktops.png)、[窄屏](evidence/cloud-console-20261109/narrow.png)。页面主题集中在 `console/src/styles/tokens.css`,可交给 UI 同事继续调整。
|
||||
- Node 依赖完整审计为 0 项已知漏洞。Vitest 已更新;TypeScript ESLint 的 minimatch 子依赖定向覆盖至 11.0.1+,静态检查通过。
|
||||
- Docker API/控制台镜像构建并启动,数据库保留卷内已有业务数据。未向现场 PVE 或盒子发送操作。
|
||||
|
||||
## 数据与执行边界
|
||||
|
||||
- 在线按最近 90 秒心跳计算;FPS/RTT 缺失为 null,不填 0。PVE 电源状态与 VM 内连接服务状态分开。
|
||||
- 任务成功必须有计算适配结果或设备回执。提交后发生通信异常时保留“结果未确认”,由管理员核对实际状态,不自动重试。
|
||||
- 桌面“重启”只用 PVE reboot;未将硬重启 reset 冒充“还原镜像”。
|
||||
- 当前任务和审计列表展示最近 1000 条,完整记录仍存 PostgreSQL;长期检索和归档后续扩展。
|
||||
- 凭据不写入审计;设备上报的自由文本不直接放入公开任务结果。
|
||||
58
docs/current-status.md
Normal file
@ -0,0 +1,58 @@
|
||||
# 当前状态
|
||||
|
||||
截至 2026-11-09,依据仓库中的实施记录整理。本次文档整理没有重新连接现场设备,下面的“已验证”指相应日期的历史验收,并非实时健康报告。
|
||||
|
||||
## 产品与架构
|
||||
|
||||
当前主线是 RK3528 盒子上的 Qt kiosk,通过 Moonlight/Sunshine 接入 Windows、Ubuntu、麒麟。盒子开机进入“选择云桌面”,选择后启动串流,结束后回到列表。11-09 已在独立 production 数据库接通 PVE VM100/101/102 和真实盒子:设备注册/心跳、授权列表、一次性票据、三系统串流事件与远程会话重置。服务目前在开发机 Docker,详见[真实接入说明](cloud-live-integration.md)。
|
||||
|
||||
```text
|
||||
当前:LightDM 独占 vdi-kiosk 会话 → 看门狗 → Qt 桌面列表 → Moonlight → Sunshine 客体
|
||||
目标:终端 → 自研管理平台 API → Proxmox/KVM 管理桌面与授权
|
||||
```
|
||||
|
||||
终端不保存 PVE 管理凭据、不直接调用 PVE API;仓库 `tools/` 中的 PVE/Guest Agent 操作属于管理员部署工具。
|
||||
|
||||
## 联调基线
|
||||
|
||||
以下内网地址仅标识本次联调环境,新客户使用本地配置或平台下发地址。
|
||||
|
||||
| 对象 | 最近记录的配置/状态 | 需要注意 |
|
||||
| --- | --- | --- |
|
||||
| 盒子 `.188` | RK3528 PRO DDR4,ARM64,Armbian/Ubuntu Noble,LightDM + Xorg 独占会话 | 正式 Weston/Wayland 尚未落地;不运行 MATE 桌面/面板 |
|
||||
| PVE `.51` | Proxmox/KVM,节点 starhe | GTX 965M 分配给 Windows;宿主资源有限 |
|
||||
| Windows VM100 / `.70` | Windows 10,GTX 965M/NVENC,VDD 单活动主屏 2560×1440@60Hz,串流请求 45FPS | 历史实测约 43~44FPS,不能称稳定 2K60;曾有重启 Code43 |
|
||||
| 麒麟 VM101 / `.71` | 2560×1440,30FPS 请求,H.264/libx264,30Mbps;std VGA 64MB,2 vCPU | 11-06 确认 UKUI/KWin 合成器关闭;动态场景约 23FPS |
|
||||
| Ubuntu VM102 / `.72` | 2560×1440,近期测试请求 45FPS,H.264/libx264,30Mbps;cpu=host、virtio-gl | KMS 与 X11 路径随原生光标租约/普通服务切换,不能把早期 KMS 测试概括为所有会话固定 KMS |
|
||||
| 监控 `.51:8090` | 独立 `vdi-monitor` 项目 | 实时状态去监控页面查看;本仓库只保留开发与排障资料 |
|
||||
|
||||
盒子实际使用私有 RKMPP 硬解/EGL 路径;早期文档中的“仅软解、1080p、Linux 未接入”已过时。桌面 FPS 是请求值,不等于实际动态收流/呈现帧率,实际值以 `desktops.json` 与该次日志为准。
|
||||
|
||||
## 最近已完成
|
||||
|
||||
| 事项 | 记录与验证边界 |
|
||||
| --- | --- |
|
||||
| 开机直接显示桌面列表 | 11-09 独占 LightDM 会话;保留[冷启动截图](evidence/boot-direct-list-cold-boot.png) |
|
||||
| 退出短暂露出绿色桌面 | 11-09 列表全屏常驻串流窗口下方;三系统两轮共 3121 个 X11 采样未见底层窗口 |
|
||||
| 进入左上角小窗口 | 11-09 Moonlight 0007 隐藏 CLI Qt 窗口,SDL 创建时铺满屏幕;三系统进入 2695 次、返回 1562 次采样均无违规 |
|
||||
| 显示器关开恢复 | 11-03 显示守护恢复定制 2K 模式,用户现场确认自动恢复全屏 |
|
||||
| Linux 原生独立光标 | 11-05 起已接入形状/热点/显隐与本地位置;有短时故障恢复和点击验证 |
|
||||
| 鼠标按钮残留 | 11-06 失焦及退出时释放五个按钮,输入相关测试通过 |
|
||||
| 客体图形关机/重启限制 | 11-04 按当前用户部署 Windows NoClose 与 Linux polkit/D-Bus 规则;菜单逐项目视及重启后验证不完整 |
|
||||
| U 盘真实透传 | Windows 已识别真实 USBSTOR、完成文件校验与实物拔插;后续扩展纯存储设备策略 |
|
||||
| 固定 PCI 设备不可弹出 | 11-05 Windows 冷启动后四项能力属性 6→0,GPU/网络/系统盘正常;托盘外观和真实 U 盘组合仍需现场验收 |
|
||||
|
||||
进入/退出结果来自 X11 窗口采样与截图,没有外置 HDMI 逐帧采集,不能外推为物理屏幕每一帧的保证。
|
||||
|
||||
## 重要限制与历史冲突
|
||||
|
||||
- Ubuntu 原生光标能即时移动,但点击后页面、拖窗内容仍要等待虚拟显示捕获与 CPU 编码。11-05 KMS 45FPS 长按框选 P50/P95 约 196/247ms;这是合成注入到解码画面的测试,不是物理端到端延迟。
|
||||
- 11-06 Intel VAAPI 直通试验虽识别到编码器,但无法稳定建立显示捕获/串流,已经回退。普通 Sunshine 恢复 X11/software,VM102 无 Intel hostpci;不能写成硬件编码优化成功。
|
||||
- Ubuntu 显存 64MB 试验变差,已恢复默认;Windows 独立光标只有历史受控原型,当前仍保留视频光标。
|
||||
- 11-02 GPU Code43 诊断是历史故障记录;11-03 恢复后实测 NVENC,11-05 冷启动检查也为错误码 0。现网是否再次退回软编必须重新查询。
|
||||
- 快捷键捕获参数已接入;系统电源键策略补丁、客体截图组件和实际传递效果需分别核验,不凭配置就宣称 Win+Shift+S 已完成现场验收。
|
||||
- 盒子开关控制客户端连接,不等于控制 VM 生命周期;平台联动尚未实现。
|
||||
- 第一阶段 Go API、Vue 控制台与 PostgreSQL 已有本机可运行实现,含登录/RBAC、资源/桌面/终端、用户授权、任务审计和 PVE 适配;详见[云管开发说明](cloud-platform-development.md)。随后已用独立账号接通现场 PVE 与 Qt 盒子,三系统实际串流与重置会话通过;PVE 电源操作和盒子重启没有通过现场重启测试验收。
|
||||
- Rust Agent、MQTT、OTA、多租户授权及通用 USB 会话隔离仍待产品化。已有 POC 模块不等于完整功能交付。
|
||||
|
||||
当前构建、策略和排障入口分别见[终端说明](terminal-guide.md)、[云桌面说明](desktop-guide.md)、[运维说明](operations.md);待办统一见[未来计划](roadmap.md)。历史精确配置和回退路径可从[归档索引](archive/README.md)查原文。
|
||||
89
docs/desktop-guide.md
Normal file
@ -0,0 +1,89 @@
|
||||
# 云桌面说明
|
||||
|
||||
状态以[当前状态](current-status.md)为入口,以下归纳三种系统的显示、输入、外设和电源策略。日期是现场实施日期,本次整理没有改动镜像或服务。
|
||||
|
||||
<a id="windows"></a>
|
||||
## Windows 显示与编码
|
||||
|
||||
Windows VM100 使用 GTX 965M 直通、Sunshine NVENC 和 VDD 虚拟显示器。只保留 VDD 为活动主屏,目标分辨率 2560×1440、显示刷新率 60Hz,Moonlight 请求 45FPS;11-03 实际渲染约 43~44FPS。提高请求到 60FPS、换队列/VSync/nearest 采样没有形成稳定 2K60 收益,精确尺寸采样实验未采用。
|
||||
|
||||
GPU 历史上曾出现 Code43,11-02 的 D3cold、ROM 副本、快速启动等实验没有可靠修复,均回退;之后运维修复,11-03 实际 NVENC、11-05 冷启动错误码 0 已验证。故障排查要同时看 `nvidia-smi`、设备错误码、Sunshine 的实际 `h264_nvenc` 和真实会话编码占用,不能只看显卡名称。
|
||||
|
||||
工具入口:[set-2k-display.ps1](../tools/set-2k-display.ps1)、[set-fixed-pci.py](../tools/set-fixed-pci.py)。历史 [2K45 证据](evidence/2k60-20261103/)、[单屏证据](evidence/single-display-20261103/)保留。当前 Windows 使用视频内的单个远程光标;11-03 的独立光标受控原型已收尾,没有进入产品默认路径。
|
||||
|
||||
<a id="linux"></a>
|
||||
## Ubuntu 与麒麟画质、捕获和延迟
|
||||
|
||||
两种 Linux 已接入真实 2560×1440、H.264、30Mbps、盒子 RKMPP 硬解,并按主机处理 limited 色彩范围协商;Ubuntu 光标恢复 Adwaita/24px 和默认加速,保留关闭动画。不要把“2K 请求参数”视为服务端已经真实输出 2K,要同时核对 XRandR、捕获日志及截图。
|
||||
|
||||
| 系统 | 保留的调整 | 边界 |
|
||||
| --- | --- | --- |
|
||||
| Ubuntu VM102 | cpu=host、virtio-gl、原生光标;曾验证 KMS + 原生光标与 45FPS | 原服务在 11-06 直通失败回退后为 X11/software;原生光标租约使用的捕获路径需另查配置 |
|
||||
| 麒麟 VM101 | std VGA 64MB、2 vCPU、真实 2K/30FPS;Sunshine 启动后暂停 UKUI/KWin 合成器 | 11-06 确认合成器 active=false;动态基准约 23FPS,对照开启约 19FPS,仍是 CPU 编码 |
|
||||
|
||||
Ubuntu 的可比较历史数据来自 11-05 同场景合成注入:
|
||||
|
||||
| 场景 | KMS 45FPS | X11 45FPS |
|
||||
| --- | --- | --- |
|
||||
| 长按框选回显 P50/P95 | 约 196/247ms | 约 250/312ms |
|
||||
| 视频中可见新编号更新 | 16.0 次/秒 | 10.6 次/秒 |
|
||||
|
||||
同轮实际 GTK 按钮回显约 304/378ms、masked 输入约 165/182ms;没有将合成提交冒充真实业务登录成功。这些数值不包含物理 USB 与面板扫描,也不等于光标移动延迟。原始结果见 [Ubuntu 深度调优](evidence/ubuntu-deep-latency-20261105/)。
|
||||
|
||||
11-06 的两项失败实验都已撤回:
|
||||
|
||||
- **Intel 核显直通/VAAPI**:Ubuntu 识别 i915 与编码入口,但虚拟显示捕获到编码链路不稳定,出现 KMS monitor 阻塞和 launch 超时;已恢复 VM102 的 virtio-gl、无 hostpci0、普通 Sunshine X11/software。详见[试验最终状态](evidence/ubuntu-latency-plan-20261106/intel-vaapi-trial-final.json)。
|
||||
- **虚拟显存 64MB**:短样本收流约 27.49FPS、主机处理平均 83.4ms,恢复默认后约 32.64FPS/60.8ms,未保留 64MB 参数。见 [VRAM 对照](evidence/ubuntu-vram-20261106/)。
|
||||
|
||||
VirGL 不等于可用的硬件编码。增加编码线程、虚拟显存或修改鼠标速度不能消除 CPU 捕获/编码瓶颈;后续路线见[未来计划](roadmap.md#performance)。
|
||||
|
||||
<a id="cursor"></a>
|
||||
## 输入顺序与 Linux 原生独立光标
|
||||
|
||||
客户端输入补丁保持绝对移动与点击边界顺序,优先处理输入,最新帧队列避免堆积旧视频;这是输入正确性和排队优化,不能承诺零延迟。11-06 的 [0006 补丁](../terminal/moonlight/patches/0006-vdi-release-stuck-mouse-buttons.patch) 在 SDL 失焦与退出时释放左、中、右、X1、X2 五个按钮,修复丢失 BUTTONUP 后点击/拖拽卡住的问题。
|
||||
|
||||
Linux 原生光标实现见 [terminal/native-cursor](../terminal/native-cursor/README.md) 和 Moonlight 0005。通过受限 SSH 通道同步实际形状、热点、显隐及必要位置校正,本地指针移动不等待视频;连接前启用无光标捕获,整次会话结束后恢复普通服务,避免视频内与本地同时出现光标。
|
||||
|
||||
当前约束:单座席 X11、单屏 2560×1440、绝对鼠标,形状最大 128×128。客户端状态超时约 2 秒,主机租约 4 秒;未知形状、分辨率变化或连接故障时结束会话并恢复服务,不做未经确认的运行中切换。客户端和 Linux profile 的 SHA 必须匹配,升级/回退成对处理。
|
||||
|
||||
已验证两系统 7 类形状/热点/显隐、28 次编号点击、故障清理及 Ubuntu 约 6 分钟状态读取;短时暂停 1.1 秒可恢复,持续失效会退出。完整真实失焦、长期稳定、多屏/缩放/Wayland/相对鼠标尚未全面验收。11-09 进入/退出回归沿用原生光标。证据见 [native-cursor](evidence/native-cursor-20261105/);当前二进制校验值见[终端说明](terminal-guide.md#build)。
|
||||
|
||||
<a id="power"></a>
|
||||
## 客体桌面“关机/重启”按钮
|
||||
|
||||
11-04 已按当前客户账户部署持久策略,独立于盒子键盘过滤。目标是禁止客户从桌面图形入口关机、重启、睡眠和休眠,保留管理员与 PVE 维护能力。
|
||||
|
||||
| 系统/账户 | 策略与首次备份 |
|
||||
| --- | --- |
|
||||
| Windows VM100 / test | Explorer `NoClose=1`;`C:\ProgramData\VDI\power-policy\original.clixml` |
|
||||
| 麒麟 VM101 / test1 | polkit `.pkla` + D-Bus 用户级电源方法拒绝;`/var/lib/vdi/power-policy/` |
|
||||
| Ubuntu VM102 / test2 | polkit `.rules` + D-Bus 用户级电源方法拒绝;同上 |
|
||||
|
||||
Linux 仅用 polkit 不够:当时实际图形进程的基础 reboot 授权仍有残留,因此额外使用 `/etc/dbus-1/system.d/zz-vdi-desktop-power.conf` 拒绝该用户的 login1 电源方法与 Can* 查询。麒麟 polkit 文件为 `/etc/polkit-1/localauthority/90-mandatory.d/99-vdi-power.pkla`,Ubuntu 为 `/etc/polkit-1/rules.d/00-vdi-power.rules`。
|
||||
|
||||
Windows 交互 Shell 有效策略读回为 1;Linux 八类方法使用无效签名安全探测,均 Access denied,七类 Can* 查询拒绝。未发送真实关机请求,未逐项目视验收菜单刷新,也未完成重启后的全流程验收。策略不限制管理员绕过,不改变注销/锁屏和更新行为。新账户需重新应用。
|
||||
|
||||
管理员部署/查询/回退入口为 [deploy-guest-power-policy.py](../tools/deploy-guest-power-policy.py),子工具为 [Windows](../tools/set-guest-power-policy.ps1) 与 [Linux](../tools/set-guest-power-policy.py)。`apply` 可替换成 `query` 或 `restore`,重复应用不覆盖首次备份。例:
|
||||
|
||||
```powershell
|
||||
python tools/deploy-guest-power-policy.py query --vm 100
|
||||
python tools/deploy-guest-power-policy.py query --vm 101 --user test1
|
||||
python tools/deploy-guest-power-policy.py query --vm 102 --user test2
|
||||
```
|
||||
|
||||
证据见 [guest-power-policy](evidence/guest-power-policy-20261104/)。盒子断电仅断开客户端,不等于云端 VM 随之关机;物理开关联动尚未实现。
|
||||
|
||||
<a id="usb"></a>
|
||||
## U 盘透传与 Windows 安全删除菜单
|
||||
|
||||
当前 POC 路径为盒子物理 USB → Rust `vdi-usb-monitor` → 私有 usbredirect → 回环 SSH 隧道 → QEMU usb-redir → Windows 自带 USBSTOR。显示仍走 Moonlight,盒子不保存 PVE 凭据。
|
||||
|
||||
已由固定 Kingston 身份扩展为“任意纯 USB Mass Storage”:所有接口都必须为 class 08,键鼠、Hub、混合 HID 设备拒绝;发现本地挂载、holder、swap 占用时等待,不强制卸载。一个盒子/VM 同时导出一个存储设备。udev 事件即时唤醒,500ms 扫描兜底,必须等接口枚举完整后再判断设备。
|
||||
|
||||
已经验收 Kingston 真实盘符、1MiB 随机文件往返 SHA 一致与一次实物拔插;另一只 SanDisk 双分区 Healthy;用户确认不再自动开两个文件夹、提示菜单响应改善。私有 usbredir 0.15.0 避免旧 exporter 持续占约一个 CPU 核的问题,观察降至约 1%。没有完成多盘大文件吞吐、异常断网/掉电、VM/PVE 重启恢复及多用户隔离验收。
|
||||
|
||||
管理员部署入口为 [deploy-usb-device-poc.py](../tools/deploy-usb-device-poc.py) 的 `--any-storage`;盒子策略在 `/etc/vdi-usb-monitor/device.env`,服务 `vdi-usb-monitor.service`,PVE 隧道 `vdi-usb-tunnel-100.service`。QMP watcher 的源码能力、当时是否现场安装、后续 monitor 交接版本须分别确认;`vdi-monitor` 已记录保留端点的 `--keep-attached` 版本,但不能反推任意运行环境都有同一部署。
|
||||
|
||||
11-05 为 Windows 固定 PCI 设备关闭 Q35 的 ACPI PCI/SHPC/PCIe root-port 热插拔,正常关机再启动后四项能力从 6→0,GPU/网络/系统盘正常。USB 设备级热插拔保留,内部 PCI 增删改为关机维护。此配置不能只在 Windows 点重启生效,必须让 QEMU 完整退出再启动。托盘实际外观与真实 U 盘插入/安全弹出仍待组合验收,不能承诺隐藏全部虚拟化标识。
|
||||
|
||||
回退先在 Windows 安全弹出并物理拔盘,再停止导出服务;只还原本功能配置、公钥行和二进制,不整份覆盖后续更新。精确备份路径及步骤见[历史归档](archive/README.md),测试见 [USB 原型](evidence/usb-device-20261103/) 和 [固定 PCI](evidence/fixed-pci-20261105/)。
|
||||
@ -1,70 +0,0 @@
|
||||
# 2026-11-03 显示器关屏再开后云桌面变矮:修复与验收
|
||||
|
||||
已在 `armbian@192.168.1.188` 部署。用户现场关闭显示器、等待后重新打开,确认“已自动恢复全屏”。修复过程没有重启盒子,也没有重启或重连 Moonlight。
|
||||
|
||||
## 原因与证据
|
||||
|
||||
- 异常时盒子 HDMI 输出为 **1920×1440@75Hz(4:3)**,Windows/Moonlight 视频为 **2560×1440(16:9)**。
|
||||
- Moonlight 全屏窗口跟随盒子变为 1920×1440,视频保持宽高比,因此视频内容为 1920×1080,上下各有约 180 像素黑边,表现为“宽度一样,高度矮了一点”。异常截图见 `evidence/display-resume-20261103/before.png`。
|
||||
- MATE 的 `~/.config/monitors.xml` 还保存着 HDMI-1 的 1920×1440@75Hz 配置;原启动脚本只在启动时设置一次 2K,没有处理后续热插拔和模式回退。
|
||||
- 显示器 EDID 的 241.70MHz 时序仍不适用于此 RK3528 vendor PHY;沿用此前已验证的 297MHz 定制 2K 时序,不切回该原厂时序。
|
||||
|
||||
## 修改
|
||||
|
||||
1. 新增 `terminal/kiosk-shell/scripts/vdi-display-guard`:每 2 秒检查 XRandR 状态,模式异常持续至少 2 秒后恢复 `2560x1440_60_fixed297`。模式已存在时不重复创建,热插拔后丢失关联时重新关联。
|
||||
2. 健康状态只查询,不反复设置分辨率;显示器断开或 X11 尚不可用时等待,不强设;设置失败按 2/4/8/16/32/60 秒退避,并在设置后重新读取验证。XRandR 每条命令有 10 秒超时。
|
||||
3. 新增用户级 `vdi-display-guard.service`,已启用 `default.target` 自启、异常退出重启;使用文件锁防止启动恢复与常驻守护竞争。服务独立受 systemd 管理,不挂无主后台循环。
|
||||
4. `vdi-kiosk-start` 使用相同恢复程序做启动检查,再确保服务运行;保留此前渲染配置解析及单一 kiosk 启动链。
|
||||
5. 将本机已知显示器(HDMI-1,GWR/0x2708)的旧保存布局改为 2560×1440@60,其余布局不改。部署前保存原文件。
|
||||
6. 新增只部署显示修复的 `tools/deploy_display_guard.sh`;完整 `deploy_kiosk.sh` 也接入该步骤,避免下次部署覆盖修复。此次只执行前者,未执行会停止串流的完整部署。
|
||||
|
||||
时序仍为:
|
||||
|
||||
```text
|
||||
297.00 2560 2608 2640 2720 1440 1443 1448 1820 +HSync -VSync
|
||||
```
|
||||
|
||||
本次仅调整盒子本地显示,不修改 Windows、Sunshine、GPU、远程光标或已有硬解参数。
|
||||
|
||||
## 验收
|
||||
|
||||
| 项目 | 结果 |
|
||||
| --- | --- |
|
||||
| 自动化显示恢复场景 | 9/9 通过:正常不写、断开/重连、首次创建、重新关联、晚到覆盖、失败退避、设置结果校验、X11 暂不可用等 |
|
||||
| 盒子 CTest | 3/3 通过:display-guard、moonlight-session、desktop-list-page |
|
||||
| 连续两次主动回退 1920×1440@75 | 均自动恢复 2560×1440,全屏窗口恢复 2560×1440,单次从发起切换到观测恢复约 14 秒(含两次驱动切换耗时) |
|
||||
| 真实显示器电源循环 | 用户确认自动恢复全屏;日志在 10:03:19 检测断开,10:03:31 检测到正确 2K 模式,无需额外模式设置 |
|
||||
| 会话连续性 | Moonlight PID 始终为 66385,窗口始终为 44040203;模拟测试前后 boot ID 不变;服务未发生异常重启 |
|
||||
| 最终现场 | 2026-11-03 10:04,HDMI 2560×1440 约 60Hz,Moonlight 窗口 2560×1440,守护 active/enabled |
|
||||
|
||||
原始测试输出:`evidence/display-resume-20261103/recovery-test.txt`。最终模式、进程及包含真实关屏过程的日志:`evidence/display-resume-20261103/final-state.txt`。真实开屏后的截图:`evidence/display-resume-20261103/after-power-cycle.png`。
|
||||
|
||||
这是当前这一台盒子与显示器的现场验收,未执行隔夜或多型号显示器测试。守护固定针对当前单 HDMI、2K、Xorg POC;以后换不同显示器或迁移到 Weston/Wayland 时需改为对应的输出配置机制。当前修复不代表服务端 Code43、帧率或鼠标动态残影问题已解决。
|
||||
|
||||
## 运维
|
||||
|
||||
Windows 开发机在 Git Bash 中只部署此修复:
|
||||
|
||||
```bash
|
||||
bash tools/deploy_display_guard.sh
|
||||
```
|
||||
|
||||
盒子上查看状态与日志(当前环境 `journalctl --user` 看不到用户服务日志,因此使用系统日志的用户单元过滤):
|
||||
|
||||
```bash
|
||||
systemctl --user status vdi-display-guard.service
|
||||
sudo -n journalctl _SYSTEMD_USER_UNIT=vdi-display-guard.service -n 50 --no-pager
|
||||
DISPLAY=:0 XAUTHORITY=/home/armbian/.Xauthority xrandr --query
|
||||
```
|
||||
|
||||
本次部署备份目录为 `/home/armbian/vdi-terminal/backups/display-guard-20261103-095920/`,包含原 `vdi-kiosk-start` 和原 `monitors.xml`。必要时可回退到原行为;这也会恢复旧的 4:3 保存布局和“仅启动时修复”的限制:
|
||||
|
||||
```bash
|
||||
systemctl --user disable --now vdi-display-guard.service
|
||||
cp -p ~/vdi-terminal/backups/display-guard-20261103-095920/vdi-kiosk-start ~/vdi-terminal/bin/vdi-kiosk-start
|
||||
cp -p ~/vdi-terminal/backups/display-guard-20261103-095920/monitors.xml ~/.config/monitors.xml
|
||||
rm -- ~/.config/systemd/user/vdi-display-guard.service
|
||||
systemctl --user daemon-reload
|
||||
```
|
||||
|
||||
日常操作:盒子保持开机,显示器可正常关开。若 HDMI 模式确实回退,给驱动和恢复程序约 15 秒完成切换,无需为此重启盒子。
|
||||
BIN
docs/evidence/boot-direct-list-cold-boot.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
docs/evidence/boot-direct-list-final.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
docs/evidence/boot-direct-list.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
docs/evidence/cloud-console-20261109/desktops.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
docs/evidence/cloud-console-20261109/narrow.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
docs/evidence/cloud-console-20261109/overview.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
@ -0,0 +1,11 @@
|
||||
{
|
||||
"mode": "production",
|
||||
"baseURL": "http://127.0.0.1:5182",
|
||||
"realLogin": true,
|
||||
"desktops": 3,
|
||||
"terminalOnline": true,
|
||||
"darkAndLight": true,
|
||||
"mobile": true,
|
||||
"browserErrors": [],
|
||||
"pveOrTerminalCommandsSubmitted": 0
|
||||
}
|
||||
BIN
docs/evidence/cloud-console-ui-20261109/desktops-dark.png
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
docs/evidence/cloud-console-ui-20261109/desktops-light.png
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
docs/evidence/cloud-console-ui-20261109/login-dark.png
Normal file
|
After Width: | Height: | Size: 310 KiB |
|
After Width: | Height: | Size: 136 KiB |
BIN
docs/evidence/cloud-console-ui-20261109/overview-dark.png
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
docs/evidence/cloud-console-ui-20261109/sessions-light.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
docs/evidence/cloud-live-20261109/desktops.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
docs/evidence/cloud-live-20261109/kiosk.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
5
docs/evidence/cloud-live-20261109/kylin-stream.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"actualStreamReady": true,
|
||||
"resetTaskId": "task_566520ad31e9352714b4a30594e8f634",
|
||||
"realResetAcknowledged": true
|
||||
}
|
||||
BIN
docs/evidence/cloud-live-20261109/overview.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
5
docs/evidence/cloud-live-20261109/ubuntu-stream.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"actualStreamReady": true,
|
||||
"resetTaskId": "task_15c7a5e4709d3709fb7e4186cc15d295",
|
||||
"realResetAcknowledged": true
|
||||
}
|
||||
16
docs/evidence/cloud-live-20261109/validation.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"overview": {
|
||||
"activeSessions": 0,
|
||||
"desktopCount": 3,
|
||||
"failedTasks": 1,
|
||||
"mode": "production",
|
||||
"onlineTerminals": 1,
|
||||
"runningDesktops": 3,
|
||||
"terminalCount": 1
|
||||
},
|
||||
"terminalVersion": "qt-platform/0.2.0",
|
||||
"successfulResets": 3,
|
||||
"successfulDiagnostics": 2,
|
||||
"vmPowerCommandsSubmitted": 0,
|
||||
"initialResetFailureRetained": true
|
||||
}
|
||||
5
docs/evidence/cloud-live-20261109/windows-stream.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"actualStreamReady": true,
|
||||
"resetTaskId": "task_962bb63138b4c1f36d84ce8b7dc41aff",
|
||||
"realResetAcknowledged": true
|
||||
}
|
||||
107
docs/evidence/entry-surface-20261109/entry-final-0.json
Normal file
@ -0,0 +1,107 @@
|
||||
{
|
||||
"desktop_index": 0,
|
||||
"entry_samples": 819,
|
||||
"stream_foreground": true,
|
||||
"returned_to_kiosk": true,
|
||||
"first_mapped_moonlight_geometry": {
|
||||
"14680073": {
|
||||
"t": 4.6786,
|
||||
"class": "com.moonlight_stream.Moonlight",
|
||||
"geometry": [
|
||||
0,
|
||||
0,
|
||||
2560,
|
||||
1440
|
||||
]
|
||||
},
|
||||
"14680075": {
|
||||
"t": 5.2771,
|
||||
"class": "com.moonlight_stream.Moonlight",
|
||||
"geometry": [
|
||||
0,
|
||||
0,
|
||||
2560,
|
||||
1440
|
||||
]
|
||||
}
|
||||
},
|
||||
"stack_changes": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"windows": [
|
||||
6291472,
|
||||
6291472,
|
||||
6291472
|
||||
],
|
||||
"classes": {
|
||||
"6291462": "vdi-kiosk-shell",
|
||||
"6291472": "vdi-kiosk-shell"
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 0.0114,
|
||||
"windows": [
|
||||
6291462,
|
||||
6291462,
|
||||
6291462
|
||||
],
|
||||
"classes": {
|
||||
"6291472": "vdi-kiosk-shell",
|
||||
"6291462": "vdi-kiosk-shell"
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 0.045,
|
||||
"windows": [
|
||||
6291472,
|
||||
6291472,
|
||||
6291472
|
||||
],
|
||||
"classes": {
|
||||
"6291472": "vdi-kiosk-shell",
|
||||
"6291462": "vdi-kiosk-shell"
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 4.6786,
|
||||
"windows": [
|
||||
14680073,
|
||||
14680073,
|
||||
14680073
|
||||
],
|
||||
"classes": {
|
||||
"6291462": "vdi-kiosk-shell",
|
||||
"6291472": "vdi-kiosk-shell",
|
||||
"14680073": "com.moonlight_stream.Moonlight"
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 5.1954,
|
||||
"windows": [
|
||||
6291472,
|
||||
6291472,
|
||||
6291472
|
||||
],
|
||||
"classes": {
|
||||
"6291462": "vdi-kiosk-shell",
|
||||
"6291472": "vdi-kiosk-shell"
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 5.2771,
|
||||
"windows": [
|
||||
14680075,
|
||||
14680075,
|
||||
14680075
|
||||
],
|
||||
"classes": {
|
||||
"6291462": "vdi-kiosk-shell",
|
||||
"6291472": "vdi-kiosk-shell",
|
||||
"14680075": "com.moonlight_stream.Moonlight"
|
||||
}
|
||||
}
|
||||
],
|
||||
"violations": [],
|
||||
"x11_errors": [],
|
||||
"ok": true
|
||||
}
|
||||
81
docs/evidence/entry-surface-20261109/entry-final-1.json
Normal file
@ -0,0 +1,81 @@
|
||||
{
|
||||
"desktop_index": 1,
|
||||
"entry_samples": 908,
|
||||
"stream_foreground": true,
|
||||
"returned_to_kiosk": true,
|
||||
"first_mapped_moonlight_geometry": {
|
||||
"14680073": {
|
||||
"t": 5.6925,
|
||||
"class": "com.moonlight_stream.Moonlight",
|
||||
"geometry": [
|
||||
0,
|
||||
0,
|
||||
2560,
|
||||
1440
|
||||
]
|
||||
},
|
||||
"14680079": {
|
||||
"t": 6.3673,
|
||||
"class": "com.moonlight_stream.Moonlight",
|
||||
"geometry": [
|
||||
0,
|
||||
0,
|
||||
2560,
|
||||
1440
|
||||
]
|
||||
}
|
||||
},
|
||||
"stack_changes": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"windows": [
|
||||
6291472,
|
||||
6291472,
|
||||
6291472
|
||||
],
|
||||
"classes": {
|
||||
"6291462": "vdi-kiosk-shell",
|
||||
"6291472": "vdi-kiosk-shell"
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 5.6801,
|
||||
"windows": [
|
||||
14680073,
|
||||
14680073,
|
||||
14680073
|
||||
],
|
||||
"classes": {
|
||||
"6291462": "vdi-kiosk-shell",
|
||||
"6291472": "vdi-kiosk-shell"
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 6.1982,
|
||||
"windows": [
|
||||
6291472,
|
||||
6291472,
|
||||
6291472
|
||||
],
|
||||
"classes": {
|
||||
"6291462": "vdi-kiosk-shell",
|
||||
"6291472": "vdi-kiosk-shell"
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 6.2755,
|
||||
"windows": [
|
||||
14680079,
|
||||
14680079,
|
||||
14680079
|
||||
],
|
||||
"classes": {
|
||||
"6291462": "vdi-kiosk-shell",
|
||||
"6291472": "vdi-kiosk-shell"
|
||||
}
|
||||
}
|
||||
],
|
||||
"violations": [],
|
||||
"x11_errors": [],
|
||||
"ok": true
|
||||
}
|
||||
107
docs/evidence/entry-surface-20261109/entry-final-2.json
Normal file
@ -0,0 +1,107 @@
|
||||
{
|
||||
"desktop_index": 2,
|
||||
"entry_samples": 968,
|
||||
"stream_foreground": true,
|
||||
"returned_to_kiosk": true,
|
||||
"first_mapped_moonlight_geometry": {
|
||||
"14680073": {
|
||||
"t": 5.8743,
|
||||
"class": "com.moonlight_stream.Moonlight",
|
||||
"geometry": [
|
||||
0,
|
||||
0,
|
||||
2560,
|
||||
1440
|
||||
]
|
||||
},
|
||||
"14680079": {
|
||||
"t": 6.4936,
|
||||
"class": "com.moonlight_stream.Moonlight",
|
||||
"geometry": [
|
||||
0,
|
||||
0,
|
||||
2560,
|
||||
1440
|
||||
]
|
||||
}
|
||||
},
|
||||
"stack_changes": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"windows": [
|
||||
6291462,
|
||||
6291462,
|
||||
6291462
|
||||
],
|
||||
"classes": {
|
||||
"6291472": "vdi-kiosk-shell",
|
||||
"6291462": "vdi-kiosk-shell"
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 0.0107,
|
||||
"windows": [
|
||||
6291472,
|
||||
6291472,
|
||||
6291472
|
||||
],
|
||||
"classes": {
|
||||
"6291462": "vdi-kiosk-shell",
|
||||
"6291472": "vdi-kiosk-shell"
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 5.8743,
|
||||
"windows": [
|
||||
14680073,
|
||||
14680073,
|
||||
14680073
|
||||
],
|
||||
"classes": {
|
||||
"6291462": "vdi-kiosk-shell",
|
||||
"6291472": "vdi-kiosk-shell",
|
||||
"14680073": "com.moonlight_stream.Moonlight"
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 6.4012,
|
||||
"windows": [
|
||||
6291472,
|
||||
6291472,
|
||||
6291472
|
||||
],
|
||||
"classes": {
|
||||
"6291462": "vdi-kiosk-shell",
|
||||
"6291472": "vdi-kiosk-shell"
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 6.4817,
|
||||
"windows": [
|
||||
6291472,
|
||||
14680079,
|
||||
14680079
|
||||
],
|
||||
"classes": {
|
||||
"6291462": "vdi-kiosk-shell",
|
||||
"6291472": "vdi-kiosk-shell"
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 6.4936,
|
||||
"windows": [
|
||||
14680079,
|
||||
14680079,
|
||||
14680079
|
||||
],
|
||||
"classes": {
|
||||
"6291462": "vdi-kiosk-shell",
|
||||
"6291472": "vdi-kiosk-shell",
|
||||
"14680079": "com.moonlight_stream.Moonlight"
|
||||
}
|
||||
}
|
||||
],
|
||||
"violations": [],
|
||||
"x11_errors": [],
|
||||
"ok": true
|
||||
}
|
||||
BIN
docs/evidence/entry-surface-20261109/entry-final-list.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
docs/evidence/entry-surface-20261109/entry-live-0.png
Normal file
|
After Width: | Height: | Size: 3.1 MiB |
BIN
docs/evidence/entry-surface-20261109/entry-live-1.png
Normal file
|
After Width: | Height: | Size: 754 KiB |
BIN
docs/evidence/entry-surface-20261109/entry-live-2.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
@ -0,0 +1,32 @@
|
||||
{
|
||||
"desktop_index": 0,
|
||||
"samples": 525,
|
||||
"kiosk": 6291462,
|
||||
"stream": 14680075,
|
||||
"stream_windows": [
|
||||
14680075
|
||||
],
|
||||
"stack_changes": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"windows": [
|
||||
14680075,
|
||||
14680075,
|
||||
14680075,
|
||||
14680075
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 0.5698,
|
||||
"windows": [
|
||||
6291462,
|
||||
6291462,
|
||||
6291462,
|
||||
6291462
|
||||
]
|
||||
}
|
||||
],
|
||||
"returned_to_kiosk": true,
|
||||
"violations": [],
|
||||
"ok": true
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
{
|
||||
"desktop_index": 1,
|
||||
"samples": 527,
|
||||
"kiosk": 6291462,
|
||||
"stream": 16777231,
|
||||
"stream_windows": [
|
||||
16777231
|
||||
],
|
||||
"stack_changes": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"windows": [
|
||||
16777231,
|
||||
16777231,
|
||||
16777231,
|
||||
16777231
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 0.6019,
|
||||
"windows": [
|
||||
6291462,
|
||||
6291462,
|
||||
6291462,
|
||||
6291462
|
||||
]
|
||||
}
|
||||
],
|
||||
"returned_to_kiosk": true,
|
||||
"violations": [],
|
||||
"ok": true
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
{
|
||||
"desktop_index": 2,
|
||||
"samples": 510,
|
||||
"kiosk": 6291462,
|
||||
"stream": 14680079,
|
||||
"stream_windows": [
|
||||
14680079
|
||||
],
|
||||
"stack_changes": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"windows": [
|
||||
14680079,
|
||||
14680079,
|
||||
14680079,
|
||||
14680079
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 0.5974,
|
||||
"windows": [
|
||||
6291462,
|
||||
6291462,
|
||||
6291462,
|
||||
6291462
|
||||
]
|
||||
}
|
||||
],
|
||||
"returned_to_kiosk": true,
|
||||
"violations": [],
|
||||
"ok": true
|
||||
}
|
||||
BIN
docs/evidence/return-before-20261109.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
docs/evidence/return-surface-20261109/final-list.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
32
docs/evidence/return-surface-20261109/return-final-0.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"desktop_index": 0,
|
||||
"samples": 534,
|
||||
"kiosk": 6291462,
|
||||
"stream": 14680075,
|
||||
"stream_windows": [
|
||||
14680075
|
||||
],
|
||||
"stack_changes": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"windows": [
|
||||
14680075,
|
||||
14680075,
|
||||
14680075,
|
||||
14680075
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 0.567,
|
||||
"windows": [
|
||||
6291462,
|
||||
6291462,
|
||||
6291462,
|
||||
6291462
|
||||
]
|
||||
}
|
||||
],
|
||||
"returned_to_kiosk": true,
|
||||
"violations": [],
|
||||
"ok": true
|
||||
}
|
||||
32
docs/evidence/return-surface-20261109/return-final-1.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"desktop_index": 1,
|
||||
"samples": 481,
|
||||
"kiosk": 6291462,
|
||||
"stream": 14680079,
|
||||
"stream_windows": [
|
||||
14680079
|
||||
],
|
||||
"stack_changes": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"windows": [
|
||||
14680079,
|
||||
14680079,
|
||||
14680079,
|
||||
14680079
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 0.6282,
|
||||
"windows": [
|
||||
6291462,
|
||||
6291462,
|
||||
6291462,
|
||||
6291462
|
||||
]
|
||||
}
|
||||
],
|
||||
"returned_to_kiosk": true,
|
||||
"violations": [],
|
||||
"ok": true
|
||||
}
|
||||
41
docs/evidence/return-surface-20261109/return-final-2.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"desktop_index": 2,
|
||||
"samples": 529,
|
||||
"kiosk": 6291462,
|
||||
"stream": 14680079,
|
||||
"stream_windows": [
|
||||
14680079
|
||||
],
|
||||
"stack_changes": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"windows": [
|
||||
14680079,
|
||||
14680079,
|
||||
14680079,
|
||||
14680079
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 0.5904,
|
||||
"windows": [
|
||||
14680079,
|
||||
14680079,
|
||||
6291462,
|
||||
6291462
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 0.6373,
|
||||
"windows": [
|
||||
6291462,
|
||||
6291462,
|
||||
6291462,
|
||||
6291462
|
||||
]
|
||||
}
|
||||
],
|
||||
"returned_to_kiosk": true,
|
||||
"violations": [],
|
||||
"ok": true
|
||||
}
|
||||
41
docs/evidence/return-surface-20261109/return-windows-2.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"desktop_index": 0,
|
||||
"samples": 530,
|
||||
"kiosk": 6291462,
|
||||
"stream": 16777227,
|
||||
"stream_windows": [
|
||||
16777227
|
||||
],
|
||||
"stack_changes": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"windows": [
|
||||
16777227,
|
||||
16777227,
|
||||
16777227,
|
||||
16777227
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 0.5718,
|
||||
"windows": [
|
||||
16777227,
|
||||
16777227,
|
||||
6291462,
|
||||
6291462
|
||||
]
|
||||
},
|
||||
{
|
||||
"t": 0.6144,
|
||||
"windows": [
|
||||
6291462,
|
||||
6291462,
|
||||
6291462,
|
||||
6291462
|
||||
]
|
||||
}
|
||||
],
|
||||
"returned_to_kiosk": true,
|
||||
"violations": [],
|
||||
"ok": true
|
||||
}
|
||||
BIN
docs/evidence/return-windows-stream-20261109.png
Normal file
|
After Width: | Height: | Size: 3.1 MiB |
@ -0,0 +1,25 @@
|
||||
Ubuntu 云桌面启动断联修复记录(2026-11-09)
|
||||
|
||||
现象
|
||||
点击 Ubuntu 后出现白色全屏过渡层和左上角 Moonlight 小窗口,随后约 30 秒内断联并回到列表。
|
||||
|
||||
证据
|
||||
盒子 /home/armbian/.xsession-errors 记录:
|
||||
Your windowmanager claims not to support _NET_ACTIVE_WINDOW
|
||||
xdo_activate_window ... error
|
||||
vdi_connection ... 云桌面画面就绪超时
|
||||
盒子会话是无 EWMH 窗口管理器的 kiosk X11 会话。Moonlight 的 SDL 窗口已创建,但旧流程只调用 xdotool windowactivate;该命令在无窗口管理器时失败,kiosk-shell 把健康的串流误判为未就绪,过渡层超时后主动终止 Moonlight。
|
||||
|
||||
修复
|
||||
1. MoonlightSession::activateStreamWindow 在 windowactivate 失败时改走 WM-less X11 回退:windowmap、windowraise、windowmove 0 0、windowsize 到显示器尺寸;以窗口已映射且铺满输出作为就绪条件。
|
||||
2. kiosk-shell 首次显示时走直接 X11 map/raise/focus 路径,避免 _NET_ACTIVE_WINDOW 为空导致盒子输入落到根窗口。
|
||||
3. 保留原有 30 秒保护超时,避免异常进程无限占用过渡层。
|
||||
|
||||
部署
|
||||
盒子 192.168.1.188 已重新编译并部署 kiosk-shell、vdi-kiosk-monitor;仅保留一个受管 kiosk 栈。部署编译通过,CTest 3/3 通过(display-guard、moonlight-session、desktop-list-page)。
|
||||
|
||||
回退
|
||||
部署脚本会在盒子端保留显示守护配置备份;源码改动可用 git checkout 回退。此次未修改 Windows VM100、麒麟 VM101 或 Ubuntu 的编码参数。
|
||||
|
||||
待现场确认
|
||||
请在盒子上点击 Ubuntu 连接,确认过渡层消失后画面保持 2K/45;若仍出现断联,立即保留 /home/armbian/.xsession-errors、/tmp/vdi-monitor.log 和 /home/armbian/vdi-terminal/logs/moonlight-20261109.log 的对应时间段。
|
||||
@ -0,0 +1,21 @@
|
||||
Ubuntu VM102 startup recovery (2026-11-09)
|
||||
|
||||
Observed:
|
||||
- VM status was running but qmpstatus=io-error.
|
||||
- QEMU guest agent was unavailable and box-to-guest route was down.
|
||||
- PVE block statistics showed one historical failed write operation.
|
||||
|
||||
Recovery:
|
||||
- Sent QMP resume through the PVE API.
|
||||
- Resume task completed OK; no VM reboot, disk change, or config change.
|
||||
|
||||
Verified after resume:
|
||||
- qmpstatus=running
|
||||
- QEMU guest agent responding (version 8.2.2)
|
||||
- guest uptime 1 day 15:16
|
||||
- GDM, Xorg, GNOME Shell running
|
||||
- Sunshine running as /usr/bin/sunshine /home/test2/.config/sunshine/sunshine.conf
|
||||
- guest dmesg showed normal EXT4 mount/remount; no new I/O or read-only error
|
||||
|
||||
Conclusion:
|
||||
- The apparent startup failure was a paused QEMU state, not a broken Ubuntu boot configuration. Reconnect Moonlight now.
|
||||
@ -1,89 +0,0 @@
|
||||
# Windows 安全删除菜单仅保留真实 USB 存储(2026-11-05)
|
||||
|
||||
## 目标和现场证据
|
||||
|
||||
客户日常使用时,内置 GPU、网卡、系统盘控制器不应出现在安全删除菜单中;真实 U 盘
|
||||
继续按插入、识别、安全弹出、物理拔出处理。这不是隐藏整个安全删除图标,也不改设备名称。
|
||||
|
||||
用户照片列出了 GTX 965M、VirtIO Ethernet、VirtIO Serial、标准 SATA AHCI 控制器。
|
||||
Windows 实际查询四项的 DEVPKEY_Device_Capabilities 均为 6,即包含 EjectSupported (2)
|
||||
和 Removable (4)。另一个内置 AHCI 控制器为 0,不能通过名称统一修改所有同名设备。
|
||||
系统盘 QEMU HARDDISK 为 60GiB SATA 启动盘、Fixed hard disk media。
|
||||
|
||||
PVE USB 查询 device_present=false、channels=[]、仅有 USB Tablet。这说明移除空的
|
||||
usb-redir 端点并不能消除菜单里的 PCI 设备。此前将托盘图标主要归因于 usb-redir 的判断
|
||||
不完整;USB 动态端点功能与固定 PCI 设备属性需要分别验收。
|
||||
|
||||
## 准备的配置
|
||||
|
||||
保留原 GPU 子厂商参数、设备地址、磁盘总线和驱动,只为当前 Q35 VM 增加:
|
||||
|
||||
```text
|
||||
-global ICH9-LPC.acpi-pci-hotplug-with-bridge-support=off
|
||||
-global pci-bridge.shpc=off
|
||||
-global pcie-root-port.hotplug=off
|
||||
```
|
||||
|
||||
分别关闭 ACPI PCI、传统 PCI bridge SHPC、PCIe root port 热插拔。
|
||||
关闭 ACPI 之后传统桥仍可能通过 SHPC 报告可移除,不能只关其中一项。
|
||||
USB 控制器仍在,USB 总线设备的添加/删除独立于 PCI 热插拔。
|
||||
取舍:以后增删这些内部 PCI 设备需要关机操作,不再支持对应 PCI 在线热插拔。
|
||||
|
||||
微软说明能力属性由设备驱动返回,为只读属性;DeviceOverrides 只改变 container ID
|
||||
分组时对 Removable 的解释,不改变设备全局能力,不能作为可靠的托盘修复依据:
|
||||
|
||||
- https://learn.microsoft.com/en-us/windows-hardware/drivers/install/devpkey-device-capabilities
|
||||
- https://learn.microsoft.com/en-us/windows-hardware/drivers/install/deviceoverrides-registry-key
|
||||
- https://github.com/qemu/qemu/blob/master/hw/pci-bridge/pci_bridge_dev.c
|
||||
|
||||
## 部署状态(已应用并冷启动)
|
||||
|
||||
09:24 已在 PVE `/root/vdi-fixed-pci-20261105/` 保存工具;
|
||||
`backup/vm.conf.before` 和 `backup/plan.json` 保存原配置和拟应用参数。
|
||||
用户随后明确授权“你自己保存然后重启”。通过当前 test 交互会话的临时诊断任务
|
||||
检查可见窗口,只有“设置”、Explorer 和输入组件;没有发现打开的文档或编辑器。
|
||||
使用 Windows `shutdown.exe /s /t 0`(没有 `/f`)正常关机,确认 stopped 后应用配置。
|
||||
09:28:17 Windows 冷启动,09:29 已通过 Guest Agent 完成检查。
|
||||
本次没有停止或重启 USB 隧道,没有强制停机,临时诊断任务均已删除。
|
||||
|
||||
工具 `tools/set-fixed-pci.py` 的 apply/restore 都要求 VM 已停止。
|
||||
有待应用配置、QEMU args 冲突、备份目标不符、原配置发生变化会拒绝操作。
|
||||
不发送关机/启动指令,不替换整个 VM 配置。应用示例(PVE root):
|
||||
|
||||
```bash
|
||||
python3 /root/vdi-fixed-pci-20261105/set-fixed-pci.py apply --vm 100 \
|
||||
--backup /root/vdi-fixed-pci-20261105/backup
|
||||
```
|
||||
|
||||
回退(同样先正常关机):把 apply 改为 restore,再冷启动。
|
||||
不是在 Windows 中点“重新启动”即可生效,必须让 QEMU 进程退出后重新建立硬件模型。
|
||||
|
||||
## 已完成验证
|
||||
|
||||
- 4 项单元测试:保留 GPU/带空格参数、重复执行不追加、冲突拒绝、运行 VM 拒绝修改。
|
||||
- PVE QEMU 11.0.3 隔离进程,TCG/暂停,无磁盘、无网络后端、无 GPU 直通;
|
||||
使用与现场一致的 Q35 machine 和 PVE readconfig。
|
||||
- 4 个 root port hotplug=false、4 个 PCI bridge shpc=false、ICH9 LPC ACPI PCI=false。
|
||||
- 同一隔离进程中 usb-redir 动态添加和删除通过;独立测试进程已经正常退出。
|
||||
- 隔离测试期间 Windows VM 100 持续 running,未插盘时 usb-redir 仍 absent。
|
||||
|
||||
冷启动后的实际 Windows 结果:
|
||||
|
||||
- 原四项 PCI 设备 Capabilities 从 6 变为 0;同名的另一个 AHCI 控制器仍为 0。
|
||||
四项的 InstanceId 未改变,所有这些设备均 Status=OK。
|
||||
- GTX965M ConfigManagerErrorCode=0,系统 SATA 启动盘 Online/Healthy,
|
||||
网卡仍为 192.168.1.70,Sunshine 在当前 test 会话运行,Guest Agent 通信正常。
|
||||
- 盒子 USB 服务 active,现场仅有键盘和鼠标,没有物理 U 盘。盒子 Moonlight 会话
|
||||
随 Windows 正常关机退出,用户需从列表重新进入 Windows。
|
||||
- 在用户会话读取到任务栏 UIA 结构,但它没有展开传统通知图标的可访问性子项,
|
||||
因此不能仅凭这份结构记录声称已目视确认图标消失;需用户检查托盘外观。
|
||||
|
||||
隔离验证不是 Windows UI 验收,也不代表真实 USB 读写和拔插已验证。
|
||||
证据在 `docs/evidence/fixed-pci-20261105/`。
|
||||
|
||||
## 现场剩余验收
|
||||
|
||||
1. 从盒子列表重新进入 Windows,确认无 U 盘时客户桌面安全删除图标消失。
|
||||
2. 真实 U 盘插入后可读写且菜单只列真实 USB;安全弹出并物理拔出后图标消失。
|
||||
|
||||
未通过本次修复承诺隐藏设备管理器或软件检测中的全部虚拟化标识。
|
||||
@ -1,93 +0,0 @@
|
||||
# GPU 直通前置核查与数据看板(2026-10-30)
|
||||
|
||||
## 硬件事实(SSH 到宿主机实测,修正旧文档误判)
|
||||
|
||||
宿主机 `starhe`(192.168.1.51,pve-manager/9.2.21,内核 7.0.2-6-pve):
|
||||
|
||||
```text
|
||||
00:02.0 VGA : Intel 4th Gen Core (Haswell) iGPU [8086:0416] 驱动 i915(宿主显示用,保留)
|
||||
01:00.0 3D : NVIDIA GM204M [GeForce GTX 965M] [10de:13d9] 驱动 nouveau(直通前需解绑)
|
||||
两者 Subsystem 均为 CLEVO/KAPOK [1558:6557]
|
||||
```
|
||||
|
||||
- 机身是 CLEVO 准系统(外形像台式机箱),GPU 是移动版 GM204M/965M——
|
||||
此前"笔记本"与"IOMMU Group 2 独占"的记载部分有误,以本页为准。
|
||||
- HDMI 音频功能(01:00.1)不存在,无需处理。
|
||||
- **IOMMU 实测已启用**(/sys/kernel/iommu_groups 有 15 组,DMAR 生效;
|
||||
内核 6.8+ 默认开启,不需要显式 intel_iommu=on)。
|
||||
- GPU 位于 **IOMMU 组 2**,组内设备:`0000:00:01.0`(root port)+ `0000:01:00.0`(GPU),
|
||||
直通需整组传入。
|
||||
- VM 100:q35 + SeaBIOS、4 核 8G、win10,未配置 hostpci。
|
||||
|
||||
## 明日直通操作单(按序执行)
|
||||
|
||||
1. 约窗口:宿主机重启会让全部 VM(100/101/102)停机。
|
||||
2. 解绑 nouveau:`/etc/modprobe.d/blacklist.conf` 加 `blacklist nouveau`,
|
||||
`update-initramfs -u`,重启宿主机(重启后 `lsmod | grep nouveau` 应为空)。
|
||||
3. 绑 vfio:`/etc/modprobe.d/vfio.conf` 写 `options vfio-pci ids=10de:13d9`
|
||||
(含 root port 的话用 QEMU 参数整组传),重启后确认
|
||||
`lspci -nnk -s 01:00.0` 显示 `Kernel driver in use: vfio-pci`(看板会自动变绿)。
|
||||
4. 挂载:`qm set 100 -hostpci0 00:01.0,pcie=1`(按组传;若启动报隔离不满足,
|
||||
用 `-hostpci0 0000:00:01.0;0000:01:00.0` 形式补传 root port)。
|
||||
vBIOS ROM 先不挂,Code 43 在 R465+ 驱动已移除检测。
|
||||
5. VM 内装驱动:Maxwell 已进 legacy 分支,装 **R580 系**驱动(提前下载好放 VM)。
|
||||
6. 验证:VM 内 `nvidia-smi` 出卡 → 看板"VM 内实时数据"卡片自动出现利用率/显存/温度。
|
||||
7. Sunshine 切 NVENC(配置 encoder=nvenc 或按其自动探测),1080p60 复测帧率。
|
||||
|
||||
## GPU 数据看板(已上线)
|
||||
|
||||
- 地址:`http://192.168.1.51:8090/`(自动刷新 4 秒)
|
||||
- 源码:`tools/gpu_dashboard.py`(宿主机 `/root/gpu_dashboard.py`,
|
||||
systemd 单元 `gpu-dashboard.service` 已设开机自启)
|
||||
- 直通前显示:型号/驱动/直通阶段/IOMMU 状态与组内设备/VM 配置;
|
||||
直通后自动经 `qm guest exec 100` 采集 nvidia-smi:利用率、显存、温度、
|
||||
驱动版本、功耗、NVENC 编码会话数。
|
||||
- 手动重启:`systemctl restart gpu-dashboard`。
|
||||
- 后续:接管理平台后,该数据源可换成平台 GPU 池接口(Web 控制台监控模块复用)。
|
||||
|
||||
---
|
||||
|
||||
# 2026-10-30 实施记录:直通落地成功,卡在 vBIOS,已回退保环境
|
||||
|
||||
## 已完成并持久化
|
||||
|
||||
1. 宿主机:blacklist nouveau + vfio.conf(ids=10de:13d9,8086:0c01) + initramfs 更新 + 重启;
|
||||
重启后 `01:00.0` 由 vfio-pci 接管,nouveau 0。
|
||||
2. VM 100:hostpci0=0000:01:00.0,pcie=1 挂载成功;客户机出现 `3D Video Controller`。
|
||||
3. 驱动:582.66 WHQL 经"PC 下载 → 宿主机 8091 HTTP → VM 内网拉取"进入 VM
|
||||
(VM 直连 NVIDIA CDN 被 403,需走内网中转);安装器兼容性检测拒绝(桌面包 INF 无移动 ID),
|
||||
改用 `pnputil /add-driver Display.Driver/nvcvi.inf /install` 成功——
|
||||
nvcvi.inf 含我们的精确 ID `PCI\VEN_10DE&DEV_13D9&SUBSYS_65571558`,
|
||||
设备绑定成功、`C:\Windows\System32\nvidia-smi.exe` 出现。
|
||||
4. 全部 VM 已设 onboot 自启;宿主机新增 systemd 服务:`gpu-dashboard`(8090)、`dl-http`(8091)。
|
||||
|
||||
## 阻塞点:GM204M 缺少可用 vBIOS,驱动初始化失败(Code 43)
|
||||
|
||||
- 宿主机 dmesg:`Invalid PCI ROM header signature: expecting 0xaa55, got 0xffff` ——
|
||||
该 Optimus 移动卡的 ROM BAR 读不出 vBIOS;sysfs 转储 0 字节。
|
||||
- 已尝试无效:`cpu host,hidden=1`;`args -cpu host,-hypervisor,hv_vendor_id=...,kvm=off`;
|
||||
`rombar=0`(设备直接不枚举)。
|
||||
- 供体 ROM 实验:TechPowerUp 上两个 "GTX 965M" 条目实为 GM206(1427) 改版(芯片不符);
|
||||
同 GM204 的 Clevo GTX 970M(13D8) ROM 经 PCIR 补丁(vendor 10de + device 13d9 + 子系统 6557)
|
||||
后 guest 仍不枚举设备。
|
||||
- 结论:**需要这块卡(或同款 GM204M/13D9)的真 vBIOS 才能完成驱动初始化**。
|
||||
|
||||
## 已回退(保客户演示环境)
|
||||
|
||||
- VM 100 已摘除 hostpci 并重启,Sunshine 正常(x264 路线),47989 开放;
|
||||
失败安装无残留(无 NVIDIA 目录)。
|
||||
- 直通重新启用只需两条命令:`qm set 100 -hostpci0 0000:01:00.0,pcie=1[,romfile=...]` + 重启 VM。
|
||||
|
||||
## 后续选项(按代价排序)
|
||||
|
||||
1. **宿主机整机冷断电 30 秒以上**(现场 1 分钟操作;Maxwell 多次热重启后设备状态可能损坏,
|
||||
冷启动是社区最后一招)→ 开机后重挂 `romfile=gtx965m-fixed.rom`(已留存
|
||||
`/usr/share/kvm/gtx965m-fixed.rom`,即 970M ROM 补丁版)再试。
|
||||
2. **获取真 GM204M(13D9) vBIOS**:任何可进系统的 965M 机器用 GPU-Z 导出
|
||||
(同事/二手渠道),或从 CLEVO BIOS 刷写包提取;放到 `/usr/share/kvm/` 后 romfile 挂载。
|
||||
3. 业务备选:若 965M 直通确认不可行,NVENC 需换卡(桌面 GM204 及以上)或改用其他硬件方案。
|
||||
|
||||
## GPU 数据看板
|
||||
|
||||
`http://192.168.1.51:8090/`(systemd `gpu-dashboard`,源码 `tools/gpu_dashboard.py`)。
|
||||
驱动一旦可初始化,"VM 内实时数据"卡片自动显示利用率/显存/温度/NVENC 会话,无需改代码。
|
||||
@ -1,49 +0,0 @@
|
||||
# GTX 965M 重启故障诊断(2026-11-02)
|
||||
|
||||
## 结论
|
||||
|
||||
未找到已验证有效的重启后 NVENC 恢复方案。所有实验配置已恢复,VM100 可继续使用软件编码回退;VM101/102 全程保持运行。
|
||||
|
||||
此前582.78、DXGI与NVENC确实工作过;此轮故障状态为Windows Code43、nvlddmkm停止、nvidia-smi失败、Sunshine回退libx264。不能以设备管理器显示型号或PnP暂时OK作为恢复成功的依据。
|
||||
|
||||
## 原始配置与备份
|
||||
|
||||
宿主机备份目录:`/root/vdi-gpu-recovery-20261102/`,包含100.conf、vfio.conf、ROM及PCI信息。
|
||||
|
||||
- hostpci0:`0000:01:00.0,pcie=1,rombar=1,romfile=gtx965m-pci.rom`
|
||||
- args:`-set device.hostpci0.x-pci-sub-vendor-id=0x1558 -set device.hostpci0.x-pci-sub-device-id=0x6557`
|
||||
- GPU:10de:13d9,子系统1558:6557,总线01仅此设备。
|
||||
- 原ROM SHA256:`080d9765337a5ea3bceefb1f632f073e14b09d215ef44b885e2d97cb04d27160`
|
||||
- `/etc/modprobe.d/vfio.conf`:`options vfio-pci ids=10de:13d9 disable_idle_d3=1`,本轮未修改该文件。
|
||||
- Windows快速启动原值HiberbootEnabled=1;本轮临时关闭后无收益,已恢复1。
|
||||
|
||||
## 实验与结果
|
||||
|
||||
| 实验 | 结果 |
|
||||
|---|---|
|
||||
| 关闭Windows快速启动,VM完整停止/启动 | Code43仍存在 |
|
||||
| 临时disable_idle_d3=N,再解绑/重绑VFIO | D0 active、runtime_usage=1;无法证明完成电源周期 |
|
||||
| 干净卸载并重载仅vfio_pci模块,disable_idle_d3=0 | 引用计数1→0;实测runtime suspended、D3cold;启动VM后D0,但Code43/NVML失败仍存在 |
|
||||
| 原供体ROM校验和修正副本 | Windows Guest Agent未上线,未保留 |
|
||||
| 本机既有sysfs ROM副本 | Windows启动但Code43/NVML失败 |
|
||||
| 本机ROM+已确认D3cold电源周期 | 同样失败,未保留 |
|
||||
|
||||
没有写PMCSR、没有刷写显卡固件、没有重启宿主机、没有卸载KVM或VFIO核心模块;仅在VM100停止且无VFIO使用者时重载vfio_pci。最终模块参数已恢复Y,原ROM配置已恢复。
|
||||
|
||||
## ROM证据与局限
|
||||
|
||||
1. 当前原始供体ROM为184320字节:传统BIOS镜像110080字节,PCIR ID10de:13d9,镜像和模256为1;第二UEFI镜像74240字节仍为10de:13d8。版本84.04.22.00.11。这些是明确的不一致,但不能认定为唯一故障原因;VM使用SeaBIOS,UEFI ID不匹配不等同于此次启动必然失败。
|
||||
2. 既有本机sysfs ROM为110592字节,10de:13d9、校验和0、版本84.04.2B.00.13,字符串含HWDIAG。尚未确认是完整的、适配此板卡的生产版固件;不能只凭校验和正常就当作可靠替代。
|
||||
3. 两份ROM均未刷入硬件。测试副本留在宿主机,当前VM不使用它们。
|
||||
|
||||
## 下一步所需条件
|
||||
|
||||
- 优先由运维/设备厂商提供该CLEVO板卡的生产版GM204M/13D9 vBIOS及适配依据,保留原件后再验证。
|
||||
- 如需要宿主机冷启动、内核/VFIO升级或平台固件实验,先安排维护窗口,备份全部VM与宿主启动配置;不能影响其他VM而不协调。
|
||||
- 当前证据只证明D3cold电源周期可实现,并不证明它能够修复此显卡;不要添加反复复位/重启任务充当永久修复。
|
||||
|
||||
## 验收标准
|
||||
|
||||
nvidia-smi成功、DXGI枚举NVIDIA、Sunshine实际选择h264_nvenc、真实串流有编码利用率;随后Windows重启和VM停启后重复通过。2K显示、20FPS配置和正常Windows单光标应同时保留。
|
||||
|
||||
参考:[Linux PCI](https://docs.kernel.org/PCI/pci.html)、[VFIO](https://docs.kernel.org/driver-api/vfio.html)、[PCI sysfs](https://docs.kernel.org/PCI/sysfs-pci.html)、[Proxmox qm](https://pve.proxmox.com/pve-docs/qm.1.html)。
|
||||
@ -1,52 +0,0 @@
|
||||
# 云桌面图形电源操作限制(2026-11-04)
|
||||
|
||||
本次已通过现有 PVE Guest Agent 在三台正在运行的云桌面部署持久策略,限制当前客户桌面账户的关机、重启、睡眠和休眠入口。此修改发生在客体系统中,不依赖 Moonlight 的键盘过滤补丁是否已编译部署。
|
||||
|
||||
| 客体 | 限制账户 | 已应用策略 |
|
||||
| --- | --- | --- |
|
||||
| Windows VM100 | `test` | 当前用户 Explorer `NoClose=1`,移除开始菜单和 Windows 安全界面的电源命令 |
|
||||
| 麒麟 VM101 | `test1` | polkit 0.105 `.pkla` 拒绝电源权限,加系统 D-Bus 用户级电源方法拒绝规则 |
|
||||
| Ubuntu VM102 | `test2` | polkit `.rules` 拒绝电源权限,加系统 D-Bus 用户级电源方法拒绝规则 |
|
||||
|
||||
Windows 在实际登录用户的交互会话内广播 `WM_SETTINGCHANGE/Policy`,并调用 `SHRestricted(REST_NOCLOSE=2)` 验证有效结果为 1。未重启 Explorer、注销用户或重启 Windows。临时验证任务 `VDIPowerPolicyProbe` 在执行后删除。
|
||||
|
||||
Linux 同时限制 login1 的 PowerOff、Reboot、Halt、Suspend、Hibernate、HybridSleep、SuspendThenHibernate、ScheduleShutdown 及对应 WithFlags 方法,并拒绝桌面用户的 Can* 查询。菜单不能再获得电源操作能力,电源请求也会在系统总线被拒绝。D-Bus 仅 ReloadConfig,不重启总线或图形会话。
|
||||
|
||||
实测发现两台 Linux 当前图形会话的 `pkcheck` 仍对基础 reboot 动作返回授权,即使后台 CanReboot 查询已返回 no;因此不能只用 polkit 后台查询宣称重启已屏蔽。此现象的内部原因未确定。本次以额外 D-Bus 用户拒绝规则封住实际请求路径。定位过程中仅在麒麟重启过一次 polkit 服务,Sunshine 和图形会话保持运行。
|
||||
|
||||
## 验证范围
|
||||
|
||||
- Windows 注册表读回和交互 Shell 有效策略均为 1。
|
||||
- 两台 Linux 七项 Can* 查询均被系统总线拒绝;八种电源方法的错误参数探测均返回 Access denied。
|
||||
- 电源探测故意使用字符串参数 `s VDI_INVALID_SIGNATURE`;正常方法需要 boolean、flags 或其他签名。即使拒绝规则失效,该参数也无法通过 logind 的签名校验,不会形成有效关机/重启请求。
|
||||
- 两台 Linux 使用实际 ukui-session/gnome-shell 进程检查 polkit,并记录上述 reboot 授权残留;额外总线拒绝验证通过。两台 vdi-sunshine 均 active,root 的 CanPowerOff 仍 yes。
|
||||
- 三台 VM 均 running;Windows LastBoot 和 Linux boot ID 与本轮部署前一致。
|
||||
- 未点击会真实执行关机的按钮,未进行重启后验证,也未逐项验收各桌面的菜单外观或缓存刷新表现。菜单若暂时仍显示旧项目,Linux 后端仍拒绝电源调用。
|
||||
|
||||
原始结果位于 [evidence/guest-power-policy-20261104](evidence/guest-power-policy-20261104/)。
|
||||
|
||||
## 重复部署与恢复
|
||||
|
||||
工具:`tools/set-guest-power-policy.ps1`(Windows)、`tools/set-guest-power-policy.py`(Linux)。管理机上的 `tools/deploy-guest-power-policy.py` 使用已有 PVE 认证和 Guest Agent,无新增凭据。
|
||||
|
||||
```powershell
|
||||
python tools/deploy-guest-power-policy.py apply --vm 100
|
||||
python tools/deploy-guest-power-policy.py apply --vm 101 --user test1
|
||||
python tools/deploy-guest-power-policy.py apply --vm 102 --user test2
|
||||
```
|
||||
|
||||
把 `apply` 换成 `query` 可查询,换成 `restore` 可恢复本轮首次部署前的值/文件;重复 apply 不覆盖初始备份。新镜像或新客户账户应针对实际账户重新执行,不能假设策略自动覆盖新用户。
|
||||
|
||||
Windows 备份:`C:\ProgramData\VDI\power-policy\original.clixml`。
|
||||
|
||||
Linux 备份:`/var/lib/vdi/power-policy/`。目标文件为:
|
||||
|
||||
- 麒麟:`/etc/polkit-1/localauthority/90-mandatory.d/99-vdi-power.pkla`。
|
||||
- Ubuntu:`/etc/polkit-1/rules.d/00-vdi-power.rules`。
|
||||
- 两者:`/etc/dbus-1/system.d/zz-vdi-desktop-power.conf`。
|
||||
|
||||
本策略限制客户图形电源入口,保留 root、管理员和 PVE 维护能力;不把具有管理员权限的客户账户变成安全隔离账户,不阻止管理员使用命令维护,也不修改自动更新行为。本轮未改变锁屏、注销或切换用户策略。
|
||||
|
||||
盒子断电会中断客户端连接,并不自动等于云端 VM 关机;本次未实现“盒子物理开关控制 VM 生命周期”的管理平台联动。
|
||||
|
||||
依据:[Microsoft NoClose 策略](https://learn.microsoft.com/en-us/windows/client-management/mdm/policy-csp-admx-startmenu#noclose)、[login1 电源接口](https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.login1.html)。
|
||||
@ -1,64 +0,0 @@
|
||||
# 独立光标二次开发设计与接入条件
|
||||
|
||||
> 2026-11-05更新:Ubuntu/麒麟已部署单座席X11受限方案,通过认证SSH同步形状,在新连接前启动无光标捕获并在整次会话结束后恢复服务,见[实施记录](linux-native-cursor-20261105.md)。以下是11月3日Windows正式控制协议设计与当时现场状态;该Windows协议仍未实现,不能混同Linux新方案。
|
||||
|
||||
状态:已通过受控的 Windows 光标导出 + 盒子 X11 本地指针原型验证;尚未实现 Sunshine/Moonlight 的自动独立光标通道,不应标记为生产功能已部署。测试已结束,当前恢复单个 Windows 远程白色光标。实测结果见 [独立光标可行性验证](independent-cursor-feasibility-20261103.md)。
|
||||
|
||||
## 已核实的实际路径
|
||||
|
||||
- 盒子固定 Moonlight Qt 6.2.0,现有 absolute mouse 输入仍经 moonlight-common-c 的可靠输入通道发送。
|
||||
- 当前 RKMPP/EGL 路径使用 PacerRender 独立线程;实际诊断会话没有主线程 render 调用,输入优先调度不会改变这个渲染路径。
|
||||
- Sunshine `input.cpp` 的 Ctrl+Alt+Shift+N 直接翻转全局 `display_cursor`。`video.cpp` 将其地址传入 display capture。它没有按会话的目标状态、确认或租约,不能作为可靠的独立光标协议。
|
||||
- Windows Desktop Duplication 可能返回独立光标形状,也可能返回已含光标的画面;不能假定所有显示设备都能从视频中无损移除光标。
|
||||
- Windows 当前没有现成的 Sunshine C++ 构建环境。因此本轮可以构建盒子客户端、验证输入和刷新率,但不能把未构建的服务端补丁当成已完成联调。
|
||||
|
||||
参考:[Sunshine input.cpp](https://github.com/LizardByte/Sunshine/blob/master/src/input.cpp)、[video.cpp](https://github.com/LizardByte/Sunshine/blob/master/src/video.cpp)、[Windows RAM capture](https://github.com/LizardByte/Sunshine/blob/master/src/platform/windows/display_ram.cpp)、[Microsoft Desktop Duplication 光标说明](https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/desktop-dup-api)。master 仅用于核实设计,实际修改须先固定现场 Sunshine 版本对应提交。
|
||||
|
||||
## 实施方式
|
||||
|
||||
在 Sunshine 与 moonlight-common-c/Moonlight 间扩展现有加密控制通道;不另外开放未经认证的 UDP 光标端口。默认仍使用远程视频光标,仅在双方协商并确认支持后启用。新增消息必须有协议版本、会话标识、显示配置代数及严格的大小校验。
|
||||
|
||||
| 消息 | 必要内容与作用 |
|
||||
| --- | --- |
|
||||
| 能力协商 | 支持形状类型、最大尺寸、显隐/视频帧交接能力;旧客户端保持原行为 |
|
||||
| 光标形状 | 形状版本、尺寸、热点、像素/掩码格式;形状变化时发送并缓存 |
|
||||
| 光标状态 | 状态序号、形状版本、显隐、坐标、显示区域及缩放配置代数 |
|
||||
| 交接请求/确认 | 明确目标模式,携带首个采用该模式的视频帧 ID;不能使用无确认的 toggle |
|
||||
| 位置校正 | 服务端主动移动与最近输入序号关联;用户持续移动时不能用旧位置反复拉回 |
|
||||
| 心跳/结束 | 会话租约、显式结束;连接丢失时服务端自动恢复视频光标 |
|
||||
|
||||
光标移动由盒子的本地指针立即显示,形状和显隐仍取自服务端。窗口、按钮响应和内容更新继续走视频,所以独立光标不等于窗口拖动零延迟。
|
||||
|
||||
## 单光标交接
|
||||
|
||||
1. 服务端确认当前捕获后端可独立控制光标,客户端收到并成功创建当前形状;否则保持远程模式。
|
||||
2. 服务端在明确的视频帧边界停止混合光标,并发出该帧 ID。客户端显示到这个边界后才开启本地光标,避免旧视频仍有光标时出现双指针。
|
||||
3. 结束独立模式时反向交接,在开始包含远程光标的视频帧上隐藏本地光标。
|
||||
4. 辅助状态超时、形状不支持、捕获设备更换、显示缩放改变、解码器重建、第二路会话接入时,执行确认后的回退。控制连接完全断开且无法继续交接时结束串流,返回选择页。
|
||||
5. 不能通过替换 Windows 全局鼠标主题为透明图片实现;不能把普通箭头覆盖在视频光标上。
|
||||
|
||||
心跳和租约先按 250ms/1000ms 做实验参数,最终值由 Wi-Fi 抖动及故障恢复测试决定。现有全局 display_cursor 还须改为与会话/捕获生命周期一致的状态,防止多个客户端互相覆盖。
|
||||
|
||||
## Windows 特殊情况
|
||||
|
||||
- 处理彩色 alpha、单色 AND/XOR 和 masked-color;不支持的形状明确回退。不能把 XOR 像素简单当透明像素。
|
||||
- 热点随流画面缩放映射,窗口信箱区域不发送越界输入;切换分辨率后废弃旧代数的光标消息。
|
||||
- 服务端隐藏指针、文字选择、拖动缩放、忙碌动画必须正确;锁屏/UAC 桌面切换不能继续显示过期光标。
|
||||
- SDL 硬件指针操作留在主线程;网络线程仅提交有界状态,禁止直接调用 SDL_SetCursor/释放正在使用的 cursor。
|
||||
- 本地移动与远程主动 warp 分开处理,避免拖动时抖动、回弹;点击仍使用已修复的按钮坐标/输入顺序。
|
||||
|
||||
## 必须通过的验收
|
||||
|
||||
| 场景 | 要求 |
|
||||
| --- | --- |
|
||||
| 移动/拖动/右键/快速双击 | 单光标、点击准确、无丢失的按键释放 |
|
||||
| 箭头/文字/调整大小/忙碌/隐藏/自定义 | 形状、热点及显隐正确;不支持时可靠回退 |
|
||||
| 100%/125%/150% 缩放、分辨率切换 | 无偏移;旧代数消息失效 |
|
||||
| 辅助消息停止、控制断开、服务器重启 | 无持久双光标或无光标状态 |
|
||||
| 窗口失焦、鼠标离开、重新捕获 | 不锁死本地系统指针;恢复后坐标正确 |
|
||||
| 旧服务端/客户端、多会话 | 不误启用;保持原有远程模式 |
|
||||
| 高速摄像对照 | 分别报告指针移动、点击响应、拖窗内容更新的 P50/P95 |
|
||||
|
||||
## 下一阶段开始条件
|
||||
|
||||
固定现场 Sunshine 版本及源码提交,在独立构建目录准备可复现 Windows 构建,备份原安装并在测试实例验证后,再修改双方协议和捕获交接。不得为部署光标实验重装当前 Windows、刷 GPU ROM 或中断其他虚拟机。
|
||||
@ -1,55 +0,0 @@
|
||||
# 独立光标原型可行性实测(2026-11-03)
|
||||
|
||||
> 2026-11-05更新:Linux两台主机已接入不同于本原型的认证SSH原生光标方案,见[实施记录](linux-native-cursor-20261105.md)。本页保留11月3日Windows受控实验;Windows仍走原视频光标,正式双方控制协议尚未实现。
|
||||
|
||||
## 结论和当前现场
|
||||
|
||||
本地独立移动和 Windows 光标静态形状显示可行,值得继续开发 Sunshine + Moonlight 的正式同步协议。本轮完成了真实盒子和 VM100 的受控原型,不是仅做源码调研。但是自动形状同步、视频帧交接、断线回退尚未成为产品功能,不能默认启用,也不能称鼠标或窗口零延迟。
|
||||
|
||||
测试已结束,Windows 临时任务 VDICursorPOC 已停止并删除。正常 Moonlight PID31694、2560×1440/45FPS、RKMPP/EGL 和原二进制 SHA256 `35de7b1a4ade583823b23b5f2e012307f6d0a3848c8eb97bd6d4de9660afa00f` 保留。VDD读回 DISPLAY3、2560×1440@60。最后确认本地指针为全透明1×1,屏幕里只有正常Windows远程白色光标;没有遗留测试辅助进程。GTX965M错误码0,编码占用11%,Sunshine该正常会话创建了h264_nvenc。没有重启机器、修改GPU固件/直通、Windows鼠标主题或正式客户端配置。
|
||||
|
||||
## 原型做了什么
|
||||
|
||||
Windows已登录用户会话运行临时测试窗口,用 GetCursorInfo 读取实际光标状态;GetIconInfo/DrawIconEx 导出默认箭头、文字、左右缩放、忙碌四种真实Windows形状,均为32×32,热点分别为(0,0)、(8,9)、(11,4)、(16,16)。导出仅复制资源并释放 GetIconInfo 返回的位图,没有替换系统主题。通过黑白背景双次绘制重建alpha,检测到依赖背景反色的像素就拒绝;这四个样本未检测到此类像素,不代表全部自定义光标都支持。
|
||||
|
||||
管理工作站通过已有的认证PVE guest-agent通道读取导出文件,再经SSH传给盒子。没有新增网络监听端口,盒子没有直接使用PVE凭据。盒子辅助程序在现有Moonlight的X11窗口上创建本地指针,形状由测试脚本按已知测试区域顺序切换。**这是脚本控制的形状回放;没有将GetCursorInfo状态自动、实时同步到客户端。** Windows另存状态变化记录,供交叉核对。
|
||||
|
||||
测试临时使用Sunshine的Ctrl+Alt+Shift+N隐藏视频光标,最后成对恢复。它只是原型手段,没有会话确认/租约,不能作为正式交接协议。正常Moonlight二进制未重编或替换;本轮验证的是底层X11本地指针可用性。正式功能应进入Moonlight的SDL主线程,并适配后续Weston/Wayland,不能把这份X11辅助程序当最终架构。
|
||||
|
||||
## 实测结果
|
||||
|
||||
| 验证项 | 结果与边界 |
|
||||
| --- | --- |
|
||||
| 四种形状及热点 | XFixes读取本地指针,尺寸/热点均与Windows导出一致;忙碌只验证一帧,未实现动画 |
|
||||
| 光标显隐 | 本地隐藏状态为全透明1×1;Windows记录也捕获到隐藏flags=0 |
|
||||
| 移动独立于视频 | 仅将当前Moonlight进程短暂停住,小于0.5秒,并保证SIGCONT恢复;本地指针仍从(320,500)移动到(500,650),图像保持一致。这证明依赖关系解除,没有测得物理输入到屏幕毫秒数 |
|
||||
| 单光标受控稳态 | 原始XWD截图在禁用后不再包含远程指针,XFixes另行确认本地指针存在。XWD不包含本地硬件指针,不能将原始截图无指针误报为本地显示失败;未用合成图冒充屏幕截图 |
|
||||
| 点击正确性 | 最后完整一轮32次跨四区点击,Windows记录32/32区域及形状正确;绝对坐标归一化差值最大1像素,不宣称零偏差 |
|
||||
| 辅助状态超时 | 0.8秒测试租约过期,辅助程序退出并通过SDL恢复其拥有的隐藏指针;测试控制器随后恢复远程视频光标。没有证明真实控制断网时服务器能自动回退 |
|
||||
| 输入数据边界 | 10项测试通过,覆盖尺寸、热点、像素长度/范围、重复形状、XOR拒绝、格式、未知形状、过期/异常租约及文件大小 |
|
||||
|
||||
最后完整轮次为 `cursor-poc-evidence-v3`。最初两轮已验证形状、移动和点击,但辅助程序退出后当前XFixes图像暂不可用,不能算完整通过;修正后才通过完整流程。没有把前两轮的失败隐藏成成功。
|
||||
|
||||
## 暴露的两个实际问题
|
||||
|
||||
1. **静止画面的旧光标会留在旧视频帧里。** 第一轮切换快捷键后立即截图,原位置的远程箭头仍在;之后画面更新才消失。最后一轮加入测试窗口时钟更新并等待新帧,禁用后的光标区域变为空背景,恢复后的区域重新出现Windows忙碌光标。正式实现必须用双方确认的视频帧边界切换,不能只延时几百毫秒后直接显示本地箭头。
|
||||
2. **X11指针资源有连接所有权。** 辅助程序在自己连接上复制一个隐藏指针,再关闭连接,并不足以可靠恢复SDL原先的资源。原型现在在XFixes临时隐藏保护下,使用Moonlight本地光标快捷键成对切换,让SDL重新设置它自己拥有的隐藏指针,再释放辅助资源。此清理依赖同一前台绝对鼠标会话,不能代替正式客户端内部的资源生命周期管理。代码失焦时明确报错,不声称任意退出路径都能可靠恢复。
|
||||
|
||||
## 下一阶段执行顺序
|
||||
|
||||
1. 固定现场Sunshine版本对应提交,在独立目录准备Windows可复现构建;当前未准备好该服务端C++构建环境。先验证未经修改的构建,再备份/部署测试实例,避免直接替换正常服务。
|
||||
2. 在现有已认证、加密的控制通道添加能力协商、形状/热点、显隐、状态序号与显示配置代数。Moonlight将有界状态投递SDL主线程,缓存形状,本地移动直接跟随物理输入;旧服务端保持远程模式。
|
||||
3. Sunshine提供有明确目标状态、帧边界确认和会话租约的光标交接,代替全局toggle;未捕获到可分离光标或形状不支持时回退。锁屏/UAC、远程主动warp、动画、缩放、多会话也须覆盖。
|
||||
4. 验收断网/重连/失焦、文字选择、拖窗、右键/双击及100%/125%/150%缩放,再用高速摄像分别测指针移动、点击反馈、窗口内容的P50/P95。通过后再开放可关闭的试用开关。
|
||||
|
||||
独立光标可以绕开捕获、编码、网络、解码对“指针位置显示”的等待;窗口本身和点击后的页面仍需视频返回。它不会直接解决当前盒子2K60只实渲染约46FPS的问题,视频呈现优化仍按此前对照结论推进。
|
||||
|
||||
## 文件与复核
|
||||
|
||||
- `tools/cursor-poc-host.ps1`、`tools/run-cursor-poc-host.py`:临时Windows交互任务的start/status/export/stop。不自动触发、不修改Sunshine。
|
||||
- `tools/cursor-poc-x11.py`:有界资产/状态解析、静态alpha形状、本地租约、SDL所有权归还;必须显式选择同一个前台Moonlight绝对鼠标窗口。仅用于受控实验。
|
||||
- `tools/verify-cursor-poc-box.py`:受控演示、32点击、短暂停客户端及恢复;**仅可在上述Windows测试窗口位于前台时运行,不能向用户普通桌面盲目注入点击。** 输出目录拒绝覆盖,复跑须使用新目录名。
|
||||
- `tools/test-cursor-poc.py`:Linux边界测试,不更改指针。
|
||||
- `docs/evidence/cursor-poc-20261103/`:Windows资产和状态/点击记录、XFixes形状、原始截图转换、首轮失败证据、最终成功结果、超时日志、GPU/显示/本地指针收尾查询。压缩包保留原始XWD。
|
||||
|
||||
实现依据:[Microsoft GetCursorInfo](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorinfo)、[GetIconInfo](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-geticoninfo)、[Desktop Duplication光标元数据](https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/desktop-dup-api)。正式协议及验收要求继续见 [双方二次开发设计](independent-cursor-design-20261103.md)。
|
||||
@ -1,51 +0,0 @@
|
||||
# 鼠标点击与画面跳转优化(2026-11-03)
|
||||
|
||||
本轮针对用户反馈的“鼠标拖动/点击有残影、点击后画面跳转慢”做了输入队列和低延迟呈现优化。已有单白色 Windows 远程光标、显示器开关自动恢复和 RKMPP/EGL 客户端均保留。
|
||||
|
||||
## 现场根因
|
||||
|
||||
1. **点击位置顺序会被旧的绝对鼠标移动批次覆盖。** Moonlight 上游为了减少包数,会让一个尚未发送的绝对鼠标包持续吸收后续位置。高速移动后点击时,点击包与位置包在网络队列中的边界不明确,画面中的 Windows 控件可能在旧位置响应。32 次左右交替点击的实测中,修改前收到 32 条点击记录,其中第 1、13 条落在错误的一侧(30/32 正确)。这是延迟体感之外的可重复顺序错误。
|
||||
2. **鼠标移动合并会扫描 SDL 队列。** 原逻辑只按 `SDL_MOUSEMOTION` 类型取事件,可能跨过按钮/键盘/窗口事件,把点击后的移动提前合并。修改后只消费紧邻且属于同一鼠标设备/窗口/按键状态的移动,遇到边界立即停止,每次最多处理 64 条,避免输入线程长时间占用。
|
||||
3. **低延迟模式的解码帧积压。** 当前服务端仍为软件 `libx264`、约 30 FPS;盒子已使用 `h264_rkmpp` + `drm_prime` + EGL/GLES。修改后的非 VSync pacing 呈现队列可以丢弃尚未呈现的等待帧,只保留最新等待帧,减少画面落后于点击的时间。正在呈现的帧、上一帧延迟释放和解码参考帧不会被释放。
|
||||
|
||||
## 实现
|
||||
|
||||
- `terminal/moonlight/patches/0001-vdi-input-and-latest-frame.patch`
|
||||
- SDL 输入事件按边界合并。
|
||||
- 绝对鼠标按钮使用该按钮事件的坐标先入队。
|
||||
- `VDI_LATEST_FRAME=1` 且未启用 VSync pacing 时只替换等待呈现帧;默认 wrapper 开启,smooth/pacing 档保持上游行为。
|
||||
- `terminal/moonlight/patches/0002-vdi-absolute-click-barrier.patch`
|
||||
- 每个点击封住前一位置批次,点击后的移动进入新批次。
|
||||
- 发送线程只解除自己包的“可吸收新位置”状态,不误清除点击后新批次。
|
||||
- `terminal/kiosk-shell/scripts/vdi-moonlight-rkmpp` 默认 `VDI_LATEST_FRAME=1`,仍不创建本地黑光标、不叠加第二个光标。
|
||||
- `tools/build-rkmpp-client.sh` 固定 Moonlight/MPP/FFmpeg 提交,构建前检查并幂等应用补丁;出现源码漂移会失败,不强行覆盖。
|
||||
- `tools/set-desktop-responsiveness.ps1` 已在 Windows 登录用户会话执行:备份原设置,关闭窗口缩放动画、菜单等待从 400ms 改为 100ms;通过 `SystemParametersInfoW` 设置并读回校验成功,临时执行任务已删除。失败时脚本会自动恢复。
|
||||
- 盒子显示守护改读 Xorg 当前配置 `xrandr --current`,保留热插拔恢复,避免每 2 秒重新探测 EDID/显示模式引发 vendor 驱动时钟日志;对应 9 个场景测试通过。
|
||||
- 用户明确蓝框发生于盒子登录/选择桌面页:QLabel 继承 QFrame,原 `QFrame:hover` 蓝框会作用于文字。移除该悬停规则,卡片样式限定到对象名,移除登录输入框的蓝色焦点边框。实际悬停截图文字/卡片区域亮蓝色像素从 8,302 降到 0,按钮仍可正常使用。
|
||||
|
||||
## 自动化与现场测试
|
||||
|
||||
| 测试 | 结果 |
|
||||
|---|---|
|
||||
| SDL 事件边界、64 条上限、触摸鼠标隔离、最新帧 helper | 通过 |
|
||||
| 绝对输入 1,000 轮“位置→位置→按下→位置→释放→位置” | 通过 |
|
||||
| AddressSanitizer 版本上述 native 测试 | 通过,无内存错误 |
|
||||
| 修改前 32 次交替点击 | 30/32 正确,错误序号 1、13 |
|
||||
| 修改后 32 次交替点击 | 32/32 正确 |
|
||||
| 修改后真实 RKMPP/EGL 串流 | 日志确认 `VDI latest-frame queue: enabled`、`h264_rkmpp`、2560×1440 DRM PRIME |
|
||||
| 配对对照串流 | 原版:28.93 接收/28.48 渲染 FPS,队列 3.39ms,解码 10.49ms,呈现 6.84ms;补丁:28.40/28.09 FPS,队列 2.14ms,解码 7.96ms,呈现 6.12ms |
|
||||
| 会话连续性 | 测试退出后没有遗留测试串流;显示守护 active;盒子仍 2560×1440 |
|
||||
|
||||
对照数据不是严格实验室基准:两段会话的网络、Windows 软件编码负载和页面内容会波动。补丁最明确的收益是点击顺序正确和队列深度下降约 1.25ms;它不能把当前服务端软件编码变成 NVENC,也不能保证端到端零延迟。测试期间出现过一次 Wi-Fi 从 completed 到 disconnected 后自动重连,受影响的数据已单独保留,未作为性能结论。
|
||||
|
||||
## Windows 桌面跳转动画
|
||||
|
||||
执行前 Windows 登录用户状态为 `DragFullWindows=1`、`MouseTrails=0`、`MinAnimate=1`、`MenuShowDelay=400`。已将窗口动画改为 0、子菜单等待改为 100ms,API 读回校验成功,保留完整窗口拖动和原远程单光标。原值备份到 `%LOCALAPPDATA%\VDI\desktop-responsiveness-before-20261103.json`,恢复使用同一脚本的 `-Restore`。这些设置只减少 Windows 自身的动画和子菜单等待,不代表网页加载或网络响应变快。API 依据为 [Microsoft SystemParametersInfoW](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow)。
|
||||
|
||||
## 当前状态与回退
|
||||
|
||||
补丁客户端已接入默认 wrapper,启用最新帧队列。kiosk 为移除蓝色悬停框做了局部编译、CTest 3/3、原监控器拉起新版页面;未运行完整部署脚本,未重启盒子、VM 或宿主。UI 和 wrapper 备份为 `~/vdi-terminal/backups/input-rendering-20261103-114542/`。补丁源码、测试和构建说明均在 `terminal/moonlight/`,证据在 `docs/evidence/input-latency-20261103/`。
|
||||
|
||||
如需临时回退客户端,下次启动 wrapper 时设置 `VDI_RKMPP_CLIENT=/home/armbian/vdi-build/rkmpp-experiment/build/moonlight/app/moonlight.before-input-20261103`、`VDI_LATEST_FRAME=0`。旧二进制 SHA256 为 `a3575f86b9ebfd3b9f2a6ae4c73b057b2c8cd489cc44549a8838ff459c5e89dc`,新二进制为 `35de7b1a4ade583823b23b5f2e012307f6d0a3848c8eb97bd6d4de9660afa00f`。回退后重新连接并查看统计;不要直接覆盖运行中的 ELF。
|
||||
|
||||
下一步为用户真实鼠标拖窗/右键/快速双击体验验收;严格延迟需通过高速摄像或外部触发测输入到屏幕 P50/P95。当前数据不能宣称“无延迟”,只能确认点击排序和客户端排队得到改善。
|
||||
@ -1,52 +0,0 @@
|
||||
# 麒麟鼠标掉帧继续排查(2026-11-04)
|
||||
|
||||
本轮保留的改动是**仅麒麟主机协商 limited 视频范围**,避免旧版 Sunshine 的额外全范围转换。真实桌面和视频仍为2560×1440、H.264、30Mbps,正常卡片仍请求30FPS。同场景45FPS请求的对照中,实际渲染由21.42提高至28.81FPS(约35%);尚未达到Windows约43~44FPS的既有水平,也未证明物理鼠标到屏幕延迟与Windows相同。
|
||||
|
||||
## 现状与改动
|
||||
|
||||
- Sunshine恢复官方v0.21.0原文件,SHA256为`1c3099bf924db3aee759b3d7c05e012569ef8650c400d1479144398795decc71`。保持X11、ultrafast、zerolatency、原单线程、暂停UKUI合成器,无额外capability。
|
||||
- 盒子`vdi-moonlight-rkmpp`支持逐主机色彩范围文件:`${XDG_CONFIG_HOME:-$HOME/.config}/vdi-terminal/host-color-range/<host>`,内容只允许`0`(limited)或`1`(full)。仅配置`192.168.1.71`为`0`。显式`COLOR_RANGE_OVERRIDE`环境变量优先,无配置的主机沿用Moonlight默认行为。
|
||||
- 服务端实际日志从`Color range: [JPEG]`变为`[MPEG]`;客户端解码后仍扩展成正常屏幕RGB,不是将黑白截成灰色。未改客户端二进制、分辨率、码率、Windows/Ubuntu卡片或PVE配置。
|
||||
- wrapper已部署,语法检查、麒麟/Windows/Ubuntu隔离、显式环境覆盖和非法配置拒绝检查通过,见[配置验证](evidence/kylin-mouse-20261104/wrapper-profile-check.json)。用户明确允许切回麒麟后,已从正常kiosk列表连接,确认父进程为kiosk、2560×1440/30FPS/30Mbps、`COLOR_RANGE_OVERRIDE=0`,实际2K RKMPP/EGL解码和服务端`[MPEG]`日志通过。最终保留麒麟桌面连接,见[正常入口](evidence/kylin-mouse-20261104/final-kiosk-connection.json)及[最终桌面](evidence/kylin-mouse-20261104/final-normal-desktop.png)。
|
||||
|
||||
备份:盒子`~/vdi-terminal/backups/kylin-mouse-20261104/`有原wrapper和desktops.json;麒麟同目录有原sunshine.conf。回退删除麒麟色彩范围文件,下次连接恢复原协商;必要时恢复wrapper备份。没有修改其他主机的配置文件。
|
||||
|
||||
## 测试证据
|
||||
|
||||
局部运动GTK源约60FPS,所有以下有效轮次均核对真实未锁屏测试窗口,并发送F8和24次交替点击。每轮请求2560×1440/45FPS/30Mbps,短时顺序测试;网络延迟和抖动有波动,不作为长期保证。
|
||||
|
||||
| 服务端 / 协商 | 实际渲染FPS | 解码ms | 队列ms | 采用情况 |
|
||||
| --- | ---: | ---: | ---: | --- |
|
||||
| 原程序 / full |21.42|7.48|0.15|对照|
|
||||
| 原程序 / limited |28.81|6.90|0.23|保留limited协商|
|
||||
| 原程序 / limited / 两线程 |25.67|7.09|0.12|已撤销|
|
||||
| BGRA兼容副本 / limited |27.17|6.85|0.11|已撤销|
|
||||
| BGRA + full-chroma副本 / limited |25.61|10.38|0.13|已撤销|
|
||||
| BGRA + bilinear副本 / limited |30.79|7.44|0.13|收益不足以保留二进制维护和色度采样变化,已撤销|
|
||||
|
||||
数据及日志索引见[测试汇总](evidence/kylin-mouse-20261104/trial-summary.json)。这轮没有把请求45FPS写成真实达到45FPS。原full与limited静态区域的RGB平均绝对差约0.3~1.3/255,黑白条纹仍包含0和255,小字号与彩色文字已视觉核对;这不是逐像素无损承诺。见[色彩核对](evidence/kylin-mouse-20261104/color-comparison.json)、[limited实际截图](evidence/kylin-mouse-20261104/limitedrange-2k45.png)。
|
||||
|
||||
原始进程线程观察显示Sunshine有一个线程接近占满单核,而盒子呈现队列通常约0.1~0.2ms。通过Linux软件CPU-clock采样,旧版静态FFmpeg的逐像素色彩转换出现在热点中;ELF已剥离符号,地址区间只能粗略分类,不能当作精确函数耗时。记录见[采样说明](evidence/kylin-mouse-20261104/sampling-hotspots.json)。
|
||||
|
||||
## 未采用的兼容实验
|
||||
|
||||
仅对精确SHA256匹配的原文件创建三个独立副本,未覆盖原文件。根据v0.21.0源码与反汇编,两个`sws_getContext`调用的BGR0立即数由121改为BGRA的28;后续分别测试附加`SWS_FULL_CHR_H_INP`或改为`SWS_BILINEAR`。只对独立副本做单文件KYSEC认证。实验服务drop-in已删除,三个副本确认没有进程使用后删除,原版SHA和活动服务已读回确认,见[清理记录](evidence/kylin-mouse-20261104/restored-original.json)、[恢复状态](evidence/kylin-mouse-20261104/restored-host-state.txt)。未保留任何实验二进制作为正常入口。
|
||||
|
||||
依据:[Sunshine v0.21.0 video.cpp](https://github.com/LizardByte/Sunshine/blob/v0.21.0/src/video.cpp)、[FFmpeg n6.0转换初始化](https://github.com/FFmpeg/FFmpeg/blob/n6.0/libswscale/x86/swscale.c)、[FFmpeg色彩范围转换](https://github.com/FFmpeg/FFmpeg/blob/n6.0/libswscale/swscale.c)。
|
||||
|
||||
## 回显延迟边界及后续
|
||||
|
||||
增加了F9翻转画面黑白标记的探针:盒子在发出合成按键前计时,轮询实际解码视频所在X11根窗口,看到标记翻转后结束。该方法包含工具启动、串流往返和轮询,排除物理USB输入和显示面板扫描。
|
||||
|
||||
用户授权切回麒麟后,使用同一个未锁屏的动态源、原版服务端、相同2560×1440/30FPS/30Mbps完成顺序对照,每组24次,均无超时:
|
||||
|
||||
| 配置 | 实际渲染FPS | 解码ms | 合成输入回显P50 ms | P95 ms |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| full原协商 |23.35|9.54|150.99|186.38|
|
||||
| limited保留配置 |27.25|6.96|143.57|173.03|
|
||||
|
||||
见[完整对照数据](evidence/kylin-mouse-20261104/final-response-comparison.json)。视频更新约提高17%;回显分布重叠、网络延迟也不同,24个样本不足以证明显著或大幅的延迟下降,更不能宣称达到Windows几乎无延迟。该探针也不能替代物理输入到屏幕的测量。
|
||||
|
||||
此前full/30FPS的一组24次P50约163ms、P95约190ms,后续超出临时串流期限,盒子随后已连接Windows,完整收尾未完成;保留原始JSON和部分日志用于审计,不混入上述完整对照。临时GTK和临时串流服务已停止,最终使用正常kiosk串流。
|
||||
|
||||
麒麟2vCPU的软件捕获/颜色转换/编码链路仍然限制视频更新。若目标是Windows同等的视频响应,需要进一步实现并验证转换并行化/硬件编码;若目标是指针本身不等待视频,则需要实现自动形状同步、显隐、热点和可靠交接的独立光标通道。现有受控原型尚不满足这一要求,不能用普通本地箭头叠加冒充完成。
|
||||
@ -1,65 +0,0 @@
|
||||
# 麒麟真实 2K 与响应优化(2026-11-04)
|
||||
|
||||
后续鼠标掉帧排查已保留逐主机limited协商,同场景原程序对照约21→29FPS,尚未达到Windows水平,见[继续排查记录](kylin-mouse-20261104.md)。下文为本轮之前的真实2K落地记录。
|
||||
|
||||
麒麟已从 1080p 升级为真实 **2560×1440** 桌面与视频,盒子麒麟卡片配置为 **30 FPS、30Mbps、H.264**。分辨率与码率已与 Windows 对齐;实际帧率尚未达到 Windows 的水平。保留暂停 UKUI 桌面合成器的优化,局部运动短测由约 19 FPS 提高至约 23 FPS。物理鼠标到屏幕的端到端延迟未测量。
|
||||
|
||||
## 最终部署
|
||||
|
||||
| 项目 | 生效配置 |
|
||||
| --- | --- |
|
||||
| PVE VM 101 kylin-test | `vga=std,memory=64`,其他字段保持原值 |
|
||||
| 麒麟 Virtual-1 | 2560×1440,约59.95Hz,自定义模式 `2560x1440_60.00` |
|
||||
| Sunshine | v0.21.0,X11 捕获,libx264 ultrafast / zerolatency,原单线程策略 |
|
||||
| 盒子麒麟对象 | `desk-kylin-01`,192.168.1.71 / Desktop,2560×1440 / 30 FPS |
|
||||
| 客户端 | 原 RKMPP 硬解、DRM PRIME、EGL/GLES、latest-frame 队列、无VSync/无frame-pacing |
|
||||
| 视频码率 | 30Mbps,沿用现有产品对2K的规则;Windows同为30Mbps |
|
||||
|
||||
用户明确授权后,已正常关闭 VM 101 至 stopped,修改显存并重新启动。配置差异检查确认只有 `vga` 变化;未重启 PVE、Windows 或 Ubuntu。麒麟内部 PCI BAR 已识别为64MB。见 [执行记录](evidence/kylin-quality-20261104/vm101-execution.json)、[配置差异](evidence/kylin-quality-20261104/vm101-config-change.json)。
|
||||
|
||||
服务 `~/.config/systemd/user/vdi-sunshine.service` 增加启动前创建、添加和切换2K模式的步骤,时序为:
|
||||
|
||||
```text
|
||||
241.50 2560 2608 2640 2720 1440 1443 1448 1481 +hsync -vsync
|
||||
```
|
||||
|
||||
已通过“切回1080p、删除自定义模式、启动服务”的实测,确认能重新创建并进入2K。新服务配置尚未通过第二次整机重启验收。麒麟 test1 图形会话在本次开机后存在;未改变其登录或锁屏策略。
|
||||
|
||||
服务启动后经 `org.ukui.KWin /Compositor` 暂停桌面合成,停止服务时恢复合成;读回最终 `active=false`。这针对当前UKUI/Xorg现场环境,调用失败不阻止 Sunshine 本体启动。原 `kwinrc` 未修改。见 [最终服务](evidence/kylin-quality-20261104/final-sunshine.service)、[最终配置](evidence/kylin-quality-20261104/final-sunshine.conf)。
|
||||
|
||||
## 实际验证
|
||||
|
||||
临时 GTK 窗口展示小字号文字、单像素条纹、彩色文字与局部运动块;有效轮次的源绘制约59FPS。只在确认 Moonlight 窗口焦点后注入输入。测试后窗口已关闭。
|
||||
|
||||
| 有效2K动态桌面轮次 | 实际渲染 FPS | 解码 ms | 帧队列 ms | 呈现 ms |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| X11 / 单线程 / 合成开启 | 18.89 | 14.13 | 0.09 | 2.12 |
|
||||
| X11 / 两线程 / 合成开启 | 18.85 | 8.96 | 0.29 | 2.12 |
|
||||
| X11 / 单线程 / 合成暂停(采用) | 22.97 | 9.04 | 0.12 | 2.11 |
|
||||
|
||||
这是约20秒保持加连接与输入阶段的短测,网络延迟和抖动并不完全相同,不能视为长期性能保证。客户端帧队列很短,未证明物理输入延迟下降;2K实际23FPS也不能宣传为稳定30或45FPS。
|
||||
|
||||
- 实际桌面与视频尺寸均为2560×1440,已核对盒子截屏,文字不再由1080p放大。见 [2K动态图](evidence/kylin-quality-20261104/2k30-desktop-no-compositor-valid.png)。
|
||||
- 当前有效测试进程记录中,48次跨屏交替点击全部落在预期坐标(误差不超过1px),收到F8。见 [输入结果](evidence/kylin-quality-20261104/2k-input-result.json) 和 [原始事件](evidence/kylin-quality-20261104/2k-valid-source-and-input.log)。
|
||||
- 从正常 kiosk 麒麟卡片连接、退出回列表、再次连接,确认2K参数与RKMPP/EGL路径;最终保持正常麒麟桌面连接供用户体验。见 [重连结果](evidence/kylin-quality-20261104/final-kiosk-check.json)、[正常入口日志](evidence/kylin-quality-20261104/final-kiosk-client.log)、[最终桌面](evidence/kylin-quality-20261104/final-desktop.png)。
|
||||
- 只修改盒子的麒麟对象,Windows、Ubuntu对象经结构比较保持原值;没有修改产品源码、客户端二进制和全局渲染配置。
|
||||
|
||||
部分早期2K轮次在长时间暂停后采到了锁屏画面,已经从有效性能结论中排除:`2k30-ultrafast-thread1`、`2k30-damage-thread1`、`2k30-damage-thread2`。这些原始文件保留用于审计。当前有效轮次均为文件名含 `desktop-...-valid` 的三轮,截图与输入记录已核对。测试源码也改为只重绘运动区域,不能直接将新旧不同负载的数字相减作为提升幅度。
|
||||
|
||||
曾探测KMS捕获,但此虚拟显卡上未初始化成功,已恢复X11;最终 Sunshine 实体二进制与入口的 `getcap` 均为空,未保留该探测使用的额外 capability。两线程、较慢压缩预设未带来适合本机的收益,均未保留。
|
||||
|
||||
## 原因、边界与回退
|
||||
|
||||
原默认 Standard VGA 只有16MB。Linux5.15 DRM VRAM helper限制单帧缓冲不超显存一半,2560×1440×4字节的单帧约14.06MiB,双缓冲约28.13MiB,因此原2K切换失败;增加到64MB后解除限制。参考 [内核实现](https://github.com/torvalds/linux/blob/v5.15/drivers/gpu/drm/drm_gem_vram_helper.c)。
|
||||
|
||||
麒麟目前只有2个虚拟CPU且使用软件编码。这次完成了真实2K、码率对齐、合成开销优化和基础输入/重连验证;45FPS、长时间高动态负载、物理输入延迟、音质、生产多用户隔离仍未验收。
|
||||
|
||||
备份位置:
|
||||
|
||||
- 麒麟 `/home/test1/vdi-terminal/backups/kylin-quality-20261104/`:原 sunshine.conf、vdi-sunshine.service、kwinrc、xrandr-before.txt。
|
||||
- 盒子 `/home/armbian/vdi-terminal/backups/kylin-quality-20261104/desktops.json`。
|
||||
- PVE [原配置快照](evidence/kylin-quality-20261104/vm101-config-before.json),原 `vga` 字段未设置。
|
||||
|
||||
回退先正常退出串流;恢复盒子备份(若其他桌面后续有修改,只恢复麒麟对象);在麒麟恢复原 Sunshine 配置与服务,`systemctl --user daemon-reload` 后重启服务。当前服务停止会恢复合成;原服务将显示器设回1080p。64MB显存可保留;如需完全恢复原PVE设置,删除显式 `vga` 字段并在维护窗口正常关机再启动。
|
||||
|
||||
编码参数含义参考 [Sunshine v0.21.0 官方说明](https://docs.lizardbyte.dev/projects/sunshine/v0.21.0/about/advanced_usage.html#min-threads)。
|
||||
@ -1,21 +0,0 @@
|
||||
# 麒麟 2K 软件低延迟调整(2026-11-06)
|
||||
|
||||
本轮只修改麒麟 VM101。当前链路仍为 `2560x1440 + X11 + libx264 ultrafast/zerolatency`,未修改 Windows、Ubuntu 或盒子客户端。
|
||||
|
||||
## 已实施
|
||||
|
||||
麒麟 Sunshine 服务启动后现在会显式调用 `org.ukui.kwin.Compositing.suspend`,并补齐 `DISPLAY`、`XAUTHORITY`、`XDG_RUNTIME_DIR` 和 `DBUS_SESSION_BUS_ADDRESS`。原先服务启动时该调用经常因 KWin 尚未准备好而失败,导致合成器仍处于 active;现在服务启动后的 `ExecStartPost` 返回成功,KWin 状态确认是 `active=false`。
|
||||
|
||||
VM101 仍保持 `vga=std,memory=64`、2 vCPU;没有增加虚拟显存以外的硬件资源,也没有改动 PVE 显卡直通。
|
||||
|
||||
## 验收
|
||||
|
||||
Sunshine 日志确认麒麟仍以真实 2560×1440 X11 画面建立串流,并使用 `libx264`;盒子回到 kiosk 桌面入口,测试串流和临时测试进程已经退出。现有 2K 动态基准显示,关闭 UKUI/KWin 合成器时实际渲染约 23FPS,开启时约 19FPS;历史麒麟点击回显基准 P50 约 128ms、P95 约 169ms。
|
||||
|
||||
麒麟安全策略阻止本轮临时 GTK 测试脚本直接运行,因此本轮没有新增物理输入端到端样本;服务状态和显示链路验收通过。
|
||||
|
||||
## 结论
|
||||
|
||||
这项软件调整已经生效,能够减少桌面合成带来的重绘和排队,适合保留在 2K 配置中。它不能把 CPU 编码变成硬件编码,2K 下仍不能宣称完全达到 Windows 的延迟。下一步若还需继续压低尾延迟,应优先评估给麒麟增加 vCPU 是否会改善实际编码排队,但当前宿主总物理核数有限,不能直接盲目增加。
|
||||
|
||||
证据:[预检](evidence/kylin-quality-20261106-preflight.txt)、[合成器服务重启](evidence/kylin-quality-20261106-compositor-service-restart-v2.txt)、[最终状态](evidence/kylin-quality-20261106-final-state.txt)。
|
||||
@ -1,96 +0,0 @@
|
||||
# Ubuntu / 麒麟云桌面接入记录(2026-11-04)
|
||||
|
||||
> 后续更新:麒麟已升级真实2560×1440/30FPS配置与30Mbps,动态短测约23FPS,见[麒麟2K调优记录](kylin-quality-20261104.md)。以下1080p参数属于初次接入记录。
|
||||
|
||||
> 2026-11-05:两套Linux均为真实2K/30FPS请求配置与30Mbps,已启用同步原生形状的独立光标,指针位置不再等待视频;Ubuntu恢复原生Adwaita/default加速,关闭动画保留。详见[独立光标实施记录](linux-native-cursor-20261105.md)。视频动态性能和物理输入延迟仍有边界,较早画质实验见[Ubuntu调优记录](ubuntu-quality-20261105.md)。
|
||||
|
||||
两台 Linux 已接入现场盒子的正常桌面选择流程。实际验证了连接、画面、键盘、鼠标、退出和再次连接;最终回到桌面列表。Windows 也完成了原配置连接回归。
|
||||
|
||||
## 现场配置
|
||||
|
||||
| 桌面 | 地址 / 用户 | 系统与服务端 | 串流配置 |
|
||||
| --- | --- | --- | --- |
|
||||
| Ubuntu | 192.168.1.72 / test2 | Ubuntu 24.04.4;Sunshine 2026.914.233613 | Desktop,1920×1080,30 FPS,H.264 软件编码 |
|
||||
| 麒麟 | 192.168.1.71 / test1 | 银河麒麟 V10 SP1;Sunshine v0.21.0 Ubuntu20.04 构建 | Desktop,1920×1080,30 FPS,H.264 软件编码 |
|
||||
| Windows | 192.168.1.70 | 原 Sunshine 服务端 | 原 Desktop,2560×1440,45 FPS 配置 |
|
||||
|
||||
盒子为 `armbian@192.168.1.188`。原 Linux 桌面上的 Moonlight 是客户端,用于连接其他主机;本次补齐 Linux 的 Sunshine 服务端,让盒子 Moonlight 可以连接它们。未移除原 Moonlight。
|
||||
|
||||
盒子使用原 RKMPP 私有客户端与 EGL/GLES 呈现路径;本次没有修改产品源码,也没有把现场 IP 写入源码默认值。
|
||||
|
||||
## Linux 服务
|
||||
|
||||
两台均使用登录用户的 systemd 服务与 XDG 自启动:
|
||||
|
||||
- `~/.config/systemd/user/vdi-sunshine.service`:已 enabled,现场为 active。
|
||||
- `~/.config/autostart/vdi-sunshine.desktop`:图形登录后导入 DISPLAY、XAUTHORITY 等会话环境并启动服务。
|
||||
- `~/.config/sunshine/sunshine.conf`:X11 捕获、software 编码、ultrafast、zerolatency、禁用 UPnP、Web 管理来源限制为本机。
|
||||
- `~/.config/sunshine/apps.json`:发布 `Desktop`。
|
||||
- `~/.config/sunshine/vdi-admin.json`:随机生成的各自主机管理凭据,权限 600;未复制进仓库。
|
||||
|
||||
服务启动前把 `Virtual-1` 设置为 1920×1080,创建或复用 `vdi_audio` 虚拟音频 sink,并设为默认。Sunshine 配置使用 `audio_sink = vdi_audio`,采集端自动选中 `vdi_audio.monitor`。两台最后一轮串流均未出现采音初始化失败;Ubuntu 日志明确显示 Opus 初始化,盒子日志显示双声道音频流启动。实际扬声器听感尚未验收。
|
||||
|
||||
### Ubuntu 登录配置
|
||||
|
||||
用户明确授权 test2 自动登录并使用 Xorg,修改前已备份:
|
||||
|
||||
- `/etc/gdm3/custom.conf`:`WaylandEnable=false`、`AutomaticLoginEnable=true`、`AutomaticLogin=test2`、`DefaultSession=ubuntu-xorg.desktop`。
|
||||
- `/var/lib/AccountsService/users/test2`:`Session` / `XSession` 为 `ubuntu-xorg`。
|
||||
- 备份目录 `/var/backups/vdi-linux-20261104-100722/`,文件 `custom.conf`、`test2`。
|
||||
|
||||
重启 GDM 后已进入 test2 的 Xorg 桌面,`DISPLAY=:0`、`XAUTHORITY=/run/user/1000/gdm/Xauthority`。未重启虚拟机;整机重启后的自动恢复仍待验证。另补装 `pulseaudio-utils`,未执行系统整体升级。
|
||||
|
||||
### 麒麟兼容部署
|
||||
|
||||
现场 glibc 2.31,选择可兼容的官方 v0.21.0 Ubuntu20.04 构建。麒麟安全策略拒绝未签名 DEB 后,采用解包部署:
|
||||
|
||||
- root 拥有的 `/opt/vdi-sunshine-0.21.0/`。
|
||||
- `/usr/share/sunshine` 指向其资源目录。
|
||||
- 对具体 Sunshine 二进制及当次临时安装脚本做 `kysec_set -n exectl -v verified` 单文件认证;未全局关闭 KYSEC。
|
||||
- `/etc/udev/rules.d/85-vdi-sunshine.rules` 配置 uinput,现场 ACL 已赋予 test1 读写权限。
|
||||
|
||||
麒麟沿用已有 test1 图形会话;当时锁屏已临时解锁,未关闭长期锁屏,也未配置新的自动登录。后续若回到登录界面或锁屏,需要按原策略登录或解锁。
|
||||
|
||||
## 盒子配置与配对
|
||||
|
||||
只更新 `~/.config/vdi-terminal/desktops.json` 中 Ubuntu / 麒麟对象,Windows 对象保留原值。原配置备份:
|
||||
|
||||
`/home/armbian/vdi-terminal/backups/linux-desktops-20261104-101507/desktops.json`
|
||||
|
||||
两台均完成盒子原 Moonlight 配对;Sunshine 服务重启后原配对仍有效。只在无串流时重启 kiosk 刷新列表。正常客户端路径:
|
||||
|
||||
`/home/armbian/vdi-build/rkmpp-experiment/build/moonlight/app/moonlight`
|
||||
|
||||
退出串流回列表使用 `Ctrl+Alt+Shift+Q`。
|
||||
|
||||
## 验证结果与证据
|
||||
|
||||
证据目录:[linux-desktops-20261104](evidence/linux-desktops-20261104/)。
|
||||
|
||||
| 项目 | 结果 | 证据 |
|
||||
| --- | --- | --- |
|
||||
| 三张卡可连接 | 通过 | [列表截图](evidence/linux-desktops-20261104/desktop-list.png) |
|
||||
| Ubuntu 实际画面与输入 | F8(65477)及鼠标左键事件到达虚拟机临时 GTK 窗口 | [截图](evidence/linux-desktops-20261104/ubuntu-stream.png)、[事件](evidence/linux-desktops-20261104/ubuntu-input.jsonl) |
|
||||
| 麒麟实际画面与输入 | F8(65477)及鼠标左键事件到达虚拟机临时 GTK 窗口 | [截图](evidence/linux-desktops-20261104/kylin-stream.png)、[事件](evidence/linux-desktops-20261104/kylin-input.jsonl) |
|
||||
| 最后 Ubuntu / 麒麟 / Windows 依次连接再返回列表 | 三项均硬解就绪且保持运行,最后正常退出 | [结果 JSON](evidence/linux-desktops-20261104/final-stream-check.json) |
|
||||
| Linux 实际视频尺寸与渲染路径 | h264_rkmpp、drm_prime、EGL/GLES、1920×1080 | [Ubuntu 日志](evidence/linux-desktops-20261104/ubuntu-client-final.log)、[麒麟日志](evidence/linux-desktops-20261104/kylin-client-final.log) |
|
||||
| Windows 原配置回归 | 2560×1440 硬解就绪,正常退出 | [客户端日志](evidence/linux-desktops-20261104/windows-client-final.log) |
|
||||
|
||||
最后约十秒有效视频样本中 Ubuntu 渲染 29.59 FPS、麒麟 29.35 FPS,仅用于确认短时链路可运行,不代表长时间性能验收。Windows 这轮短测渲染 35.76 FPS,不据此重新定义原 45 FPS 配置的性能结论。
|
||||
|
||||
`*-sunshine.service`、`*-sunshine.conf` 是最终配置快照,`*-sunshine-final.log` 是最后串流服务端日志,`*-final-state.txt` 记录服务、音频和 uinput 状态,`ubuntu-login-config.txt` 记录登录配置。截图是盒子 2560×1440 输出,Linux 视频本身为 1920×1080。
|
||||
|
||||
Ubuntu 日志仍有 CAP_SYS_ADMIN 探测失败及显示信息回退提示,但实际选中了 X11 并成功捕获 1080p;本次没有为消除这些提示授予额外高权限。麒麟退出时有 GLib Source ID 警告,服务仍为 active 且可再次连接。
|
||||
|
||||
最初 Xvfb + 软件解码独立探测曾出现控制断开,同期原 Windows 会话也断开,原因未确定;后续以上正常硬解入口回归均通过。安装 Xvfb 时 apt 同时升级了盒子的 `libpciaccess0`、`xserver-common`、`xserver-xorg-core`;未重启 Xorg。临时 Xvfb 与输入测试服务已停止。
|
||||
|
||||
当前验收覆盖基础画面与输入及短时重连;Linux 2K、GPU 编码、长期稳定性、重启恢复、锁屏策略、音质与生产多用户隔离尚未验收。
|
||||
|
||||
## 回退与后续维护
|
||||
|
||||
1. 盒子恢复上述备份的 `desktops.json`,在无活动会话时刷新 kiosk,可撤回本轮卡片配置。
|
||||
2. 在对应 Linux 图形用户下执行 `systemctl --user disable --now vdi-sunshine.service`,将 `~/.config/autostart/vdi-sunshine.desktop` 移出自启动目录,可停止本次 Sunshine 自动启动。保留 `~/.config/sunshine/` 可保留配对与配置。
|
||||
3. Ubuntu 如需恢复原登录方式,以 sudo 将备份 `custom.conf` 放回 `/etc/gdm3/custom.conf`,备份 `test2` 放回 `/var/lib/AccountsService/users/test2`,保留原属主和权限;在维护窗口重启 GDM(会终止图形会话)。
|
||||
4. 麒麟如需彻底卸载,先停服务,再核对 `/usr/share/sunshine` 链接目标和 uinput 规则是否仍被其他应用使用,最后移除本次 `/opt/vdi-sunshine-0.21.0` 部署;不要直接覆盖发行版同名文件或全局修改 KYSEC。
|
||||
|
||||
排查服务可先运行 `systemctl --user status vdi-sunshine.service` 和 `journalctl --user -u vdi-sunshine.service`。麒麟版本较旧是当前兼容方案,后续升级需要重新评估系统库兼容性与安全维护。
|
||||
@ -1,66 +0,0 @@
|
||||
# Ubuntu / 麒麟原生独立光标实施(2026-11-05)
|
||||
|
||||
本轮按用户要求保留系统原生光标,降低指针移动对视频更新的依赖。两台Linux都使用X11、真实2560×1440、30FPS请求、30Mbps和原有软件编码;Windows仍为原硬编路径。Ubuntu光标主题已从此前的DMZ-White恢复Adwaita/24px,鼠标加速恢复default,关闭动画的性能设置保留。
|
||||
|
||||
## 调研与取舍
|
||||
|
||||
当前Moonlight v6.2.0的普通本地光标快捷键不会同步服务端形状,不能用于本任务。[上游使用说明](https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide)和[维护者说明](https://github.com/moonlight-stream/moonlight-qt/issues/1386)与固定版本源码一致。Linux Sunshine X11捕获会通过XFixes读取光标后混入视频;[v0.21.0捕获源码](https://github.com/LizardByte/Sunshine/blob/v0.21.0/src/platform/linux/x11grab.cpp)已实际下载核对。
|
||||
|
||||
继续调编码参数无法消除指针位置对软件编码、网络、解码的等待。换SPICE/VNC/RDP虽然可采用协议原生光标,但会改变现有桌面入口、音视频和部署路径;本轮没有把更换协议当成已验证优化。两台又有不同Sunshine版本,因此采用**已认证SSH光标通道 + 精确版本固定的进程级捕获适配器 + SDL主线程原生光标**,保留现有Moonlight视频和可靠鼠标输入通道。
|
||||
|
||||
这与11月3日的正式控制协议设计不同:不做运行中切换,而是在新视频连接前启动无光标捕获,退出时整个捕获会话结束后才恢复普通服务。这样无需用未关联的视频帧ACK或固定延时来猜测旧光标何时消失。SSH使用每台主机独立的受限强制命令密钥和固定主机公钥;没有新增未认证UDP端口。实现、安装、生命周期和回退详见[模块说明](../terminal/native-cursor/README.md)。
|
||||
|
||||
[SDL光标API](https://wiki.libsdl.org/SDL2/SDL_CreateColorCursor)提供真实彩色形状和热点。[SDL2 X11源码](https://github.com/libsdl-org/SDL/blob/release-2.30.0/src/video/x11/SDL_x11mouse.c)还暴露两个适配点:Xcursor像素直接复制,必须预乘alpha;启用指针边界时窗口内warp可能不执行,因此主动位置校正使用窗口范围内的global warp。没有更换SDL系统库。
|
||||
|
||||
## 实现及边界
|
||||
|
||||
- 0005客户端补丁由构建器在现有补丁后应用;Linux主机JSON才启用功能,Windows无该JSON。profile中的客户端SHA256在远端租约前校验,旧备份客户端实测被拒绝且麒麟原服务PID未改变。fork监督进程后exec保留kiosk预期的Moonlight PID,实际测试从正常列表进入。
|
||||
- 主机guard核验私有Sunshine副本和adapter SHA256;adapter仅在该进程的XFixes捕获结果中清除光标像素。原系统二进制、主题、原配置保留;租约结束删除/run中的临时服务drop-in并恢复原服务。
|
||||
- 形状、热点、动画、透明隐藏、单屏几何、序号和随机租约均有边界检查;原子状态文件位于盒子0700私有目录。SDL资源只在主线程创建/切换/释放。
|
||||
- 最新本地输入位置先由服务器实际指针确认;本地静止500ms且没有按钮按下时才接受服务器主动warp,避免拿迟到的输入回显回拉光标。
|
||||
- 当前仅支持已配置的单座席X11/2560×1440/绝对鼠标、最多128×128像素形状。短暂停顿保留上次形状,本地位置继续移动;未知形状、源分辨率改变、同步状态超过2秒或SSH故障会结束当前串流,恢复普通服务后可从列表重连。主机租约4秒,不允许无限期保留旧状态。
|
||||
- 不宣称通用多座席、相对鼠标游戏、Wayland、任意显示设备或运行中平滑降级已完成。启用模式内阻止切换为相对鼠标或固定本地箭头的快捷键;其他主机保持原逻辑。
|
||||
- 原视频保留2K H.264 4:2:0及30Mbps,没有用降分辨率提高指针反馈。独立指针解决位置移动,点击后的页面和拖窗内容继续受视频链路限制。
|
||||
|
||||
## 联调与证据
|
||||
|
||||
原始证据位于[evidence/native-cursor-20261105/](evidence/native-cursor-20261105/)。最终SHA为 `5d4330…` 的两系统复测为 `ubuntu-release-probe.json`、`kylin-release-probe.json`,点击与源事件核对见 `release-click-verification.json` 及对应 `*-release-source.log`。较早 `ubuntu-v4-final-probe.json`、`kylin-final-probe.json` 等保留作修正历史,不混同最终二进制证据。
|
||||
|
||||
| 验证项目 | Ubuntu | 麒麟 |
|
||||
| --- | --- | --- |
|
||||
| 原生箭头、文字、横向缩放、忙碌、隐藏、十字、自定义共7种 | 几何/热点一致,采样像素最大差0 | 几何/热点一致,采样像素最大差0 |
|
||||
| 光标附近100×100均匀视频区域 | 七种均无非均匀像素;隐藏0可见像素 | 七种均无非均匀像素;隐藏0可见像素 |
|
||||
| 唯一编号点击与源事件核对 | 28/28,最大坐标误差1.055px | 28/28,最大误差0.829px |
|
||||
| 合成按下至编号出现在解码画面 | P50 188.38ms / P95 522.42ms | P50 134.37ms / P95 165.56ms |
|
||||
| 稳定会话短按/双击补测 | 16次40ms按住全部收到,双击计数+2 | 16次40ms按住全部收到,双击计数+2 |
|
||||
| 暂停Moonlight进程时移动本地指针 | 约250ms内4个位置读回正确,形状保留 | 约250ms内4个位置读回正确,形状保留 |
|
||||
| 服务端主动warp至(400,600) | 本地读回(400,600) | 本地读回(400,600) |
|
||||
| 主机agent / guard被SIGKILL后恢复原服务 | 2.724s / 0.771s检查恢复 | 3.220s / 0.893s检查恢复 |
|
||||
|
||||
这些点击数字测量的是**页面/视频响应**,不表示本地指针移动延迟;坐标查询耗时也不是物理显示延迟。暂停视频生产者仍可移动本地指针证明了指针位置不再等待视频更新。没有测量物理USB输入、面板扫描或与Windows做相同仪器对照,因此不能宣称物理零延迟或定量等同Windows。
|
||||
|
||||
Ubuntu冻结盒子bridge后状态超时退出,3.289s检查无残留客户端且原服务恢复;麒麟实际改为1920×1080后自动结束会话,3.555s检查恢复原服务和2560×1440,之后从正常入口重连2K通过。焦点探针仅确认原生形状重新出现;kiosk键盘抓取下没有证据证明XSetInputFocus(None)触发了真正SDL失焦,**完整失焦场景尚未验收**。详见对应 `*-recovery.json`、`ubuntu-stale-state.json`、`kylin-resolution-change.json`、`kylin-focus.json` 和warp记录。
|
||||
|
||||
4项本地协议/边界/旧客户端拒绝单测通过;固定基线按顺序应用0001~0005(0002在common-c子模块)的检查通过,构建器规范化补丁CRLF后仍严格检查上下文。现场客户端保留原先源码修改并加入0005,本轮未把其他任务的0004重新构建部署;不得仅据构建器清洁应用成功宣称现场已包含0004。
|
||||
|
||||
最终2秒等待策略的Ubuntu持续测试(`ubuntu-final-soak.json`)完成360.003秒、65,960次实际状态读取,接收器持续存活。观察到1次“包时间戳晚于读前时间、但不晚于读后时间”的真实并发更新,支持修正校验顺序;读后未来时间0次。测试中主动暂停主机代理1.1秒,本地指针移动至(650,900),会话存活并恢复同步(`ubuntu-short-stall.json`);最大状态年龄1109.79ms包含这次故障注入,不是鼠标移动延迟。之后同一会话的七种光标和28点击复测全部匹配;再冻结bridge 2.6秒,2.904秒检查客户端已退出,原Sunshine路径和guard inactive核对通过,见 `ubuntu-final-stale-state.json`、`final-stale-recovery-services.json`。这仍是有界回归,长期稳定性未验收。
|
||||
|
||||
## 故障与修正
|
||||
|
||||
1. Ubuntu系统Sunshine带file capabilities,加载器忽略LD_PRELOAD。没有清除原文件能力;只在租约内使用相同SHA256、无额外能力的私有副本,正常退出恢复原文件。适配器映射和dlsym receipt均确认后才返回READY。
|
||||
2. 麒麟KYSEC拒绝未认证新脚本;仅认证host.py、私有Sunshine副本和adapter。没有关闭全局安全策略。
|
||||
3. GTK blank cursor实测是1×1、热点(1,1)。初版按可见光标边界拒绝并安全结束会话;修正为全透明图像的规范1×1/热点(0,0)。可见图像仍严格拒绝越界热点。
|
||||
4. 第一轮原生边缘像素差异大,发现SDL2 X11复制未预乘像素;修正后逐像素复测,不把初版当通过。初版28次点击P50约180ms/P95约475ms,只是探索数据。
|
||||
5. Ubuntu两次“强制暂停客户端→恢复→第一点击”探针首击未回显(40ms和80ms各一次),根因未确定。最终将正常输入测量与SIGSTOP实验分开,在稳定会话核对28个唯一编号及源端事件,再单独暂停客户端;稳定短按/双击补测也通过。没有证据把失败归因于GTK/窗口焦点,不得据后续通过抹去失败,或宣称任意高速输入绝不丢失。
|
||||
6. 主动warp第一轮未移动,确认是SDL2在pointer barrier存在时忽略窗口内warp;改为有界global warp后再复测。
|
||||
7. 最终解锁Ubuntu时弹出历史Sunshine崩溃提示。`/var/crash`中报告为11:12:07、原`/usr/bin/sunshine`、SIGTRAP;同秒服务日志为“10 seconds passed, yet Sunshine's still running: Forcing shutdown”,发生在故障恢复测试期间原服务停止的阶段,随后11:12:14恢复。该停止看门狗问题未作上游修复;不能据后续恢复称所有服务退出都无异常。原报告保留,未向外发送;元数据与日志在证据目录。
|
||||
8. 普通Ubuntu桌面连续运行约4分42秒后触发“stale or mismatched lease”退出。复核发现客户端先取当前时间再打开状态文件,可与写入进程原子更新文件竞态,合法新时间戳可能大于先前读数;修正为读取完成后再取校验时间。该轮错误未区分未来时间/实际过期,不能断言该竞态是退出的唯一原因。此前详细形状/点击证据来自修正前构建。
|
||||
9. 仅修正读时间顺序的构建先完成6分钟/65,658次状态读取,最大状态年龄308.10ms、无未来时间;但之后会话9分32秒时发生真实heartbeat timeout。故障发生前有持续忙碌动画和桌面应用启动,尚未确定是X11读取、调度还是SSH发送阻塞,不能只凭CPU软件编码推断唯一原因。最终将客户端/bridge等待上限调整为2秒,主机租约为4秒,并增加超过100ms的X11读取耗时诊断;本地指针位置仍立即显示,状态最多保留2秒后结束整次会话。修正版需按新阈值验证短暂停顿容忍和持续失效恢复;不把前一轮6分钟通过当成长时间稳定证明。
|
||||
|
||||
## 配置、备份与回退
|
||||
|
||||
现场客户端SHA256 `5d4330f703d7f8e053bc3795ca42bdaf231c9e9dd8ebb00a612013adbcccee03`,两个Linux profile均固定该值。私有副本:Ubuntu SHA256 `61e1ecbc8db2df786b9760c652aa4b9885dff36c57d4af09616344314cb7c34c`;麒麟 `1c3099bf924db3aee759b3d7c05e012569ef8650c400d1479144398795decc71`。adapter `cff4b7532aee69f0f205fa42946f45a89c1330486adc6358b90d13d2ccf1a0ea`。精确manifest在证据目录。未重启VM/盒子、未改PVE显卡或Windows服务端。
|
||||
|
||||
最终正常入口Windows为2K45/RKMPP/EGL、无原生光标环境;麒麟为2K30、原生形状恢复后正常退出,原服务active且无临时drop-in。盒子随后保留Ubuntu2K30会话,PID299860/父235482、原生Adwaita/24及default加速,实际视频源仍2K。两台GTK源程序和盒子持续探针均停止;活动Ubuntu会话的guard按设计保持active。见 `windows-final.json`、`kylin-normal-client.json`、`ubuntu-normal-client.json`、`final-services.json` 及普通桌面截图。XWD截图不含盒子原生指针,不能根据截图中没有箭头判定无光标。
|
||||
|
||||
盒子备份 `~/vdi-terminal/backups/native-cursor-20261105/` 有旧客户端和wrapper。回退先退出Linux会话并确认guard inactive/原Sunshine active,再把盒子对应 `~/.config/vdi-terminal/native-cursor/<IP>.json` 改名为.disabled;重连即使用原视频光标。若要恢复旧客户端,必须先停用两个JSON。Ubuntu主题原值备份仍在 `~/vdi-terminal/backups/ubuntu-quality-20261105/gsettings.json`;本轮恢复原生主题符合最新用户要求。
|
||||
@ -1,348 +0,0 @@
|
||||
# Moonlight 云桌面开机直达落地记录(2026-10-28)
|
||||
|
||||
## 起因
|
||||
|
||||
上一个对话在做完 RDP/Moonlight 实测对比(见 `rdp-vs-moonlight-comparison-20261028.md`)后,
|
||||
提出把 Moonlight 产品化为开机直连云桌面,但只列了 5 条待办就中断了。
|
||||
现场表现:登录盒子后看到的是 MATE 的绿色桌面,不是云桌面。
|
||||
|
||||
## 根因
|
||||
|
||||
盒子自启项 `~/.config/autostart/vdi-kiosk.desktop` 仍然拉起的是 RDP 版 `kiosk-shell`,
|
||||
而 Moonlight 从来没有进入过开机自启链;同时命令行直连串流还存在两处硬伤:
|
||||
|
||||
1. **应用 id 失效**:Moonlight 图形界面里配对的记录里,`Desktop` 的 app id 是 `881448767`,
|
||||
但 Sunshine 重装/升级后服务端的应用 id 已经变了,用旧 id 启动直接报
|
||||
`Qt Critical: 找不到应用程序 881448767`,串流根本起不来。
|
||||
改用应用**名称** `Desktop` 启动即可,服务端当前返回的应用列表是
|
||||
`Desktop` / `Steam Big Picture`。
|
||||
2. **硬解路径不可用**:`moonlight-qt` 的 `--video-decoder auto` 会优先尝试
|
||||
`h264_v4l2m2m`,在 RK3528 上报 `Could not find a valid device`
|
||||
(尽管存在 `/dev/mpp_service`、`/dev/rga`、`/dev/dri/renderD128`)。
|
||||
显式指定 `--video-decoder software` 后串流稳定。
|
||||
|
||||
另外两个在联调中发现并修掉的问题:
|
||||
|
||||
3. **Monitor 只杀直接子进程**:`vdi-kiosk-monitor` 通过 `sh -c` 启动主进程,
|
||||
杀掉它只会结束 `sh -c` 包装进程,真正的启动器变成孤儿并继续拉起第二条串流,
|
||||
两条流同时抢显示设备。已改为子进程独立进程组、按组结束整棵进程树。
|
||||
4. **主机可达性判断用错了协议**:Windows 防火墙屏蔽 ICMP,盒子 ping 不到 `192.168.1.70`,
|
||||
但 TCP/UDP 正常。可达性判断改成 TCP 连接探测(`bash /dev/tcp`)。
|
||||
|
||||
## 已交付
|
||||
|
||||
仓库内新增(`terminal/kiosk-shell/`):
|
||||
|
||||
```text
|
||||
scripts/vdi-moonlight # 云桌面串流启动器(POSIX sh)
|
||||
etc/moonlight.conf # 串流参数模板
|
||||
vdi-moonlight.desktop # 开机自启条目(Monitor + 启动器)
|
||||
```
|
||||
|
||||
修改:`src/monitor.cpp`(进程组管理)、`src/monitor_main.cpp` 无改动。
|
||||
|
||||
盒子上的部署位置:
|
||||
|
||||
```text
|
||||
~/vdi-terminal/bin/vdi-moonlight # 启动器
|
||||
~/vdi-terminal/bin/vdi-kiosk-monitor # 看门狗(已按本次修改重新编译)
|
||||
~/.config/vdi-terminal/moonlight.conf # 串流参数
|
||||
~/.config/autostart/vdi-moonlight.desktop # 开机自启(已启用)
|
||||
~/.config/autostart/vdi-kiosk.desktop # RDP 旧路径(已禁用,保留作回退)
|
||||
```
|
||||
|
||||
启动链:
|
||||
|
||||
```text
|
||||
systemd/user autostart
|
||||
-> vdi-kiosk-monitor --heartbeat-file /tmp/vdi-moonlight.heartbeat --max-restart 200
|
||||
-- /home/armbian/vdi-terminal/bin/vdi-moonlight
|
||||
-> moonlight-qt stream 192.168.1.70 Desktop ... (fullscreen)
|
||||
```
|
||||
|
||||
启动器职责:读本地配置 → 等云桌面主机就绪(TCP 探测)→ 全屏串流 →
|
||||
每 5 秒写心跳 → 检测串流假死(CPU 与网络均无流量超过 `hang-timeout`)→ 失败重试。
|
||||
|
||||
## 实测结果
|
||||
|
||||
全部在 RK3528 盒子(192.168.1.188)上实测:
|
||||
|
||||
| 项目 | 结果 |
|
||||
|---|---|
|
||||
| 开机/手动拉起后屏幕内容 | 满屏 Windows 10 云桌面,1920x1080 与屏幕 1:1 无缩放 |
|
||||
| 杀掉 moonlight-qt 客户端 | 启动器 5 秒内重连成功 |
|
||||
| 杀掉启动器 | 看门狗 3 秒后重启启动器,重新串流 |
|
||||
| 心跳文件 | `/tmp/vdi-moonlight.heartbeat` 每 5 秒刷新 |
|
||||
| 串流进程数 | 始终 1 条,无孤儿进程 |
|
||||
|
||||
`moonlight-qt --performance-overlay` 实测(1080p60 / H.264 / 软解):
|
||||
|
||||
```text
|
||||
Video stream: 1920x1080 21.59 FPS (Codec: H.264)
|
||||
Incoming frame rate from network: 21.59 FPS
|
||||
Decoding frame rate: 21.59 FPS
|
||||
Rendering frame rate: 18.15 FPS
|
||||
Frames dropped by your network connection: 0.00%
|
||||
Frames dropped due to network jitter: 13.64%
|
||||
Average network latency: 3 ms (variance: 1 ms)
|
||||
Average decoding time: 15.04 ms
|
||||
Average frame queue delay: 47.50 ms
|
||||
Average rendering time (including monitor V-sync latency): 51.09 ms
|
||||
```
|
||||
|
||||
结论:链路健康,网络丢包 0%,延迟 3 ms;21.6 FPS 的上限来自**服务端**,
|
||||
VM 100 内 Sunshine 走 libx264 软件编码(无 GPU 直通),抖动丢帧 13.64% 同源。
|
||||
|
||||
## 仍未验证 / 下一步
|
||||
|
||||
1. **服务端 GPU 直通**:宿主机 `starhe` 有 GTX 965M(`0000:01:00.0`,IOMMU Group 2 独占),
|
||||
主机同时有 Intel 核显 `0000:00:02.0` 可继续驱动本机显示,具备直通条件。
|
||||
直通后 Sunshine 可用 NVENC 硬编码,是把帧率提到 60 FPS、改善鼠标拖尾/卡顿的前提。
|
||||
2. **鼠标卡顿**:性能叠图实测渲染帧率仅 18.15 FPS、帧队列延迟 47.50 ms、
|
||||
渲染/VSync 51.09 ms,再叠加服务端 libx264 软编码,视频链路本身已累计约 100 ms。
|
||||
`--absolute-mouse` 已启用,输入模式没有配置错误;体感滞后来自低帧率和视频队列,
|
||||
根治仍是 Sunshine NVENC。短期可降为 1080p30、使用 `--no-vsync --no-frame-pacing`
|
||||
做 A/B 测试,但可能增加撕裂,不能替代服务端硬编码。
|
||||
3. **RK3528 硬解**:`h264_v4l2m2m` / `hevc_v4l2m2m` 缺少可用设备,需要 Rockchip MPP 的
|
||||
FFmpeg 补丁或 V4L2 request 驱动。当前 1080p 软解可用,4K 需要硬解。
|
||||
4. **2K 分辨率**:盒子当前屏幕是 1920x1080@60Hz,Sunshine 侧虚拟显示器也是 1920x1080@60Hz。
|
||||
若产品要求 2K,需要同时调整虚拟显示器分辨率和盒子端显示模式。
|
||||
5. **音频**:`--audio-on-host` 已开启,但终端出音未经现场耳朵确认。
|
||||
6. **产品登录流程**:当前开机直连 Moonlight 只是串流技术验证,不是最终产品形态。
|
||||
最终应由 `kiosk-shell` 显示首配/登录/桌面列表,用户选择后再获取短期连接票据并启动会话;
|
||||
管理员 Web 控制台是独立的 `console/`,方案见 `docs/web-console-mvp-plan-20261028.md`。
|
||||
7. **与 RDP 的关系**:RDP 路径代码保留在 `src/rdp_session.*`,自启项已禁用,可随时切回。
|
||||
正式协议选型仍需按实测数据与产品方案确认,不能仅凭本记录定版。
|
||||
8. **管理平台 API**:Moonlight 路径目前是本地配置文件驱动,接入自研管理平台后
|
||||
应由 API 下发主机/分辨率/帧率等参数,替换 `etc/moonlight.conf` 的静态配置。
|
||||
9. **重启验收异常**:2026-10-28 15:54 执行真实重启后,盒子在 3 分钟内未恢复
|
||||
`192.168.1.188` 的网络/SSH,局域网扫描也未发现其 SSH 地址。重启前已确认
|
||||
NetworkManager 连接 `YXTech` 的 `autoconnect=yes`,但真实重启后的开机自启验收因此未完成;
|
||||
需要现场查看盒子 HDMI 画面/电源状态,并通过串口或本地键盘确认启动与 WiFi 情况。
|
||||
|
||||
## 附:常用运维命令
|
||||
|
||||
```bash
|
||||
# 查看状态
|
||||
/home/armbian/vdi-terminal/bin/vdi-moonlight status
|
||||
|
||||
# 查看日志
|
||||
tail -f ~/vdi-terminal/logs/moonlight-$(date +%Y%m%d).log
|
||||
tail -f /tmp/vdi-monitor.log
|
||||
|
||||
# 手动重启串流
|
||||
pkill -f '^moonlight-qt stream' # 启动器会自动重连
|
||||
|
||||
# 切回 RDP 路径
|
||||
sed -i 's/^X-GNOME-Autostart-enabled=false/X-GNOME-Autostart-enabled=true/' \
|
||||
~/.config/autostart/vdi-kiosk.desktop
|
||||
sed -i 's/^X-GNOME-Autostart-enabled=true/X-GNOME-Autostart-enabled=false/' \
|
||||
~/.config/autostart/vdi-moonlight.desktop
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 2026-10-29 追加:登录/桌面列表产品流程落地与网络根因修复
|
||||
|
||||
## 新增验证结论(全部实测)
|
||||
|
||||
1. **产品流程全面跑通**:开机 → 登录页(本地演示认证)→ 桌面列表
|
||||
(Windows 可连接 / Ubuntu、麒麟待分配)→ 点击连接 → Moonlight 全屏串流
|
||||
Windows 10 桌面 → 串流结束自动回桌面列表。
|
||||
2. **重启验收通过**:真实重启后固定 IP `192.168.1.188` 自动恢复,
|
||||
唯一自启链 `vdi-kiosk-monitor -> kiosk-shell` 生效,开机只进登录页,
|
||||
不再出现"先 RDP 后 Moonlight"双链竞争。
|
||||
3. **Monitor 崩溃恢复**:kill -9 kiosk-shell 后 4 秒内自动重启,心跳正常。
|
||||
|
||||
## 昨晚两次串流失败的根因(重要)
|
||||
|
||||
用户给盒子配静态 IP `192.168.1.188` 时,是在 DHCP 连接上"添加地址",
|
||||
导致 wlan0 同时持有 `.185`(DHCP)和 `.188`(静态)两个地址。
|
||||
双地址引发 ARP 混乱,实测:
|
||||
|
||||
```text
|
||||
盒子 -> 网关 ping 丢包 66%~85%(间歇)
|
||||
PC -> 盒子 ping 丢包 33%
|
||||
UDP 双向近乎全丢(Moonlight 的视频/控制都是 UDP)
|
||||
TCP/SSH 基本正常
|
||||
```
|
||||
|
||||
Moonlight 两次失败(38 秒断流、第二次连不上)均为该丢包所致,
|
||||
与 Moonlight 参数、Sunshine 无关。WiFi 信号本身满格(-38dBm)。
|
||||
|
||||
修复:`nmcli` 将 YXTech 连接改为 `ipv4.method=manual` 纯静态,
|
||||
只保留 `.188` 一个地址。修复后网关 ping 0% 丢包(60/60),
|
||||
串流稳定运行 7 分钟以上直至手动结束。
|
||||
|
||||
**教训:给无线终端配静态 IP 必须用 `ipv4.method=manual` 整体替换,
|
||||
不能在 DHCP 连接上叠加第二个地址。**
|
||||
|
||||
## 鼠标延迟 A/B 实测(1080p60,服务端 libx264)
|
||||
|
||||
| 指标 | 基线 vsync+frame-pacing | no-vsync + no-frame-pacing |
|
||||
|---|---|---|
|
||||
| 串流帧率 | 21.64 FPS | 21.60 FPS |
|
||||
| 渲染帧率 | 18.20 FPS | 18.16 FPS |
|
||||
| 网络丢帧 | 0.00% | 0.00% |
|
||||
| 抖动丢帧 | 15.91% | 13.64% |
|
||||
| 帧队列延迟 | 44.13 ms | 47.21 ms |
|
||||
| 渲染/VSync | 48.79 ms | 49.49 ms |
|
||||
|
||||
结论:关闭 vsync/frame-pacing **无改善**(队列延迟反而略升)。
|
||||
~45ms 队列延迟 ≈ 一个帧间隔(21.6FPS → 46ms/帧),
|
||||
瓶颈是服务端软编码帧率,不是客户端参数。
|
||||
决定:保持 vsync + frame-pacing 开启(C++ 默认),
|
||||
`etc/moonlight.conf` 默认值已同步改回 `1`。
|
||||
鼠标卡顿的根治手段仍是服务端 GPU 直通 + NVENC。
|
||||
|
||||
---
|
||||
|
||||
# 2026-10-29 下午追加:四件事落地(删 RDP / 退出设计 / 过场层 / ultrafast 实测)
|
||||
|
||||
## 已交付(全部编译部署到盒子并实测)
|
||||
|
||||
1. **删 RDP**:`rdp_session.*`、FreeRDP 渲染管线、CMake 构建依赖全部移除,
|
||||
协议概念(`Protocol` 枚举)从代码删除,Moonlight 是唯一会话通道;
|
||||
RDP 代码只存在于 git 历史(网关备用)。部署后 `ldd kiosk-shell` 无任何 freerdp 依赖。
|
||||
2. **退出设计三层**:
|
||||
- 会话内 `Ctrl+Alt+Shift+Q` 退出串流回桌面列表——moonlight-qt 自带快捷键为主路径,
|
||||
kiosk 侧保留同一快捷键兜底(串流未建立/焦点仍在 kiosk 时生效);
|
||||
- 桌面列表右上角"退出登录"→ 登录页(已有);
|
||||
- 登录页删除"退出"按钮(退出终端是维护功能,只留在管理菜单 Ctrl+Alt+Shift+M)。
|
||||
3. **加载过场层**:新增 `src/transition_overlay.*`,点连接后全屏置顶黑层
|
||||
("正在进入云桌面…"动态省略号 + 旋轮),盖住 moonlight-qt 启动期左上角小窗闪烁;
|
||||
首帧检测用 moonlight 进程 CPU 时间近似(软解 CPU 远高于握手期,阈值 120 tick / 15s 兜底),
|
||||
检测到后 1 秒淡出。活动期间吞键盘鼠标、周期 raise 保持置顶。
|
||||
4. **Sunshine x264 预设 ultrafast**:VM 100 的 `C:\Program Files\Sunshine\config\sunshine.conf`
|
||||
写入 `sw_preset = ultrafast`(该版本支持 `sw_preset`/`sw_tune` 键),服务已重启生效。
|
||||
|
||||
## 关键实测结论:21.6 FPS 帽子不在 x264,旧根因判定被推翻
|
||||
|
||||
ultrafast 前后对照(1080p60 / H.264 / 盒子软解,同参数同网络):
|
||||
|
||||
| 指标 | medium(基线 10-28) | ultrafast(本次) |
|
||||
|---|---|---|
|
||||
| Video stream FPS | 21.59~21.64 | 21.57~21.61 |
|
||||
| 抖动丢帧 | 13.64%~15.91% | 13.64%(分毫不差) |
|
||||
| 客户端解码 | 15.04 ms | 10.51~11.14 ms |
|
||||
|
||||
决定性证据:
|
||||
|
||||
- 串流进行中 VM 内 `Processor(_Total)` CPU 仅 0.2%~0.5%,
|
||||
sunshine 进程自服务重启起累计 CPU 时间仅 14 秒(含三次共约 3 分钟会话);
|
||||
- 用 xdotool 持续晃鼠标制造画面运动,帧率与 CPU 都不变;
|
||||
- libx264 只有 `MMX2 SSE2Fast SSSE3 SSE4.2`——VM CPU 型号是
|
||||
`QEMU Virtual CPU version 2.5+`(kvm64 类),宿主指令集(含 AVX2)没有透传。
|
||||
|
||||
结论:**21.6 FPS 的上限来自虚拟显示/捕获节奏(IddSampleDriver/MttVDD 呈现与
|
||||
Desktop Duplication 的脏帧节奏,叠加 QEMU 显示栈),不是编码器算力**——
|
||||
编码器近乎空转,预设怎么调都不会变。ultrafast 保留(无害,客户端解码也略降)。
|
||||
根治路径按优先级:GPU 直通(NVENC + 真实显示栈)> 换 VM 显示方案
|
||||
(VirtIO-GPU / cpu=host 先排除 QEMU 显示栈因素)> 都不是调 Sunshine 参数能解决的。
|
||||
|
||||
## 运维新装 Linux 云桌面 VM(2026-10-29 接入信息)
|
||||
|
||||
| VM | Proxmox | 地址 | 账号 | 实测 |
|
||||
|---|---|---|---|---|
|
||||
| kylin-test | 101 | 192.168.1.71 | test1 / yx66668888 | SSH 可登,银河麒麟 V10 SP1 桌面版,UKUI + Xorg,图形会话运行中 |
|
||||
| ubuntu-test | 102 | 192.168.1.72 | test2 / yx66668888 | SSH 可登,Ubuntu 24.04.4 LTS,GNOME + Xorg,图形会话运行中(4 核 / 5.8GiB) |
|
||||
|
||||
两台均未装 Sunshine(47989 等端口关闭),还不能被 Moonlight 连接。
|
||||
接入桌面列表的步骤:装 Sunshine(Linux 版)→ 配对 → 在盒子"桌面连接设置"里
|
||||
把 Ubuntu/麒麟卡片的地址填上即可(kiosk 代码已支持,无需改代码)。
|
||||
|
||||
---
|
||||
|
||||
# 2026-10-30 下午追加:GPU 直通实操 + 配对断链修复(大量新坑位)
|
||||
|
||||
## 直通实操结论(VM 100 / GTX 965M CLEVO 1558:6557)
|
||||
|
||||
1. **vfio reset 会把 GPU 的 Subsystem ID 清零**(Windows 里实例变成
|
||||
`SUBSYS_00000000`),而 NVIDIA 移动卡驱动全部按 SUBSYS 匹配
|
||||
(本卡在 `nvcvi.inf` 的 `SUBSYS_65571558`),不注入就永远"无法安装"。
|
||||
解法 = QEMU 设备参数 `x-pci-sub-vendor-id=0x1558,x-pci-sub-device-id=0x6557`。
|
||||
2. **移动卡 vBIOS 不在卡上**(ROM BAR 为空,宿主 dmesg 反复
|
||||
"No more images in PCI ROM")。宿主开机时系统 BIOS 已 shadow 过 vBIOS,
|
||||
可从 sysfs 抽取:停 VM → `echo 0000:01:00.0 > /sys/bus/pci/drivers/vfio-pci/unbind`
|
||||
→ `echo 1 > rom; cat rom > gtx965m.rom`(108KB,55AA 开头有效)→ 重绑 vfio。
|
||||
3. **NVIDIA 安装器(-s/-x)在 qemu-ga SYSTEM 会话下静默挂起**(CPU 时间 ~0s,
|
||||
无解包无日志)。VM 内装驱动的可行路径:PC 端 7zr 解包 → 取 Display.Driver →
|
||||
zip 传入 VM → `pnputil /add-driver nvcvi.inf /install`。
|
||||
国内线路被 NVIDIA CDN 403,直链需 PC 中转(PC→scp 宿主机→宿主机 HTTP→VM 拉)。
|
||||
4. **582.78 绑定成功但 D3D 全灭**:PnP Status OK(需手动
|
||||
`pnputil /restart-device` 踢一次,开机自启是 Code 43),但 dxdiag 看不到适配器、
|
||||
NVML 初始化失败、所有硬编编码器 D3D11 建设备报 0x887A0004 (DEVICE_REMOVED)。
|
||||
已换 **566.36**(2024-12,Maxwell+VM 社区验证充分),包已装、设备已绑定;
|
||||
D3D 是否健康**尚未验证**(GPU 在替换期间被移出 VM,见下)。
|
||||
5. **可用挂载参数模板**(未来重启 VM 用):
|
||||
`-device vfio-pci,host=0000:01:00.0,bus=pcie.0,addr=0x10,x-pci-sub-vendor-id=0x1558,x-pci-sub-device-id=0x6557,romfile=gtx965m.rom`
|
||||
(rom 放 /usr/share/kvm/;另一会话在测 `hv_vendor_id=NVidiaFIX,kvm=off` 方案)。
|
||||
6. **local-lvm thin 池曾被驱动文件写满**(100% → error IO → VM 被 QEMU 暂停),
|
||||
已 `lvextend -L +13G pve/data`(49→62GB)。VM 内 C:\nvsetup 有 ~12GB 驱动
|
||||
中间产物可清理;池仍超配(160GB 卷/62GB 池),建议后续扩存储。
|
||||
|
||||
## 配对断链与修复(重要运维知识)
|
||||
|
||||
- `sunshine_state.json` 存服务器 uniqueid 与已配对客户端。**删它 = 所有客户端失配**。
|
||||
修复后的新流程(2026.914 的"Web UI 批准式"配对):
|
||||
1. 盒子:`moonlight-qt pair 192.168.1.70 --pin 4321`(**PIN 由客户端生成**,可 `--pin` 指定);
|
||||
2. 宿主/PC 侧调 Sunshine Web API 批准:
|
||||
`GET /api/csrf-token`(Basic Auth)→
|
||||
`POST /api/pin` body `{"pairing_id":"<GET /api/pin 里拿>","pin":"4321"}`,
|
||||
头带 `X-CSRF-Token` + Basic Auth(无 /api/login;POST 必须 CSRF 头)。
|
||||
- Sunshine Web 管理凭据由环境管理员保管,不在仓库记录明文密码。
|
||||
- **旧版缓存 uniqueid 会让 pair 卡 "Found unexpected PC" 死循环**:
|
||||
盒子 `~/.config/Moonlight Game Streaming Project/` 整目录删除后重配即可。
|
||||
- Sunshine 重启不再换 uniqueid(持久化已恢复);**若再出现"每次重启换 uniqueid",
|
||||
先查 sunshine_state.json 是否能写**。
|
||||
- NVIDIA 设备半坏时 Sunshine 捕获初始化会崩循环(服务恢复 5-6 分钟拉起一次),
|
||||
表象是"每几分钟换一次 UUID + 配对全部失败"。根因仍是驱动 D3D 问题。
|
||||
|
||||
## 2026-11-02:GPU/NVENC与客户端渲染阶段性实测(最终状态见下节)
|
||||
|
||||
- GTX 965M 直通、582.78 驱动、DXGI/D3D 和 NVENC 已打通;Sunshine 实际使用
|
||||
`h264_nvenc`,连续 60Hz 动画下服务端发送约 59 FPS、网络丢帧 0%、服务端处理约 4.5ms。
|
||||
- Virtual Display Driver 已从 8.35.11.365 升级到官方签名的 11.30.4.434;
|
||||
保持 1920x1080@60,并绑定 `NVIDIA GeForce GTX 965M`。
|
||||
- 鼠标最终采用 Windows 远程光标方案:白色 Windows 光标在 NVENC/30FPS 稳定链路下已不卡顿;
|
||||
已删除 kiosk 自动开启盒子本地黑色光标的逻辑,并移除 Windows 透明光标登录任务、恢复系统光标,
|
||||
避免黑色光标带动白色光标的双光标现象。
|
||||
- 客户端瓶颈实测定位在 RK3528 的 Xorg/MATE 合成和 SDL OpenGL 呈现:关闭 MATE
|
||||
compositor 后,1080p60 连续动画渲染从 16.3 FPS 提到 28.8 FPS;强制
|
||||
`SDL_RENDER_DRIVER=opengles2` 后提高到 37.4 FPS。
|
||||
- 900p60 约 43.4 FPS、720p60 约 45.4 FPS,画质损失大但仍不到60;最终产品默认采用
|
||||
**1920x1080@30**:实测输入30.04、解码30.04、渲染29.90 FPS,网络丢帧0%、
|
||||
抖动丢帧0.46%、客户端渲染4.69ms,体感比“名义60实际持续丢帧”稳定。
|
||||
- 新增 `vdi-kiosk-start`,开机固定盒子输出1920x1080@60后启动受管 kiosk;
|
||||
`vdi-kiosk.desktop` 已改走该入口。保留 VSync/Frame Pacing(关闭无收益)。
|
||||
|
||||
## 当前现场(2026-11-02 15:45,优先于上面的历史实验结论)
|
||||
|
||||
- 真实2K三段已验证:Windows MttVDD 2560x1440@30、Moonlight 2560x1440@20(30000 Kbps)、盒子 HDMI 2560x1440@60。1920x1440不是本项目验收所说的2K。
|
||||
- 显示器 EDID 首选时序为2560x1440、241.70MHz;当前RK3528 vendor内核拒绝此时钟。改用已支持的297MHz和加长垂直消隐后成功:
|
||||
`297.00 2560 2608 2640 2720 1440 1443 1448 1820 +HSync -VSync`,实际约59.994Hz,link-status Good。
|
||||
此模式不是显示器原厂时序,已在现场显示器测试;其他显示器必须重新验证,启动器失败回退1080p,不能把回退结果报成2K。
|
||||
- `vdi-kiosk-start` 开机建立上述模式并启动单一kiosk链;盒子真实重启后仍为2560x1440,单链自启验收通过。保留GLES2和关闭MATE合成器。
|
||||
- VDD配置路径经25.7.23源码核查为 `C:\VirtualDisplayDriver\vdd_settings.xml`,包含1080p60和1440p30/60;旧安装目录的XML不应再作为生效证据。
|
||||
- Windows计划任务 `VDISet2KDisplay` 以已登录test用户的交互令牌运行,执行 `C:\ProgramData\VDI\set-2k-display.ps1`。脚本动态查找活动的Virtual Display Driver,不能写死DISPLAY编号,也不能在Guest Agent的Session 0里切换交互桌面。
|
||||
对应源码归档为 `tools/set-2k-display.ps1`;重启VM后日志 `set-2k-result.txt` 返回result=0、mode=2560x1440@30。Windows 125% DPI下逻辑坐标2048x1152不代表物理像素不足2K。
|
||||
- NVIDIA正常期间的连续动画测试:2K30输入29.92/渲染21.52 FPS、抖动丢帧27.75%;2K24渲染22.15、丢帧8.14%;2K20输入20.31/渲染19.67、网络丢帧0%、抖动丢帧0.94%。所以现场选择2K20,未达到2K30/60满帧。
|
||||
- **重启验收发现GPU回归故障,当前NVENC未恢复**:驱动582.78、ROM `gtx965m-pci.rom`、Proxmox hostpci0配置仍保留;VM重启后Code43。设备重启可暂时令PnP显示OK,但NVML仍报Not Found,Sunshine实际使用 `libx264 [software]`。
|
||||
客户机内重复重启设备、暂时停用VDD/Sunshine、VM完整关机再启动均未恢复;之前NVENC成功及2K20性能数据不能作为当前性能保证。
|
||||
- 已删除无效的 `VDIRecoverNvidia` 自动恢复任务和脚本。宿主GPU只有bus reset,`vfio_pci.disable_idle_d3=Y`,VM关机后仍D0;这些证据不足以断言唯一根因,未修改运维的VFIO模块/ROM配置,也未重启宿主机或影响VM101/102。
|
||||
- 当前盒子可正常进入2K20 Windows桌面,白色Windows光标保留、本地黑色光标自动切换已删除;GPU恢复需要与运维协同,验收必须检查nvidia-smi、DXGI和Sunshine实际编码器,不能只看设备管理器。
|
||||
|
||||
|
||||
---
|
||||
|
||||
# 2026-10-30 追加:开机直进桌面列表
|
||||
|
||||
按产品决定,盒子开机不再显示 kiosk 登录页,直接进入"选择云桌面"列表页:
|
||||
|
||||
- 构造时以设备身份(SN)自动走 DesktopService 演示认证(SN 即当前用户展示),
|
||||
状态链 `kConfig -> kAuthenticating -> kDesktopList` 同步完成,无登录页闪烁;
|
||||
- 列表页右上角"退出登录"按钮移除(无登录概念后无去向),管理菜单"退出终端"保留;
|
||||
- **登录页与 DesktopService 认证代码全部保留**:接入管理平台正式认证
|
||||
(/api/v1/terminal/session/login)后,由 DesktopService 决定是否回到登录流程,
|
||||
页面代码无需重写;git 历史含完整"登录页 -> 桌面列表"产品流程版本。
|
||||
- 已编译部署盒子并抓屏验证:开机落在桌面列表,Windows 可连接、Ubuntu/麒麟待分配。
|
||||
@ -1,67 +0,0 @@
|
||||
# Moonlight 客户端渲染 profile
|
||||
|
||||
## 目的
|
||||
|
||||
统一 `kiosk-shell` 产品入口与现场调试入口的 Moonlight 呈现参数,避免测试时只改了
|
||||
`vdi-moonlight` 脚本而正式页面仍使用另一套 VSync、Frame Pacing 和解码器配置。
|
||||
|
||||
代码默认 profile 是 `smooth`;RK3528现场在硬解与EGL验证通过后,通过render.conf
|
||||
选择了 `low-latency` 和2K30。软件基线和其他未验证设备仍使用smooth。
|
||||
不把关闭同步当成通用修复,现场对照与风险见 `rendering-execution-20261102.md`。
|
||||
|
||||
## 产品入口环境变量
|
||||
|
||||
这些变量由 `kiosk-shell` 和 `vdi-moonlight` 调试启动器读取,重启串流进程后生效:
|
||||
|
||||
```text
|
||||
VDI_STREAM_PROFILE=smooth # 默认,VSync + Frame Pacing
|
||||
VDI_STREAM_PROFILE=low-latency # 关闭 VSync + Frame Pacing,减少呈现等待
|
||||
VDI_STREAM_PROFILE=diagnostic # smooth + Moonlight 性能叠图
|
||||
VDI_STREAM_STATS=1 # 独立开启叠图,可同时比较smooth和low-latency
|
||||
VDI_MOONLIGHT_DECODER=software # 系统客户端默认;RKMPP现场配置使用 hardware
|
||||
VDI_MOONLIGHT_CODEC=H.264 # 当前默认,先不切换 HEVC/AV1
|
||||
VDI_MOONLIGHT_PROGRAM=... # 可指定独立构建的Moonlight启动器,不覆盖系统二进制
|
||||
VDI_SDL_FORCE_EGL=1 # 仅现场验证 SDL X11 EGL 时设置
|
||||
VDI_SDL_VIDEODRIVER=wayland # 仅实验;正式入口的X11窗口激活逻辑尚需适配
|
||||
```
|
||||
|
||||
每次会话启动时,最终 profile、解码器、编解码器、同步开关、SDL renderer 和完整参数
|
||||
会写入 `~/vdi-terminal/logs/moonlight-YYYYMMDD.log` 的 `[VDI launch]` 段落。
|
||||
|
||||
## A/B 顺序
|
||||
|
||||
1. 先恢复并确认 Sunshine 使用 `h264_nvenc`,否则客户端 A/B 不能说明服务端问题已经解决。
|
||||
2. 1080p60:比较 `smooth` 与 `low-latency`,两组都设置 `VDI_STREAM_STATS=1` 收集 queue/render/jitter。
|
||||
3. RK3528 硬解与 EGL/GLES 现场路径已验证;后续仍需单独完成 2K60 验收,不能由 2K30 结果推断。
|
||||
4. 任何 profile 都必须保留 `--absolute-mouse`;当前已解决的双光标行为不应回退。
|
||||
|
||||
## GPU 重启故障边界
|
||||
|
||||
`gpu-restart-diagnosis-20261102.md` 已确认当前没有经过验证的重启后软件恢复办法。profile
|
||||
只改变客户端呈现,不会修复 Windows Code 43、NVML 失败或 Sunshine 回退 `libx264`。
|
||||
GPU 修复仍需设备厂商提供匹配的 GM204M/13D9 生产版 vBIOS,并在维护窗口内按验收标准
|
||||
重复验证 `nvidia-smi`、DXGI、`h264_nvenc` 和 Windows/VM 重启后的持久性。
|
||||
|
||||
## RK3528现场配置与回退
|
||||
|
||||
`~/.config/vdi-terminal/render.conf` 当前内容:
|
||||
|
||||
```ini
|
||||
profile=low-latency
|
||||
stats=0
|
||||
program=/home/armbian/vdi-terminal/bin/vdi-moonlight-rkmpp
|
||||
decoder=hardware
|
||||
codec=H.264
|
||||
```
|
||||
|
||||
Windows桌面卡当前请求2560x1440@30、30Mbps,实际持续动画约23FPS;
|
||||
虚拟屏仍为30Hz。私有构建位于 `~/vdi-build/rkmpp-experiment`,不要删除该目录。
|
||||
目前是用户目录内的POC部署,尚未打包为完整OTA镜像。
|
||||
|
||||
如现场发现关闭同步导致明显撕裂,可先将profile改为smooth,并将桌面卡FPS改回20。
|
||||
如需彻底退回系统客户端,退出串流后将render.conf改名保存,并恢复
|
||||
`desktops.before-rkmpp-20261102.json` 为desktops.json,然后重新启动kiosk受管栈。
|
||||
系统 `/usr/bin/moonlight-qt` 未被覆盖,不需要卸载MPP或替换系统FFmpeg。
|
||||
|
||||
设备访问规则是 `/etc/udev/rules.d/70-vdi-rockchip-video.rules`,限定video组0660;
|
||||
部署工具会安装并重载规则。udev匹配已验证,本轮未重启盒子做重启后串流复验。
|
||||
@ -1,62 +0,0 @@
|
||||
# 鼠标进一步优化执行记录(2026-11-03)
|
||||
|
||||
这是用户要求“制定计划并按照计划干”后的本轮记录,与此前已部署的两个输入补丁分开。
|
||||
|
||||
后续更新:用户确认运维修复后已恢复联调;当前已落地VDD60Hz/客户端45FPS,约43~44FPS渲染,详见`docs/2k60-validation-20261103.md`。下文保留运维期间的历史记录,不再代表当前GPU故障或联调暂停状态。
|
||||
|
||||
最新补充:用户要求尝试独立光标,已完成受控X11本地指针原型,四种真实Windows静态形状/热点、隐藏、暂停客户端时独立移动、32/32区域点击和辅助租约超时清理均通过。没有实现自动形状同步或正式双方交接协议;测试已收尾并恢复单个Windows远程白色光标,2K45及正常二进制保留。详见`docs/independent-cursor-feasibility-20261103.md`,勿将下文历史“未实现”理解为本地原型未做,也勿将原型误报为产品已部署。
|
||||
|
||||
## 实际进度
|
||||
|
||||
用户确认运维正在处理 GPU Code43。测试中 Windows 多次外部关机/启动;本轮未发出任何 VM/设备/宿主重启指令。已暂停 Windows 配置修改和串流测试,继续完成盒子端开发与文档,尚未达到最终性能验收。
|
||||
|
||||
| 阶段 | 状态 | 结果 |
|
||||
| --- | --- | --- |
|
||||
| 基线 | 完成初检 | GTX965M Code43 / libx264;VDD活动设备DISPLAY2、2K30Hz,枚举支持2K60Hz;盒子输出正常2K60 |
|
||||
| 刷新率 | 部分完成,联调暂停 | 工具支持查询/枚举/临时应用/恢复;临时切换2K60并读回成功;完整动态对照待运维结束 |
|
||||
| 输入调度 | 开发、编译、自动化测试完成 | 第三补丁及可关闭的诊断可用;当前独立渲染线程没有主线程render调用,未证明收益,默认未启用 |
|
||||
| 独立光标 | 设计完成,尚未实现 | 必须改双方协议和捕获交接;不能把全局快捷键或叠加本地箭头当成独立光标功能 |
|
||||
| 现场收尾 | 盒子完成;Windows待运维结束 | 测试串流已退出,默认客户端还原原哈希;显示守护正常;Windows测试窗口随关机退出,任务登记待可安全联调时清理 |
|
||||
|
||||
## 新增实现
|
||||
|
||||
- `terminal/moonlight/patches/0003-vdi-input-priority.patch`:帧前有限输入调度(`VDI_INPUT_PRIORITY=1`才启用)和汇总诊断(`VDI_INPUT_TIMING=1`才启用)。只让紧邻的本窗口真实鼠标/键盘事件优先,最多32条或2ms,不跨焦点、窗口、退出、其他帧或设备事件。
|
||||
- `terminal/moonlight/tests/test_input_priority.cpp`、`tools/test-moonlight-latency.sh`:普通与AddressSanitizer测试覆盖新增边界;现有移动/按下/释放1000轮回归保持通过。ASAN现在也实际覆盖SDL/帧所有权测试,所有测试显式保留断言。
|
||||
- `tools/set-vdi-display-mode.ps1`、`tools/run-mouse-display-probe.py`:只选择活动VDD,拒绝多VDD歧义;模式预检、备份、临时应用、读回和恢复。通过已登录用户会话运行并清理本次探测任务,不写注册表持久模式。
|
||||
- `tools/bench-mouse-stream.sh`、`tools/inject-mouse-sweep.py`:独立标签日志,拒绝覆盖已有证据或现有用户串流;125Hz/20秒模拟移动和32次点击。仅可在Windows临时全屏测试窗口已运行时使用,不可对普通工作桌面盲目注入点击。
|
||||
- `tools/input-latency-bench.ps1`:修正源定时器量化问题,使用绝对单调时钟帧序号及测试进程1ms定时精度,退出时释放。修正版尚未在Windows部署运行。
|
||||
|
||||
## 初测与限制
|
||||
|
||||
| 项目 | 2K30、VDD30 | 2K60请求、VDD60 |
|
||||
| --- | --- | --- |
|
||||
| 接收/渲染 FPS | 29.01 / 28.39 | 31.08 / 30.56 |
|
||||
| 主机处理平均 | 39.9ms | 45.3ms |
|
||||
| 解码/排队/呈现平均 | 11.90 / 0.33 / 1.81ms | 10.19 / 0.16 / 1.73ms |
|
||||
| 网络延迟/抖动丢帧 | 4ms / 2.15% | 10ms / 1.68% |
|
||||
| 新输入优先调度 | 未启用 | 未启用(诊断候选二进制) |
|
||||
|
||||
**这两段不能作为“2K60达标”或性能提升结论。** 旧WinForms测试窗口的16ms定时器被系统时间粒度量化,实际画面源约32FPS,网络也有波动。已修正本地测试源,需要重新对照。中断的baseline30/paced30不计入数据。
|
||||
|
||||
诊断会话trial60-no-priority派发年龄2601个样本,平均0.97ms、P95=1ms、最大1006ms;主线程render调用样本0。P95说明该会话大多数输入事件排队较短,不能忽略最大值,也不能据此宣称物理鼠标到屏幕只有1ms。约1秒异常可能涉及启动遗留事件,需要下一次带启动阶段分类的诊断核实,尚不能认定原因。基线会话Windows窗口确认收到32条交替点击,全部落在预期左右区域。
|
||||
|
||||
源代码核实PacerRender独立线程负责当前EGL呈现,因而新的“呈现前处理输入”调度对这条路径没有已证明的作用,保持关闭。当前鼠标画面仍需服务端捕获/编码后返回;下一步重点仍是修复GPU、验证足够的捕获/编码帧率和独立光标交接。
|
||||
|
||||
## 构建与回退
|
||||
|
||||
盒子目录:`/home/armbian/vdi-build/rkmpp-experiment/build/moonlight/app/`。
|
||||
|
||||
- 正常默认`moonlight`已恢复本轮前版本,SHA256 `35de7b1a4ade583823b23b5f2e012307f6d0a3848c8eb97bd6d4de9660afa00f`。
|
||||
- 本轮候选`moonlight.input-priority-20261103`,SHA256 `ef0707ed2053f15ad6f62a33a79ac360237b27f307777c79b379b105fabfe3a2`;仅用显式`VDI_RKMPP_CLIENT`选择。正常wrapper未改用它。
|
||||
- `moonlight.before-priority-20261103`为正常版本的额外备份。
|
||||
- VDD60是临时模式,没有修改原登录设置2K30的脚本。VM关机后不以旧读回结果推断新启动状态;恢复联调首先Query,必要时Restore。备份位于`C:\ProgramData\VDI\mouse-optimization\display-before.json`。
|
||||
- Windows基准任务没有开机/周期触发器。恢复联调后先执行`python tools/run-input-latency-bench.py stop`清理遗留登记,再启动修正后的测试源。
|
||||
|
||||
证据在`docs/evidence/mouse-optimization-20261103/`。没有重启盒子,没有修改GPU直通/ROM、Sunshine配置、Windows全局鼠标主题或其他虚拟机。
|
||||
|
||||
## 运维结束后的顺序
|
||||
|
||||
1. 清理临时基准任务,查询VDD;记录nvidia-smi、NVIDIA D3D11及当前Sunshine实际编码器,不能只看编码器探测日志。
|
||||
2. 用修正源确认足够的源帧率,再做2K30/60配对和输入/拖窗验收。未通过则保持当前稳定参数。
|
||||
3. 显式选择候选客户端并开启诊断,核实约1秒最大派发年龄是否属于初始化事件;当前EGL路径不默认启用帧前输入优先。
|
||||
4. 按独立光标设计准备固定版本Sunshine构建,然后实现双方协议、帧边界交接、形状/显隐及故障回退,再进行单光标验收。此项尚未完成。
|
||||
@ -1,43 +0,0 @@
|
||||
# 鼠标与拖窗优化实施计划及结果(2026-11-03)
|
||||
|
||||
## 目标与边界
|
||||
|
||||
降低鼠标输入等待和画面更新延迟,保留单个 Windows 白色光标、正确点击位置、完整窗口拖动及现有显示器恢复功能。用实测决定是否启用,不能将帧间隔、客户端统计或本地光标响应称为端到端零延迟。
|
||||
|
||||
现场初检:盒子实际启动 2560×1440 / 30 FPS / RKMPP / EGL;Windows GTX 965M 仍 Code 43,Sunshine 实际 libx264,静态画面最低目标约 15 FPS。没有可直接构建 Sunshine 的现成 Windows C++ 工具链。
|
||||
|
||||
## 执行顺序
|
||||
|
||||
1. **记录基线。** 保存当前配置、二进制校验及串流日志,确认 GPU、虚拟屏刷新率、网络和解码路径。静态画面重复帧不能作为动态画面性能基准。
|
||||
2. **验证刷新率整条链路。** 用临时测试窗口对比虚拟屏/串流 30 与 60 FPS;修改必须支持只读查询、枚举、测试、临时应用和恢复。只有动态画面实测改善且解码/丢帧可接受,才更改默认配置;否则恢复 2K30。
|
||||
3. **改进 Moonlight 输入调度。** 检查主线程呈现阻塞输入的问题。允许帧呈现前先处理紧邻的有限数量输入,保持输入间原始顺序、窗口/焦点边界及工作量上限;增加可关闭的输入队列年龄与呈现阻塞统计。编译、自动化边界测试及真实串流对照后决定是否启用。
|
||||
4. **核实独立光标的落地条件。** 必须同步光标形状/热点/显隐、单光标交接、缩放及重连回退。Sunshine 现有 Ctrl+Alt+Shift+N 是全局开关,没有按会话确认,不能据此可靠实现断线回退;不能只叠加本地箭头后宣称完成。先形成与实际源码相符的协议和验收要求,具备服务端构建条件后才能集成双方修改并验收。
|
||||
5. **交付与现场恢复。** 移除临时测试任务/窗口,恢复正常云桌面连接;记录实际部署状态、测试结果、回退命令和未完成项。
|
||||
|
||||
## 验收
|
||||
|
||||
- 输入:快速交替点击无错位;双击、右键、拖动中的按下/释放顺序保持正确;失焦及退出快捷键正常。
|
||||
- 调度:窗口和焦点事件不能越过;高频鼠标不能无限拖延视频;禁用新开关可恢复旧调度。
|
||||
- 视频:相同动态测试源、相同分辨率/码率下记录接收/渲染 FPS、主机处理、解码、排队、呈现及网络丢帧;避免用空闲画面的平均 FPS 下结论。
|
||||
- 光标:只有一个指针;文字/缩放/忙碌形状、显隐正确;断线/辅助通道故障不能无光标或双光标。
|
||||
- 严格输入到屏幕延迟留待高速摄像/外部触发验证,客户端软件统计仅衡量其中一段。
|
||||
|
||||
## GPU 前置条件
|
||||
|
||||
本轮不重启宿主、不刷 ROM、不动 VM101/102。Code 43 仍需按现有运维方案排查,修复后再验收实际 NVIDIA D3D11 和 NVENC;不能只设置 encoder=nvenc 就认定修复。
|
||||
|
||||
## 前序优化结果
|
||||
|
||||
代码修改、自动化测试和现场配对串流对照已完成,结果见
|
||||
`docs/input-rendering-optimization-20261103.md`。当前状态为:点击排序和客户端排队已改善,
|
||||
仍需用户对真实拖窗、右键、快速双击进行体验验收;严格的输入到显示延迟仍需高速摄像或外部触发测量。
|
||||
|
||||
## 本轮进一步优化进度
|
||||
|
||||
用户确认运维正在处理 GPU Code43,已暂停 Windows 配置修改和串流测试,避免同时操作。盒子端新增第三补丁已编译并通过普通/ASAN测试,但当前独立渲染线程没有主线程呈现调用,未证明新调度收益,因此默认客户端已恢复本轮前版本。60Hz对照尚未完成,独立光标只完成设计,尚未实现。
|
||||
|
||||
本轮逐项结果、初测数据限制、候选版本和恢复联调步骤见 `docs/mouse-optimization-execution-20261103.md`。独立光标协议和单光标交接要求见 `docs/independent-cursor-design-20261103.md`。
|
||||
|
||||
## 运维修复后的继续执行
|
||||
|
||||
用户提供GPU修复验收后已恢复联调,现场复核错误码0和实际NVENC。修正测试源对比表明:当前盒子请求2K60实际仅渲染约46FPS,原队列/VSync/nearest实验均未改善;按实测将默认落为VDD60Hz、Moonlight45FPS,实际约43~44FPS,客户端队列约1.3~1.6ms。已保存配置、验证登录显示脚本、从正常入口重连并清理临时任务。完整数据与回退见`docs/2k60-validation-20261103.md`;稳定2K60与独立光标尚未完成。
|
||||
72
docs/operations.md
Normal file
@ -0,0 +1,72 @@
|
||||
# 运维说明
|
||||
|
||||
本文件维护云桌面项目的排障与回退入口。监控页面、采集器和看门狗后台的维护说明以独立 [vdi-monitor 项目](../../vdi-monitor/README.md) 为准。
|
||||
|
||||
<a id="monitor"></a>
|
||||
## 日常监控看哪里
|
||||
|
||||
| 事项 | 位置 | 保存范围 |
|
||||
| --- | --- | --- |
|
||||
| 实时监控页面 | 联调环境 `http://192.168.1.51:8090` | 硬件、盒子状态、日志解释等;实际功能看 monitor 版本 |
|
||||
| 盒子采集器 | `vdi-monitor/deploy/box-sync.sh` | 约每 30 秒 SSH 采集一次 |
|
||||
| 监控日志解读 | `vdi-monitor/source/box_metrics.py` 的 `LOG_RULES` / `diagnose()` | 页面规则在监控项目维护 |
|
||||
| PVE 最新原始数据/状态 | `/var/lib/vdi-monitor/box/raw.txt`、`status.txt` | 最新快照,不是完整历史原始日志库 |
|
||||
| PVE 指标历史 | `/var/lib/vdi-monitor/box/history.log` | 约 7 天/最多约 20000 行,保存时间、健康、温度、内存、磁盘、kiosk、uptime 等指标 |
|
||||
| 开发证据 | 本仓库 [evidence/](evidence/) | 固定测试/故障的原始材料,不能由指标历史替代 |
|
||||
|
||||
因此旧的过夜观察说明可合并,但不能因为“监控接入”就删除唯一故障日志、回退配置或停掉盒子的记录服务。指标保留期限也不是故障证据归档期限。
|
||||
|
||||
## 盒子日志和健康记录
|
||||
|
||||
| 路径/服务 | 用途 |
|
||||
| --- | --- |
|
||||
| `/tmp/vdi-monitor.log` | kiosk 看门狗输出;重启可能丢失 |
|
||||
| `/tmp/vdi-kiosk.heartbeat` | kiosk 心跳,不是错误根因 |
|
||||
| `~/vdi-terminal/logs/*.log` | Moonlight 会话及过夜健康记录等 |
|
||||
| `~/vdi-terminal/logs/overnight-summary.txt` | 中文汇总 |
|
||||
| `~/vdi-terminal/logs/overnight-health-YYYYMMDD.log` | 每分钟 JSON 行,按日轮转 |
|
||||
| `~/vdi-terminal/state/overnight-state.json` | 观察器持久 boot ID/计数基线 |
|
||||
| `/var/lib/vdi-observer/pstore.json` | 限量只读采集的 pstore 快照 |
|
||||
| `vdi-overnight-observer.service` | 盒子持久记录,与监控页面采集互补 |
|
||||
|
||||
11-04 观察器从启用时建立基线,不能把“计数 0”解释为盒子历史从未重启。用 boot ID 与单调 uptime 判断整机重启,服务重启不算整机重启;墙上时钟变化和 `last` 不能单独定论。
|
||||
|
||||
该盒子 `/var/log` 是 zram;journal 链接到 eMMC 的 `/var/log.hdd/journal`。观察器部署了 persistent journald 配置,磁盘预算 64MB、运行预算 16MB、保留 7 天、同步间隔 30 秒;观察日志保留 7 个日期,每日最多 4 个 8MB 分段。历史 41 字节的 `reboot: Restarting system` 是旧 pstore 基线,不是新故障证明。突然断电/硬复位可能缺少末尾日志,缺日志不能直接证明电源故障。
|
||||
|
||||
要停用观察器可由管理员执行 `sudo systemctl disable --now vdi-overnight-observer.service`;该命令只停止记录,不删除已有数据。本次整理未执行。具体初次部署与回退细节在[归档](archive/README.md)的 `overnight-box-observation-20261104.md` 中。
|
||||
|
||||
<a id="diagnosis"></a>
|
||||
## 常见问题的排查顺序
|
||||
|
||||
1. **开机/退出出现盒子桌面或系统 greeter**:核对真实运行会话 `DESKTOP_SESSION=vdi-kiosk`、LightDM 配置、是否残留 `mate-session/mate-panel/marco/caja`,检查是否从 SSH 脱离 LightDM 拉起了 kiosk。不要仅检查磁盘默认会话或一张开机截图。
|
||||
2. **进入时左上角小窗**:核对 Moonlight 0007 构建、`VDI_KIOSK_STREAM=1`、SDL 首次映射几何、连接遮罩,使用进入探针;只测退出不能覆盖进入阶段。
|
||||
3. **黑边/画面变矮**:同时核对盒子 XRandR、Moonlight 窗口、客体捕获尺寸与显示守护;原 1920×1440 回退会让 16:9 视频上下留边。
|
||||
4. **Win+Shift+S 无反应**:先检查传给 Moonlight 的系统键捕获参数,再检查 Windows 截图组件与客体收到的键;“不弹底层窗口”和“客体截图成功”是两个验收点。
|
||||
5. **指针流畅但点击/拖窗慢**:区分本地光标移动与视频回显,查看实际编码器、捕获路径、主机处理延迟、网络 RTT、解码/渲染;Linux 软件编码瓶颈不能靠调鼠标速度解决。
|
||||
6. **按钮像一直按住/拖拽失效**:核对 0006 失焦/退出按钮释放与当前二进制,不把输入状态残留都当作网络延迟。
|
||||
7. **Linux 启动失败/光标租约失败**:检查二进制与 profile 的 SHA、远端 guard 和普通 Sunshine 恢复状态。11-09 曾因旧 SHA 拒绝,也出现普通捕获 503;这些失败没有计入通过样本。
|
||||
8. **Windows 编码突然慢**:查询 GPU Code43、`nvidia-smi`、Sunshine 实际编码器,不能只因设备名称存在就认定 NVENC 正常。
|
||||
|
||||
盒子显示查询(SSH 内使用已授权的图形会话):
|
||||
|
||||
```bash
|
||||
systemctl --user status vdi-display-guard.service
|
||||
sudo -n journalctl _SYSTEMD_USER_UNIT=vdi-display-guard.service -n 50 --no-pager
|
||||
DISPLAY=:0 XAUTHORITY=/home/armbian/.Xauthority xrandr --query
|
||||
```
|
||||
|
||||
会话交接探针为 [verify_entry_surface.py](../terminal/kiosk-shell/tests/verify_entry_surface.py) 与 [verify_return_surface.py](../terminal/kiosk-shell/tests/verify_return_surface.py)。它们会实际连接/退出桌面,先阅读脚本参数与前置条件,在允许打断盒子当前会话时运行。
|
||||
|
||||
## 异常退出与监控自动恢复
|
||||
|
||||
11-04 Windows 非正常退出与 USB 隧道重启时间接近,已保留 PVE/Windows 时间线,但缺少 QEMU 退出码、栈和转储,原因尚未确认。后续在隔离 VM 或维护窗口记录 QEMU stderr、QMP 事件和退出状态,再复现隧道断开、拔盘等情景;不能把相关性写成已确认根因。
|
||||
|
||||
`vdi-monitor` 交接版本带 VM 自动恢复和 USB watcher 调整。维护前应开启该 VM 维护模式或关闭自动恢复;其“最近 90 秒 pvedaemon 日志”识别不能可靠区分所有正常停止。自动恢复成功、VM 重新 running 不等于退出原因解决。详见监控项目[已知限制](../../vdi-monitor/docs/运维说明与已知限制.md)。
|
||||
|
||||
## 备份与回退原则
|
||||
|
||||
- 盒子功能备份集中在 `~/vdi-terminal/backups/`,11-09 交接修复入口见[终端说明](terminal-guide.md#build)。Linux 光标客户端和 profile SHA 必须成对恢复。
|
||||
- Windows 电源策略备份与 Linux `/var/lib/vdi/power-policy/` 见[电源策略](desktop-guide.md#power),使用对应工具 restore。
|
||||
- PCI 热插拔属性回退由 `set-fixed-pci.py restore` 管理,要求 VM 先正常关机;不能覆盖整份 PVE 配置,也不能仅客体重启。
|
||||
- U 盘回退前安全弹出并拔盘,不在文件传输时重启隧道;保留其他功能和其他公钥。
|
||||
- 不通过本次文档整理重启任何设备,不更改监控服务、系统日志保留策略或现网配置。新测试应记录真实部署版本、完整失败和最后恢复状态。
|
||||
@ -1,51 +0,0 @@
|
||||
# 盒子过夜重启与健康记录(2026-11-04)
|
||||
|
||||
已为盒子192.168.1.188启用`vdi-overnight-observer.service`,每60秒落盘一次心跳,并开机自启。记录始于2026-11-04 19:35左右;统计范围是启用之后,不冒充历史累计重启次数。没有重启盒子、Windows、Moonlight,也没有修改显示、输入、网络或GPU配置。
|
||||
|
||||
## 明早看哪里
|
||||
|
||||
盒子上的中文汇总:`/home/armbian/vdi-terminal/logs/overnight-summary.txt`。
|
||||
|
||||
详细日志:`/home/armbian/vdi-terminal/logs/overnight-health-YYYYMMDD.log`,跨午夜自动切换日期,今晚及明早两天都要看。内容为带时间和事件类型的JSON行。状态:`/home/armbian/vdi-terminal/state/overnight-state.json`。
|
||||
|
||||
```bash
|
||||
cat ~/vdi-terminal/logs/overnight-summary.txt
|
||||
systemctl status vdi-overnight-observer.service --no-pager
|
||||
grep '"event": "boot_change"\|"event": "restart_assessment"' ~/vdi-terminal/logs/overnight-health-*.log
|
||||
```
|
||||
|
||||
汇总每分钟更新启用时间、最近心跳、重启次数、当前boot ID、开机时长及最近一次重启线索判断。服务自身停止/重启不算盒子重启。
|
||||
|
||||
## 记录内容和原因边界
|
||||
|
||||
- 每分钟:真实boot ID、`/proc/uptime`、负载、可用内存、温度、剩余磁盘、网络接口状态/错误计数、Moonlight和Kiosk进程PID、串流目标/分辨率/帧率、X11桌面几何。
|
||||
- 每10分钟:当前启动的内核warning/error线索,不将普通USB电源管理或watchdog能力声明直接判断为故障原因。
|
||||
- 每次启动:与持久状态比较boot ID;变化时增加一次整机重启计数,保存上次最后心跳/运行时长,并按上次实际boot ID读取journal尾部、warning和`last -x`。使用单调运行时间辅助发现NTP/RTC时间跳变,不靠墙上时钟或`last`单独断定是否重启。
|
||||
- 重启原因只按证据报告:发现正常关机/重启流程;发现panic/OOM等线索但因果未确认;或证据不足、原因未确认。突然断电和硬复位可能没有完整记录,不能保证区分两者,更不能从缺日志直接断定电源坏了。
|
||||
- 原子替换状态/中文汇总、每次写日志执行fsync,并限制重复服务实例。日志仅记录选定进程字段,不复制凭据或连接票据。
|
||||
|
||||
低权限观察进程通过root拥有的固定启动辅助程序`/usr/local/libexec/vdi-observer-pstore`获取`/sys/fs/pstore`及`/var/lib/systemd/pstore`的只读、有大小上限快照。快照写到root拥有、systemd-journal组只读的`/var/lib/vdi-observer/pstore.json`;不清除原档案、不修改内核崩溃参数。现有归档仅含41字节`reboot: Restarting system`,文件时间陈旧,作为部署前基线保留,不能认定为今晚新发生的重启。
|
||||
|
||||
## 跨重启存储
|
||||
|
||||
现场`/var/log`是zram内存盘,而`/var/log/journal`是指向`/var/log.hdd/journal`的符号链接,后者位于eMMC ext4。新增`/etc/systemd/journald.conf.d/70-vdi-observer-journal.conf`,使用persistent、磁盘日志预算64MB、运行日志预算16MB、保留7天、30秒同步间隔,并已flush/sync到磁盘。
|
||||
|
||||
只重启了日志系统journald和本次观察服务;没有停止当前串流。journald容量限制及轮转不是“断电绝不丢最后一秒”的保证。观察日志按日保留7份,每个日期最多4个8MB分段;不会删除原Kiosk、Moonlight或其他业务日志。
|
||||
|
||||
## 验证和限制
|
||||
|
||||
11项测试覆盖:首次基线、同boot服务重启、跨boot计数及上次心跳、未知原因不误报、正常关机标记、OOM仅作为线索、旧pstore不冒充新故障、状态版本校验、连续重启/历史上限、状态与中文汇总落盘、跨日期分文件。实际验证了观察服务重启后计数仍为0、文件落盘、自动启动登记、正常显示/串流进程保持。没有为验证而真正重启盒子。
|
||||
|
||||
部署首次在systemd-tmpfiles处理Armbian journal符号链接时失败,未启动观察服务,也未影响串流;随后改为处理真实eMMC目标目录,部署成功。升级pstore读取仅重启本次观察服务,仍不计作整机重启。
|
||||
|
||||
证据:`docs/evidence/overnight-observer-20261104/`。原服务/本次配置(如存在)的备份在盒子`~/vdi-terminal/backups/overnight-observer-20261104-*`。
|
||||
|
||||
## 停止记录和回退
|
||||
|
||||
```bash
|
||||
sudo systemctl disable --now vdi-overnight-observer.service
|
||||
```
|
||||
|
||||
这只停止本次记录,已有日志和汇总保留。如需恢复journald原设置,恢复该drop-in备份或移除本次新增`70-vdi-observer-journal.conf`后重启journald;不删除已经保存的journal数据。后续不再需要时再清理本次unit/root辅助程序。
|
||||
|
||||
依据:[systemd journald存储设置](https://www.freedesktop.org/software/systemd/man/latest/journald.conf.html)、[journalctl按boot ID读取和flush/sync](https://www.freedesktop.org/software/systemd/man/latest/journalctl.html)。
|
||||
@ -1,82 +0,0 @@
|
||||
# RDP 与 Moonlight 实测对比(2026-10-28)
|
||||
|
||||
## 测试环境
|
||||
|
||||
```text
|
||||
终端:RK3528 PRO DDR4,Armbian 25.05,aarch64,LightDM + Xorg
|
||||
显示器:1920×1440 @ 75Hz
|
||||
云桌面:VM 100(win10-test),Proxmox VE 9.2.21,节点 starhe
|
||||
终端到云桌面:192.168.1.188 -> 192.168.1.70,同一局域网 WiFi
|
||||
```
|
||||
|
||||
服务端显示环境改造(本次关键前提):
|
||||
|
||||
```text
|
||||
改造前:Microsoft Basic Display Adapter,1920×1080 @ 1Hz
|
||||
改造后:Virtual Display Driver 8.35.11.365,1920×1080 @ 60Hz
|
||||
```
|
||||
|
||||
改造前 Sunshine 抓不到稳定帧(1Hz),改造后可正常编码。
|
||||
|
||||
## 对比数据
|
||||
|
||||
| 指标 | RDP(当前 kiosk-shell) | Moonlight 1080p30 | Moonlight 1080p60 |
|
||||
|---|---|---|---|
|
||||
| 协议/编码 | RDP GFX,CPU 软解 | H.264 / libx264 软编 | H.264 / libx264 软编 |
|
||||
| 分辨率 | 1920×1440 | 1920×1080 | 1920×1080 |
|
||||
| 终端 CPU | 约 111%(单核近满载) | 约 26% | 约 26% |
|
||||
| 终端内存 | 约 232 MB | 约 192 MB | 约 165 MB |
|
||||
| 终端温度 | 约 58~65℃ | 约 56℃ | 约 53℃ |
|
||||
| 实际帧率 | 无可靠统计(观感为低帧+卷帘) | 12.83 FPS | 22.79 FPS |
|
||||
| 网络丢包 | 未测 | 0.00% | 0.00% |
|
||||
| 网络抖动丢帧 | 未测 | 0.08% | 15.28% |
|
||||
| 网络延迟 | 未测 | 2 ms | 6 ms |
|
||||
| 主机处理延迟 | 未测 | 14.4 / 27.6 / 平均 18.1 ms | 18.8 / 78.3 / 平均 38.2 ms |
|
||||
| 平均渲染耗时 | 未测 | 3.34 ms | 46.26 ms |
|
||||
| 画面观感 | 卷帘、翻页 | 完整清晰,无撕裂 | 完整清晰,无撕裂 |
|
||||
|
||||
## 结论
|
||||
|
||||
1. **卷帘问题不是 RDP 协议的限制,是当前 RDP 客户端实现路径的问题。**
|
||||
Moonlight 在同一台盒子、同一台云桌面、同一网络下画面完整清晰,无卷帘无撕裂,
|
||||
抓屏确认 1080p 画面文字锐利、任务栏时间正确。
|
||||
|
||||
2. **Moonlight 的瓶颈在服务端,不在终端,也不<E4B99F><E4B88D><EFBFBD>网络。**
|
||||
1080p30 时网络丢包 0.00%、延迟 2 ms、渲染 3.34 ms、终端 CPU 26%,
|
||||
但实际帧率仅 12.83 FPS;1080p60 时主机处理延迟升到平均 38.2 ms、渲染 46.26 ms。
|
||||
服务端当前使用 libx264 软件编码(VM 内无 NVIDIA 直通,`nvidia-smi` 不存在),
|
||||
4 核 CPU 同时承担 1080p 编码与桌面捕获,是帧率上限的真正原因。
|
||||
|
||||
3. **RDP 路径的 CPU 代价约为 Moonlight 的 4 倍**(111% vs 26%),
|
||||
因为 RDP 走 `FreeRDP GDI -> CPU 内存复制 -> QImage/QPixmap -> QLabel -> Xorg`,
|
||||
而 Moonlight 只做一次解码和一次窗口呈现。
|
||||
|
||||
4. **RK3528 VPU 硬解在当前 moonlight-qt 6.2.0 下不可用。**
|
||||
`h264_v4l2m2m` / `hevc_v4l2m2m` / `hevc_v4l2request` 均报
|
||||
`Could not find a valid device`,即使盒子存在 /dev/mpp_service、/dev/rga 和
|
||||
/dev/dri/renderD128。需 Rockchip MPP FFmpeg 补丁或 V4L2 request 驱动。
|
||||
|
||||
## 后续建议
|
||||
|
||||
优先级从高到低:
|
||||
|
||||
1. **服务端 GPU 直通**:将宿主机 GTX 965M(PCI 01:00.0,IOMMU Group 2 独占)
|
||||
直通给 VM 100,使 Sunshine 可用 NVENC 硬编码。预期把 1080p 帧率从 13 FPS
|
||||
提升到 60 FPS,同时释放服务端 CPU。需先确认内核 `intel_iommu=on`、
|
||||
`vfio-pci` 已绑定,以及该笔记本 GPU 的 vBIOS ROM 提取。
|
||||
2. **消除帧率随请求升高而抖动**(60 FPS 时抖动丢帧 15.28%):服务端稳定后复测。
|
||||
3. **RK3528 硬解适配**:解决 V4L2 request/m2m 设备缺失,这是达成 4K 的前提。
|
||||
4. **协议选型建议**:
|
||||
- 若 GPU 直通可行,Moonlight 在 CAD/3D/视频场景优于 RDP,建议按产品方案采用双协议:
|
||||
普通办公用 RDP,GPU/CAD/3D 用 Moonlight;
|
||||
- 同时保留并优化 RDP 路径的 Weston + MPP/VPU + DMABUF/DRM 零拷贝改造,
|
||||
使 RDP 在办公场景达到可接受体验。
|
||||
5. 当前 Moonlight 未接 VPU,1080p 软件解码仅 26% CPU 但 4K 会超出能力,
|
||||
需在 4K 目标确定后优先解决硬解。
|
||||
|
||||
## 附:本次变更
|
||||
|
||||
- VM 100:安装 Virtual Display Driver(MttVDD),提供 1920×1080@60Hz 虚拟显示器。
|
||||
- VM 100:安装 Sunshine v2026.914.233613,Web UI 账号 admin / vdi12345。
|
||||
- 终端:安装 moonlight-qt 6.2.0,已与 Sunshine 完成配对。
|
||||
- 终端:kiosk-shell 自启动已恢复(~/.config/autostart/vdi-kiosk.desktop)。
|
||||
@ -1,168 +0,0 @@
|
||||
# Windows 云桌面渲染优化执行记录(2026-11-02)
|
||||
|
||||
## 执行顺序
|
||||
|
||||
1. 复核 GPU、实际编码器、客户机解码器、显示刷新率,保存原始证据。
|
||||
2. 用持续动画做同步参数 A/B;排除静态桌面的按需发帧影响。
|
||||
3. 在独立目录接通 MPP、RKMPP、DRM PRIME,先验证真实视频解码,再验证 Moonlight 串流。
|
||||
4. 只有真实串流通过后才启用新客户端,保留系统 Moonlight 的回退入口。
|
||||
5. GPU/NVENC 恢复后再做 1080p60、1440p30/60 和输入到屏幕延迟验收。
|
||||
|
||||
## 当前 GPU 证据
|
||||
|
||||
本轮经 Guest Agent 重新采集:GTX 965M 的 ConfigManagerErrorCode=43,NVML 查询失败,
|
||||
DXGI 没有枚举到 NVIDIA 适配器,Sunshine 的最新实际编码器为 `libx264`。配置已是
|
||||
`sw_preset=ultrafast`,当前 Windows 虚拟屏为 2560x1440@30。
|
||||
|
||||
没有再次复位设备、重载 VFIO、替换 ROM 或重启宿主机。本轮未改变其他 VM。
|
||||
仍没有证据确认唯一故障原因;匹配此板卡的生产版 vBIOS 或另一块可稳定直通的 GPU
|
||||
是下一轮服务端验证的候选条件,而非已完成修复。
|
||||
|
||||
验收工具更新为 `tools/validate-sunshine-nvenc.ps1` + `tools/probe-nvidia-d3d.cs`:
|
||||
实际创建 NVIDIA D3D11 设备、检查 PnP 错误码、检查最新一次实际编码器创建及时间,
|
||||
并核验 NVENC 会话与编码利用率。不会把启动阶段的编码器探测记录当作实际串流。
|
||||
|
||||
## 同步参数 A/B
|
||||
|
||||
环境:系统 Moonlight 6.2.0,H.264 软件解码,2560x1440@20、30Mbps,输出约60Hz,
|
||||
MATE compositor 已关闭,SDL OpenGL ES 2。Windows 用
|
||||
`tools/stream-motion-test.html` 持续移动条纹与棋盘;源 RAF 约30 FPS。
|
||||
125% DPI 下页面显示逻辑2048x1152,视频流和 HDMI 仍是物理2560x1440。
|
||||
|
||||
下表来自两段完整会话结束日志,包含启动/退出阶段,时长不同;属于现场初步对照,
|
||||
不构成严格统计学基准。原始日志与叠图保存在 `evidence/moonlight-20261102/`。
|
||||
|
||||
| 指标 | smooth | low-latency |
|
||||
|---|---:|---:|
|
||||
| 接收/解码 FPS | 17.16 | 17.07 |
|
||||
| 渲染 FPS | 17.07 | 17.01 |
|
||||
| 主机处理平均 ms | 33.6 | 35.4 |
|
||||
| 网络丢帧 | 0.00% | 0.00% |
|
||||
| jitter 分类丢帧 | 0.44% | 0.29% |
|
||||
| 网络延迟平均 ms | 11 | 11 |
|
||||
| 解码平均 ms | 14.07 | 16.96 |
|
||||
| 帧队列平均 ms | 0.95 | 1.14 |
|
||||
| 呈现平均 ms(含同步) | 8.38 | 10.87 |
|
||||
|
||||
系统客户端软件解码的对照中,关闭同步没有改善帧率或排队,因此软件基线保留 smooth。上述耗时
|
||||
不能直接相加当作端到端输入延迟;后续须用高速摄像/外部触发测量 P50/P95。
|
||||
|
||||
日志中的启动测试 EGLImage 导出失败和单次队列溢出,不能单独证明持续运行时故障。
|
||||
最终选中的系统路径是软件视频帧上传后的 SDL 呈现;`Using DRM renderer` 也不等于
|
||||
已经使用 VPU 或直接扫描输出。
|
||||
|
||||
## RKMPP 已验证部分
|
||||
|
||||
- 为 `/dev/mpp_service`、`/dev/rga`、`/dev/dma_heap/*` 增加 video 组读写权限;
|
||||
持久化规则为 `terminal/kiosk-shell/etc/70-vdi-rockchip-video.rules`。
|
||||
- 普通用户执行 MPP H.264 初始化,create/init/destroy 全部返回0。
|
||||
- 从固定提交构建 MPP 和 FFmpeg-Rockchip 6.1,目录是
|
||||
`/home/armbian/vdi-build/rkmpp-experiment`;依赖只下载解包,不安装或升级系统包。
|
||||
- 用 `generate-h264-sample.c` 生成60帧2560x1440 H.264样本。`h264_rkmpp` 全部解出,0错误。
|
||||
- CPU NV12输出耗时2.620秒;`-hwaccel rkmpp -hwaccel_output_format drm_prime` 输出耗时
|
||||
0.290秒,CPU时间0.120秒,日志明确为 DRM PRIME 帧,0解码错误。
|
||||
|
||||
样本纹理简单且测试期间有编译负载;这些数字仅证明硬解/帧导出可工作,不能替代
|
||||
Moonlight 真实串流、显示呈现、复杂内容或2K60验收。
|
||||
|
||||
## 产品接入点
|
||||
|
||||
`VDI_STREAM_STATS=1` 独立于 profile,可公平比较两档。解码器覆盖只接受
|
||||
software/hardware/auto;RKMPP通过 `H264_DECODER_HINT=h264_rkmpp` 选择,不把
|
||||
`h264_rkmpp` 当作 Moonlight `--video-decoder` 的值。
|
||||
|
||||
`VDI_MOONLIGHT_PROGRAM` 可指定独立客户端启动器。`vdi-kiosk-start` 支持读取
|
||||
`~/.config/vdi-terminal/render.conf` 的 allowlist 字段,不执行配置文件:
|
||||
|
||||
```ini
|
||||
profile=smooth
|
||||
stats=0
|
||||
program=/home/armbian/vdi-terminal/bin/vdi-moonlight-rkmpp
|
||||
decoder=hardware
|
||||
codec=H.264
|
||||
```
|
||||
|
||||
文件不存在时沿用系统客户端。上述为 smooth 档示例;本机实际部署档位见下表后说明。
|
||||
|
||||
## 构建与页面接入修正
|
||||
|
||||
私有客户端固定为 Moonlight v6.2.0 `de2467e433821664cdd2224aad8c89a625be1ad9`,
|
||||
MPP `7916c3cc000fefc0bdf96636b7d54fe174724f44`,FFmpeg-Rockchip 6.1
|
||||
`797387de1c9caa9170100d7217ee093d1897f750`。复现入口为
|
||||
`tools/build-rkmpp-client.sh`,仅在盒子的用户目录下载、解包和构建依赖。
|
||||
|
||||
- Qt 5 构建需要显式为上游 `drm.h` 补入 `<optional>`,脚本幂等应用。
|
||||
- 补齐 EGL 开发依赖,重新执行 qmake 功能探测,并在缺少 HAVE_EGL 时终止构建。
|
||||
- qmake 改变功能开关不会自动使旧对象失效;构建脚本记录编译/链接参数指纹,
|
||||
参数变化时清理应用对象,防止“探测有 EGL、实际对象无 EGL”。
|
||||
- RKMPP 使用独立测试解码器,上游成功路径不输出旧的 `Test decode successful`,
|
||||
而输出 `Not reusing test decoder for h264_rkmpp`。页面就绪解析同时兼容两者,
|
||||
仍要求实际 renderer 已选中,并在连接失败时拒绝进入就绪状态。
|
||||
- 私有启动器设置 QML、Qt 插件和共享库目录,系统 Moonlight 不被覆盖。
|
||||
|
||||
第一轮真实串流已输出 2560x1440 的 `h264_rkmpp / drm_prime`,但显示选中 SDL,
|
||||
并出现 `Selected hwframe->swframe ... (mapping: yes)`,因此该轮不作为零拷贝验收。
|
||||
对应证据为 `rkmpp-stale-sdl.txt` 与 `rkmpp-first.png`,包含构建负载和启动阶段。
|
||||
|
||||
另已核查用户注册表:`MouseTrails=0`、`DragFullWindows=1`,不通过添加第二个光标
|
||||
或重新设置透明光标来掩盖视频延迟。
|
||||
|
||||
## 后续顺序与验收要求
|
||||
|
||||
1. 先验证硬解实际串流与 EGL 呈现,记录持续动画下的接收、解码、呈现 FPS,
|
||||
解码/排队/呈现耗时及丢帧;没有通过则保留系统软件客户端默认入口。
|
||||
2. 通过后接入页面启动链,验证过场层退出、快捷键回列表、再次连接及持久配置。
|
||||
保留2K20现有配置作为基线,只有实测更好才提高请求帧率。
|
||||
3. 服务端 GPU 修复需匹配板卡的生产 ROM 或可稳定直通的替代设备,并重复通过
|
||||
PnP、NVML、DXGI/D3D11、真实 NVENC 以及重启后的验收。
|
||||
4. 服务端恢复后按1080p60、1440p30、1440p60逐档验证,并同步虚拟屏刷新率。
|
||||
1440p60还需要将当前30Hz虚拟屏调整到60Hz,不能只改客户端FPS参数。
|
||||
5. 用高速摄像或外部触发测输入到显示的P50/P95,另外观察快速拖窗与光标残影。
|
||||
如延迟仍主要来自光标随视频传输,再评估服务端光标位置/形状侧通道与客户端
|
||||
单光标显示的二次开发,需同时保证隐藏视频中的旧光标,不能直接叠加本地光标。
|
||||
|
||||
客户端应优先接通硬解与显示路径;镜像内安装补帧/运动插值插件不能修复 GPU Code 43,
|
||||
也不能代替真实输入响应。当前不把插帧作为低延迟方案。
|
||||
|
||||
## 真实串流对照与现场选择
|
||||
|
||||
清理旧对象后,日志明确选择 `Renderer 'EGL/GLES' with 'DRM' backend chosen`,
|
||||
实际解码尺寸2560x1440、`h264_rkmpp`、`drm_prime`,呈现使用 EGLImage。
|
||||
未见实际运行路径的 hwframe→swframe 映射回退。四组均为持续动画、30Mbps、
|
||||
源RAF30FPS、终端HDMI约60Hz、服务端libx264;均为约2分钟会话,统计包含启动/退出。
|
||||
会话按顺序进行,网络与服务端负载会波动,不能当作严格控制变量的实验室基准。
|
||||
|
||||
| 客户端/请求档位 | 接收/解码 FPS | 呈现 FPS | 主机平均 ms | 解码 ms | 队列 ms | 呈现 ms | 网络/jitter丢帧 |
|
||||
|---|---:|---:|---:|---:|---:|---:|---|
|
||||
| 系统软件解码 / 2K20 smooth(先前基线) | 17.16 | 17.07 | 33.6 | 14.07 | 0.95 | 8.38 | 0% / 0.44% |
|
||||
| RKMPP+EGL / 2K20 smooth | 19.14 | 18.97 | 38.4 | 10.53 | 2.78 | 2.54 | 0% / 0.20% |
|
||||
| RKMPP+EGL / 2K30 smooth | 24.21 | 23.29 | 46.2 | 8.24 | 31.95 | 4.19 | 0% / 3.02% |
|
||||
| RKMPP+EGL / 2K30 low-latency | 23.56 | 23.46 | 46.8 | 9.11 | 2.39 | 5.41 | 0% / 0.10% |
|
||||
| RKMPP+EGL / 2K30 VSync开、Frame Pacing关 | 23.26 | 22.63 | 47.5 | 8.28 | 28.93 | 4.14 | 0% / 2.23% |
|
||||
|
||||
对应原始统计分别为 `rkmpp-egl-20-smooth.txt`、`rkmpp-egl-30-smooth.txt`、
|
||||
`rkmpp-egl-30-lowlatency.txt`、`rkmpp-egl-30-vsync-no-pacing.txt`,均在证据目录中。
|
||||
|
||||
本机选择2K30、`profile=low-latency`、RKMPP硬解、H.264,常态关闭叠图。
|
||||
这是在当前设备上的取舍:相较2K30 smooth,平均帧队列少约30ms;关闭VSync存在
|
||||
撕裂风险,需要现场动态观察。保留2K20 smooth作为平稳显示的回退档,不将其删除。
|
||||
截图可见单个白色远程光标,但静态截图不能证明快速移动没有残影或输入延迟为零。
|
||||
|
||||
当前会话平均呈现仍只有约23FPS,未达到请求的30FPS;服务端GPU故障和libx264路径
|
||||
仍未修复。不能将这一轮称为2K60、NVENC恢复或“鼠标无延迟”验收通过。
|
||||
|
||||
## 正式入口部署验证
|
||||
|
||||
已运行 `tools/deploy_kiosk.sh` 同步、编译、CTest、安装和重启盒子的kiosk受管栈;
|
||||
CTest 2/2通过。render.conf选择上述私有硬解客户端,桌面卡请求帧率持久保存为30,
|
||||
原文件备份在盒子 `~/.config/vdi-terminal/desktops.before-rkmpp-20261102.json`。
|
||||
|
||||
两次从实际列表页点击Windows连接,日志分别在约10.1秒、9.7秒进入stream_ready,
|
||||
均明确选中 `EGL/GLES + DRM`、`h264_rkmpp`,过场层退出后画面可见。
|
||||
`Ctrl+Alt+Shift+Q` 正常退出code=0并回列表,再次连接通过;没有遗留第二条串流。
|
||||
配置已经写入现有自启链,但本轮未重启盒子、VM或宿主机;不声称完成重启后验收。
|
||||
|
||||
截图 `kiosk-rkmpp-session.png`、`kiosk-rkmpp-disconnect.png`、
|
||||
`kiosk-rkmpp-reconnect.png` 保留了产品入口验证。单白光标可见,未修改原光标策略。
|
||||
自动化拖窗未形成可计量的动态验收结果,仍需现场鼠标操作与外部摄像测量。
|
||||
专用Edge测试浏览器已关闭,临时VDIRenderBench任务已删除,未关闭其他浏览器。
|
||||
55
docs/roadmap.md
Normal file
@ -0,0 +1,55 @@
|
||||
# 未来计划
|
||||
|
||||
更新日期:2026-11-09。本文是后续工作的统一清单;列入计划不表示本次已实施。
|
||||
|
||||
<a id="deferred"></a>
|
||||
## 用户明确暂不实施的事项
|
||||
|
||||
| 事项 | 后续范围 | 当前状态 |
|
||||
| --- | --- | --- |
|
||||
| 云桌面内上网代理 / VPN | 登录首页提供 Clash/Mihomo 订阅导入/更新、节点选择、启停与连通性测试;平台向 Windows、Ubuntu、麒麟下发策略,让桌面内浏览器和应用访问 GPT 等外部服务;串流、管理与必要内网流量保留直连/专用路由 | 仅规划,未实现、未部署;不是单纯把 Moonlight 服务器地址换成 VPN 地址 |
|
||||
| 自有软件白名单与商业合规 | 按客户/桌面管理允许的软件和版本、策略下发、审计与回滚;Windows/麒麟拟使用签名插件或代理组件,Ubuntu 拟在系统层实施;定版前核对系统接口、授权、权限模型、更新与例外机制 | 仅规划,未实现、未部署;不能直接把某种底层修改一概认定为合法或违法 |
|
||||
| 盒子品牌安全画面 | 公司 Logo 全屏兜底、隐藏桌面内容,覆盖开机、断流、异常退出及重启场景 | 品牌替换尚未实施。11-09 已另行修复独占会话、常驻列表和进入小窗,不能继续把这些已做的修复写为“全部未实现” |
|
||||
|
||||
以上三项保持后续计划,不因文档合并启动开发或部署。
|
||||
|
||||
## 当前体验的剩余验收
|
||||
|
||||
- 启动、连接、退出、断流、客户端崩溃、kiosk/LightDM 重启及异常错误路径,补物理屏幕录像或 HDMI 逐帧采集;现有 X11 采样不能覆盖所有物理闪帧。
|
||||
- Windows/Ubuntu/麒麟从选择页到串流的系统快捷键逐项验收;Windows Win+Shift+S 需验证客体截图功能,不能只确认宿主没弹窗。
|
||||
- 客体电源菜单外观、重启后的持久策略、新账户继承与权限边界;不把当前用户策略误报为所有镜像/用户已覆盖。
|
||||
- 原生光标长期稳定、真实失焦、多种缩放/锁屏、显示模式变化和异常恢复;Windows 自动形状同步仍未产品化。
|
||||
- USB 多品牌、大文件、插拔、安全删除菜单、断网/断电数据一致性和 VM/PVE 重启恢复;异常 VM 退出仍需 QEMU 原因证据。
|
||||
- 多型号显示器、长期运行、网络抖动和温度/功耗基准;开机全过程及“xxx 失败”类提示需要按具体日志定位,不能依据一次不再出现就定论已解决。
|
||||
- 终端音频出音与真实业务场景的音视频体验仍需补充现场验收,不能由串流连接成功推断完成。
|
||||
|
||||
<a id="performance"></a>
|
||||
## 性能路线
|
||||
|
||||
1. 优先评估 Linux 可独占、同时承担显示与编码的 GPU,或能打通低拷贝捕获/编码的虚拟 GPU 路径。11-06 单纯直通 Intel 核显的方案已失败回退,不能直接重复当作既定部署方案。
|
||||
2. 保持 2K 的同时按 hover/click/drag/输入分别测 P50/P95,记录实际编码器与收流 FPS;必须用同一负载比较,尾延迟变差则回退。没有真实硬编时评估“2K 画质优先/1080p 低延迟”两档,但当前不自动降分辨率。
|
||||
3. Weston/Wayland/DRM 正式显示与隔离路径继续研究,验证稳定 2K60 和硬解/显示开销;当前 Windows 2K45 与 Linux 请求 FPS 不等于稳定 60FPS。
|
||||
|
||||
## 产品化与云管平台
|
||||
|
||||
保留原定“云—管—端”边界:终端通过管理平台获取身份、授权桌面和短期连接信息;管理平台统一操作 Proxmox,终端不直接管理 VM。
|
||||
|
||||
具体功能讨论另见[云端服务与 Web 控制台功能设计](cloud-console-functional-design-20261109.md)(讨论稿),不与已完成的终端实施混为一谈。
|
||||
|
||||
11-09 已开始实现第一阶段:Go API、Vue 控制台、PostgreSQL、登录/RBAC、桌面/终端/用户授权与任务审计可本机联调,PVE client 已建立。同日已用独立数据库/Token 接通现场 PVE 与 Qt,三系统串流与远程会话重置通过。后续是独立服务器部署、Rust Agent/MQTT、设备生命周期和产品化;并非七个独立服务已完成。启动与测试见[云管开发说明](cloud-platform-development.md)。
|
||||
|
||||
| 阶段 | 范围与完成条件 |
|
||||
| --- | --- |
|
||||
| 认证与连接闭环 | 正式终端注册、配置下发、用户/设备绑定、桌面列表、一次性票据、错误与重连;替换当前 SN/本地 JSON POC |
|
||||
| Web 管理 MVP | Vue3/TypeScript/Element Plus;登录、super_admin/operator/viewer、终端/桌面/用户绑定、命令和审计;页面 API 适配层支持 Mock 到真实服务迁移 |
|
||||
| 云端服务 | Go、PostgreSQL、VictoriaMetrics、HTTPS/MQTT;认证/RBAC、PVE 适配、桌面池与资源生命周期 |
|
||||
| 终端管理 | vdt-agent 注册/心跳/命令/日志、配置版本、签名 OTA/A-B 回滚、USB 会话授权与多租户隔离;已有看门狗和 USB POC 继续复用 |
|
||||
| 交付与运维 | 日志归档、长稳/恢复测试、版本清单、镜像与离线部署流程;监控页面在 vdi-monitor 独立维护 |
|
||||
|
||||
管理端开关机与客户桌面电源按钮限制职责不同;盒子物理开关联动 VM 生命周期需要明确授权、断线/断电语义与数据保存机制,尚未实现。
|
||||
|
||||
管理命令应返回 `commandId`,展示“已提交/执行中/成功或失败”,不能用 HTTP 200 代替设备执行结果。正式 API 字段、资源池与协议策略需在实施阶段核对;10-28 控制台草案保留在[历史归档](archive/README.md),原方案中的旧双协议设想不代表当前默认主线。
|
||||
|
||||
## 已完成事项不再重复排期
|
||||
|
||||
三系统正常接入、Windows NVENC、RKMPP 硬解、2K 配置、基础 USB 真实透传、图形电源策略及 11-09 进入/退出交接已经有实施记录。后续计划应写剩余验收或产品化边界,不再笼统写“未接入 Linux”“未打通硬解”“尚未开始 USB”。详情见[当前状态](current-status.md)。
|
||||
@ -1,35 +0,0 @@
|
||||
# Windows仅保留2K主屏(2026-11-03)
|
||||
|
||||
用户条件:只有在不影响当前2K屏幕使用时才调整。已完成并读回:Windows仅一个活动桌面输出,保留Virtual Display Driver主屏2560×1440@60,盒子原Moonlight进程46934继续2K45/RKMPP/EGL,截图正常。GTX965M错误码0,Sunshine布局变化后仍创建h264_nvenc,最终编码占用10%。没有重启Windows/盒子、禁用显卡设备、卸载驱动或修改PVE/GPU直通。
|
||||
|
||||
## 调整前后
|
||||
|
||||
| 输出 | 原布局 | 现布局 |
|
||||
| --- | --- | --- |
|
||||
| DISPLAY4 / Virtual Display Driver | 主屏,2560×1440@60,坐标0,0 | 保留原模式和主屏身份,唯一活动输出 |
|
||||
| DISPLAY1 / Microsoft Basic Display Adapter | 扩展屏,2560×1440@64,坐标-1024,1440 | 从Windows桌面布局停用 |
|
||||
| DISPLAY2 / NVIDIA GTX965M直接输出 | 原本未激活 | 保持未激活;GPU本身仍正常用于硬件编码 |
|
||||
|
||||
这里的“停用”仅指显示输出不参与Windows扩展桌面。设备管理器仍保留显示适配器,Windows设置也可能列出未连接的输出,不能将此操作说成卸载了多余显卡或移除了所有显示设备条目。
|
||||
|
||||
本轮VDD编号已是DISPLAY4,早先DISPLAY3记录不能用于写死设备号。工具按唯一活动VDD动态选择,并在修改前强制核对它已是2560×1440@60主屏,不满足条件不修改。
|
||||
|
||||
## 实现与验证
|
||||
|
||||
新增`tools/set-single-vdi-display.ps1`,Query只查询;Apply先保存设备身份、模式、主屏、布局为XML,使用ChangeDisplaySettingsEx逐项暂存,最后一次提交停用其他活动输出。申请为当前用户保存布局;不添加持续守护或额外登录任务。读回若不是唯一VDD主屏或模式改变,立即尝试恢复本次原布局。已有备份拒绝覆盖。
|
||||
|
||||
新增`tools/run-single-vdi-display.py`,通过已有VM100代理,在当前已登录用户会话运行一次性任务。任务已删除,最终`probe_task_present=False`。初次Query编译因缺少System.Xml引用失败,未执行显示修改;补齐引用后查询、应用、独立再次查询均成功。
|
||||
|
||||
当前状态已经验证;未通过重启/重新登录检验持久性,以避免中断用户使用。原2K登录脚本仍动态选择VDD,未改分辨率策略。盒子原配置、显示守护和串流参数未修改。
|
||||
|
||||
复核时误使用了会持续运行的`nvidia-smi encodersessions`,读取超时后已按进程及父命令精确停止本次监视子进程,再以一次性查询确认GPU健康;没有终止Sunshine或用户应用。Sunshine日志中的16:57音频端点错误早于本次显示调整,不是本次产生的新故障。
|
||||
|
||||
## 回退和证据
|
||||
|
||||
Windows原布局备份:`C:\ProgramData\VDI\single-display-20261103\before.xml`。
|
||||
|
||||
如需恢复,使用`python tools/run-single-vdi-display.py Restore`。恢复前核对显示设备名称、ID和注册表Key,身份变更时拒绝套用旧布局;本轮没有为演示回退再次打断当前单屏状态。
|
||||
|
||||
证据目录`docs/evidence/single-display-20261103/`包含before、apply、独立after-query、显卡/编码健康查询及实际2560×1440桌面截图。
|
||||
|
||||
API依据:[Microsoft ChangeDisplaySettingsEx](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-changedisplaysettingsexw)。
|
||||
@ -1,74 +0,0 @@
|
||||
# Sunshine 显卡环境配置指引(转给操作 VM 的同事)
|
||||
|
||||
## 背景
|
||||
|
||||
我们在 VM 100(win10-test,192.168.1.70)里装了 Sunshine 用于 Moonlight 串流验证。当前卡在:VM 没有可用 GPU,Windows 只有「Microsoft 基本显示适配器」,分辨率 1024×768、刷新率 1Hz,Sunshine 无法按 30/60fps 捕获编码。
|
||||
|
||||
不需要动 Proxmox,只需要在 Windows 里完成两件事:装虚拟显示器驱动 + 确认 Sunshine 配置。
|
||||
|
||||
## 第 1 步:安装虚拟显示器驱动(IddSampleDriver)
|
||||
|
||||
让 Windows 出现一个虚拟显卡,可自定义分辨率和刷新率:
|
||||
|
||||
1. 下载虚拟显示驱动包(ParrotGeek 的 IddSampleDriver,或等效的「Virtual Display Driver」)。
|
||||
常用地址:https://github.com/itsmikethetech/Virtual-Display-Driver/releases
|
||||
(如果 GitHub 打不开,用镜像:把链接前面加 https://gh-proxy.com/ )
|
||||
2. 解压后右键 `IddSampleDriver.inf` →「安装」。
|
||||
3. 重启 Windows。
|
||||
4. 装好后设备管理器会出现「IddSampleDriver Device」,显示设置里会多出一个虚拟显示器。
|
||||
5. 把虚拟显示器设置为:
|
||||
- 分辨率:1920×1080(先用 1080p,稳定后再试 2K)
|
||||
- 刷新率:60Hz(关键!当前 1Hz 是主要问题)
|
||||
- 如果看到多显示器,把虚拟屏设为「扩展这些显示器」, Sunshine 默认抓主屏;也可以只保留虚拟屏。
|
||||
|
||||
## 第 2 步:确认 NVIDIA 独显是否可用(如果宿主已直通/有驱动)
|
||||
|
||||
打开 PowerShell 执行:
|
||||
|
||||
```powershell
|
||||
nvidia-smi
|
||||
```
|
||||
|
||||
- 如果能输出显卡信息(GTX 965M):很好,说明 VM 内能用 NVIDIA。
|
||||
- 请安装 GeForce 驱动(如未装),然后告知我们,我们把 Sunshine 切到 NVENC 硬编码。
|
||||
- 如果提示找不到命令或没有 NVIDIA 设备:跳过此步,走纯软件编码 + 虚拟显示器路线(也够验证流畅度)。
|
||||
|
||||
## 第 3 步:检查 Sunshine 服务状态
|
||||
|
||||
Sunshine 已装在 `C:\Program Files\Sunshine\`,服务名 `SunshineService`。
|
||||
|
||||
PowerShell 执行:
|
||||
|
||||
```powershell
|
||||
Get-Service SunshineService
|
||||
Restart-Service SunshineService
|
||||
```
|
||||
|
||||
重启服务后再看日志确认识别到新显示器:
|
||||
|
||||
```powershell
|
||||
Get-Content "C:\Program Files\Sunshine\config\sunshine.log" -Tail 60
|
||||
```
|
||||
|
||||
需要看到的关键行(类似):
|
||||
|
||||
```
|
||||
Desktop resolution [1920x1080]
|
||||
Display refresh rate [60 Hz]
|
||||
```
|
||||
|
||||
## 第 4 步:告知我们结果
|
||||
|
||||
只需回三条信息:
|
||||
|
||||
1. 虚拟显示器是否装好,分辨率/刷新率是多少;
|
||||
2. `nvidia-smi` 是否有输出;
|
||||
3. sunshine.log 最后几十行里的 Desktop resolution / Display refresh rate 数值。
|
||||
|
||||
我们这边会立刻重新发起 Moonlight 串流测试并采集性能数据。
|
||||
|
||||
## 备注
|
||||
|
||||
- Web UI(可选):https://192.168.1.70:47990 账号 admin / 密码 vdi12345
|
||||
- 装驱动需要重启 VM,重启后 SunshineService 会自启,无需其他操作。
|
||||
- Sunshine 的串流端口 47984/47989/47990/48010 已确认开放,防火墙无需改动。
|
||||
77
docs/terminal-guide.md
Normal file
@ -0,0 +1,77 @@
|
||||
# 终端说明
|
||||
|
||||
基线日期:2026-11-09。当前是 Armbian/Xorg POC,正式目标仍为 Weston/Wayland。
|
||||
|
||||
同日已部署 Qt HTTPS 平台适配:现场存在 `~/.config/vdi-terminal/platform.json` 时,设备认证、桌面列表和一次性连接票据由真实平台提供,原 `desktops.json` 不再作为授权来源。启动、现场验收和回退见[真实云管接入](cloud-live-integration.md)。Qt 编译依赖新增 Network 模块,编码/显示配置沿用接入前版本。
|
||||
|
||||
<a id="session"></a>
|
||||
## 开机与进出云桌面
|
||||
|
||||
产品入口由 LightDM 管理 `vdi-kiosk` 独占会话,再运行 `vdi-kiosk-start → vdi-kiosk-monitor → kiosk-shell`。开机直接进入“选择云桌面”,不再通过普通 MATE 桌面或旧登录页中转。Moonlight 只在选择桌面后启动,不能与 kiosk 分别设置开机自启。
|
||||
|
||||
主要配置/实现入口:
|
||||
|
||||
- [LightDM 配置](../terminal/kiosk-shell/etc/92-vdi-kiosk.conf)、[会话定义](../terminal/kiosk-shell/vdi-kiosk-session.desktop)、[启动脚本](../terminal/kiosk-shell/scripts/vdi-kiosk-start)。
|
||||
- `~/.config/vdi-terminal/desktops.json` 保存各桌面地址、应用名、分辨率和 FPS;基础配置在 `client.conf`。`moonlight.conf` 属于旧独立调试入口,不代表产品连接参数。
|
||||
- 当前常见状态流为 `BOOT → DESKTOP_LIST → CONNECTING → SESSION → DESKTOP_LIST`;首配、认证、重连和错误分支仍保留在状态机中。
|
||||
|
||||
11-09 的交接修复包括:
|
||||
|
||||
1. 串流期间保持全屏列表窗口映射在 Moonlight 下方,预先完成绘制;Moonlight 自己处理退出快捷键并销毁串流窗口时,下面已经是产品页面。
|
||||
2. 串流期间释放 kiosk 键盘抓取,几何回调不抢焦点、不抬高列表;页面切换后再显示窗口。
|
||||
3. 连接遮罩使用不透明 X11 override-redirect 窗口,无合成器时直接隐藏,不依赖透明淡出。
|
||||
4. [Moonlight 0007](../terminal/moonlight/patches/0007-vdi-kiosk-launch-surface.patch) 由子进程环境 `VDI_KIOSK_STREAM=1` 启用:CLI Qt 启动/错误界面不映射,隐藏根窗口同步加载 QML,SDL 创建时即按输出位置与尺寸无边框全屏;失败通过非零退出码交给 kiosk 提示。独立 Moonlight GUI 不受此环境外的逻辑影响。
|
||||
5. 部署采用二进制原子替换,由 LightDM 管理会话。停止会话主进程后从 SSH 脱离启动会使 LightDM 回到系统 greeter,不能用作正常恢复方式。
|
||||
|
||||
当日回归:进入阶段 Windows/Ubuntu/麒麟分别采样 819/908/968 次,启动 Qt 小窗和非全屏违规均为 0;后续返回采样 525/527/510 次,暴露违规为 0。另有此前两轮返回共 3121 个有效采样。证据见 [entry-surface](evidence/entry-surface-20261109/) 与 [return-surface](evidence/return-surface-20261109/)。这些是 X11 状态与截图验证,未做外置物理逐帧采集。
|
||||
|
||||
<a id="shortcuts"></a>
|
||||
## 快捷键边界
|
||||
|
||||
用户要求从选择页到三种云桌面均不能通过快捷键唤起盒子系统内容。`MoonlightSession` 已使用 `--capture-system-keys always`,将 Win/Super、Alt+Tab 等系统组合键交给客体;无 MATE 会话也减少了宿主桌面快捷键入口。
|
||||
|
||||
- `Ctrl+Alt+Shift+Q`:结束当前串流并返回产品桌面列表,属于保留的退出入口。
|
||||
- `Ctrl+Alt+Shift+M`:kiosk 管理菜单入口,和客户云桌面内的快捷键职责不同。
|
||||
- `Win+Shift+S`:目标是在 Windows 内截屏;能否弹出还取决于客体截图工具/组件及键盘传递,不能把“没有盒子弹窗”当成“截图已成功”。
|
||||
- [0004 电源键策略](../terminal/moonlight/patches/0004-vdi-power-key-policy.patch) 属于客户端输入策略;客体菜单关机限制由独立系统策略实施,详见[电源策略](desktop-guide.md#power)。历史现场构建与构建器补丁集合曾不同,核验二进制来源后再判断覆盖范围。
|
||||
|
||||
验收应同时覆盖选择页、连接中、串流中、退出后,逐一检查 Windows/Ubuntu/麒麟的系统键和截图快捷键。不能以一个系统通过推断另外两个通过。
|
||||
|
||||
<a id="display"></a>
|
||||
## 显示器关开与 2K 恢复
|
||||
|
||||
当前盒子使用 `2560x1440_60_fixed297`,已验证时序:
|
||||
|
||||
```text
|
||||
297.00 2560 2608 2640 2720 1440 1443 1448 1820 +HSync -VSync
|
||||
```
|
||||
|
||||
[vdi-display-guard](../terminal/kiosk-shell/scripts/vdi-display-guard) 每 2 秒检查输出,异常持续至少 2 秒后恢复;健康状态只查询,断开时等待,失败按 2~60 秒退避。该盒子 vendor PHY 与显示器原厂 241.70MHz 时序存在兼容问题,不能直接换回原厂时序。
|
||||
|
||||
11-03 模拟两次 1920×1440 回退均自动恢复,用户实际关开显示器也确认恢复全屏;会话 PID 保持不变。当前验证局限于这台盒子、单 HDMI 显示器与 Xorg,不覆盖任意显示器或 Wayland。模式确实回退时驱动与守护切换可能需要约 15 秒。
|
||||
|
||||
仅部署显示守护入口为 [tools/deploy_display_guard.sh](../tools/deploy_display_guard.sh),状态查询见[运维说明](operations.md#diagnosis),证据见 [display-resume](evidence/display-resume-20261103/)。
|
||||
|
||||
<a id="build"></a>
|
||||
## 构建与版本回退
|
||||
|
||||
在具备 Qt5 开发依赖的 ARM64 环境编译 kiosk:
|
||||
|
||||
```bash
|
||||
cd terminal/kiosk-shell
|
||||
cmake -G Ninja -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON
|
||||
cmake --build build
|
||||
QT_QPA_PLATFORM=offscreen ctest --test-dir build --output-on-failure
|
||||
```
|
||||
|
||||
完整终端部署入口是 [tools/deploy_kiosk.sh](../tools/deploy_kiosk.sh)(开发机 Git Bash);它会重启 LightDM、打断盒子当前串流。私有硬解客户端由 [tools/build-rkmpp-client.sh](../tools/build-rkmpp-client.sh) 在盒子构建。补丁范围、未采用实验和版本来源见 [Moonlight 模块说明](../terminal/moonlight/README.md)。以上命令是维护入口,本次文档整理未执行部署。
|
||||
|
||||
11-09 已验证 Moonlight SHA256:
|
||||
|
||||
```text
|
||||
2e7441df1f6af5c4ddc79b89af15eed3b6b63cf265bea294ffc090483d8c6c51
|
||||
```
|
||||
|
||||
旧二进制和 Linux 光标配置备份在盒子 `~/vdi-terminal/backups/return-surface-20261109/`。回退必须同时匹配 Moonlight 二进制与两台 Linux 原生光标 profile 中的客户端 SHA,不能只换二进制。显示守护旧文件备份为 `~/vdi-terminal/backups/display-guard-20261103-095920/`,恢复旧布局也会恢复原先的显示回退风险。
|
||||
|
||||
当日 CTest 3/3、原生光标边界测试 4/4 通过;完整实验过程及较早版本校验值见[归档](archive/README.md)。
|
||||
@ -1,20 +0,0 @@
|
||||
# Ubuntu 交互响应深度调优(2026-11-05)
|
||||
|
||||
本轮只改 Ubuntu VM102。保留 2560×1440、原生 Ubuntu 光标、30Mbps H.264 和 RKMPP/EGL 客户端;Windows、麒麟和 NVIDIA 直通没有改动。
|
||||
|
||||
现场启用了 KMS 捕获与原生独立光标的组合:Sunshine 使用 `/dev/dri/card1` 的 virtio_gpu atomic primary scanout,客户端继续通过已验证的原生光标通道同步形状、热点和位置。KMS 守护校验唯一 primary plane、`CRTC_W/CRTC_H=2560×1440`、驱动名 `virtio_gpu`,不请求 cursor hotspot plane,因此不会把第二个光标编码进视频。virtio atomic 客户端的 legacy `crtc_id/fb_id` 保持为 0,守护不再把这两个无效字段当作活动状态;真实捕获由 Sunshine 自己完成。
|
||||
|
||||
对比结果(60Hz XTest 注入,读解码视频中的唯一编号;不含物理 USB、网络之外的面板扫描,也不等于本地箭头延迟):
|
||||
|
||||
| 场景 | KMS 45FPS | X11 45FPS |
|
||||
| --- | ---: | ---: |
|
||||
| 长按框选回显 P50 / P95 | 195.9 / 246.6ms | 250.1 / 311.5ms |
|
||||
| 视频中可见新编号更新 | 16.0/s | 10.6/s |
|
||||
| 实际按钮点击回显 P50 / P95 | 约 304 / 378ms | — |
|
||||
| masked 输入字符回显 P50 / P95 | 约 165 / 182ms | — |
|
||||
|
||||
28 次点击的编号、位置和释放均通过;7 种原生光标形状/显隐的几何和像素最大差为 0,视频背景没有混入指针。KMS 与原生光标的正常退出、空闲后重连、守护进程被强制结束后的 X11 恢复、再次重连均通过。证据在 `docs/evidence/ubuntu-deep-latency-20261105/`,包括 `drag-kms-native45.json`、`drag-x11-native45.json`、`response-kms-async.json`、`cursor-kms-native45.json` 和 `kms-lifecycle.json`。
|
||||
|
||||
45FPS 请求只带来有限收益;软件编码 `min_threads=4` 的回显中位数约 197ms、尾部变差,已撤回。失败的 VirGL 硬编码和异步通知实验也没有留在运行环境。最终当前会话保持 KMS + 原生光标;Sunshine 配置仍是软件编码,未宣称已达到 Windows 的约 90ms 或“零延迟”。真实业务应用的后端认证没有写入测试数据;本轮只对实际 GTK 按钮、masked entry 和合成提交做了定量,未把合成提交冒充真实登录成功。
|
||||
|
||||
当前剩余瓶颈是 VM102 的 X11/virtio 读回和宿主 CPU 软件编码。要进一步接近 Windows,需要可用的 Linux 硬件编码/低开销捕获路径或更高性能 GPU;不能靠继续加快本地箭头、盲目提高 FPS 或修改鼠标主题解决。
|
||||
@ -1,60 +0,0 @@
|
||||
# Ubuntu 框选与桌面渲染调优(2026-11-05)
|
||||
|
||||
本轮仅处理 Ubuntu VM102。保留系统原生光标、GNOME/Xorg、2560×1440、30Mbps、limited 色彩范围和现有受限独立光标通道。用户已明确允许正常关闭启动、对比测试及必要回退;现场共正常关闭启动三次,没有强制停止虚拟机。Windows、麒麟及其独占 GPU 配置未修改。
|
||||
|
||||
## 最终保留
|
||||
|
||||
- VM102 的 CPU 从 `x86-64-v2-AES` 改为 `host`,仍为4核/6144MB。Sunshine 实际日志确认 x264 使用 AVX/FMA3/BMI2/AVX2。
|
||||
- 默认 std/bochs 虚拟显卡改为 `virtio-gl`。Xorg 从 `Refusing to try glamor on llvmpipe`、DRISWRAST,变为 `glamor X acceleration enabled on virgl (Mesa Intel(R) HD Graphics 4600 (HSW GT2))`,客体出现 renderD128。
|
||||
- PVE 新增 GL/EGL/Mesa 运行依赖:0升级、10新增、0移除。宿主 i915 保持原驱动;未做核显 VFIO 直通。
|
||||
- 最终请求30FPS、默认 `min_threads=2`(配置中不显式覆盖)、ultrafast/zerolatency。45FPS及4线程试验未继续降低回显延迟,已撤回。
|
||||
- 盒子客户端二进制、两个 Linux 原生光标 profile、Ubuntu 主题及鼠标加速策略未改。客户端 SHA256 仍为 `5d4330f703d7f8e053bc3795ca42bdaf231c9e9dd8ebb00a612013adbcccee03`。
|
||||
|
||||
这是共享 GPU **桌面渲染**,不是硬件视频编码。Sunshine 仍为 X11/XCB 捕获+libx264 软件编码,不能以客体出现 renderD128 宣称已启用 Quick Sync。
|
||||
|
||||
## 测量和结果
|
||||
|
||||
原始数据见 [ubuntu-drag-20261105](evidence/ubuntu-drag-20261105/)。临时 GTK 全屏窗口在长按移动时绘制框选区域和唯一编号;盒子以60Hz注入300次运动,从实际解码视频读取编号。下表的延迟是合成运动到首次观察对应编号,**不含物理USB和面板扫描,也不是本地指针延迟**。
|
||||
|
||||
| 配置/证据 | 新编号视频更新/秒 | 回显 P50 / P95(ms) | 最长观察间隔(ms) |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| 原CPU+原显卡,30FPS:baseline.json | 2.0 | 398.3 / 634.6 | 1833.4 |
|
||||
| host+VirGL,30FPS:accelerated30.json | 11.8 | 215.7 / 249.1 | 179.3 |
|
||||
| host+VirGL,45FPS:accelerated45.json | 13.2 | 208.2 / 258.4 | 140.9 |
|
||||
| 仅host+原显卡,45FPS:hostcpu45.json | 2.8 | 433.3 / 545.6 | 668.2 |
|
||||
| host+VirGL,45FPS、4线程:threads4-45.json | 11.6 | 211.2 / 259.3 | 199.6 |
|
||||
| 最终host+VirGL,30FPS、默认线程:final.json | 12.4 | 224.4 / 308.3 | 176.9 |
|
||||
|
||||
最终这轮中位回显延迟较基线降低约44%,P95降低约51%。所有有效轮次均观察到最终编号300和按钮释放。样本较短、参数组合与运行时间不同,没有做随机重复或统计显著性检验;不能将改进百分比分解归因给某个单项,也不能直接预测真实应用中的每次框选。
|
||||
|
||||
“新编号视频更新”不等于 Moonlight 总帧率,视频可能含重复画面;GNOME/GDK会合并运动事件,未逐一绘制300次运动不等于丢失300次输入。有效会话全程统计中,VirGL/30FPS 收流约29.25FPS、主机处理平均52.4ms;45FPS请求约30.56FPS、62.7ms;45FPS/4线程约33.46FPS、65.5ms。不同会话工作负载和时长不同,这些数据仅用于判断升帧/线程没有达到预期,不是严格单项基准。
|
||||
|
||||
额外对照:去掉盒子持续截图采样,原配置5秒仍只产生16条客体motion记录;VirGL下直接在客体注入,串流时67条、退出串流后159条。说明截图探针不是唯一原因,捕获/渲染竞争仍存在。GTK事件到绘制回调通常不足数毫秒,但回调结束不代表合成、GPU提交和视频编码已结束,不能据此排除渲染瓶颈。
|
||||
|
||||
## 回归与现有限制
|
||||
|
||||
最终16次唯一编号点击全部回显,另外两次快速点击使序号从16增到18;源日志18次按下/18次释放,前16次最大坐标误差1.375px,见 input-verification.json。仅代表该稳定测试会话,不宣称任意高速操作绝不丢失。
|
||||
|
||||
最终画质测试实际解码尺寸2560×1440;12级灰阶解码为0、8、16、24、31、64、128、192、223、235、247、254,没有新增明显截断。文字/单像素条纹截图见 quality-final.png。仍为H.264 4:2:0,未宣称像素无损。
|
||||
|
||||
所有临时GTK测试服务已停止,留在正常Ubuntu桌面。最终状态见 final-guest-state.txt、final-box-state.txt、desktop-final.png。盒子桌面JSON与本轮备份结构完全一致,45FPS试验已撤回;共享客户端SHA核对未变。
|
||||
|
||||
另在真实GNOME桌面空白区域执行长按框选,确认橙色选择框显示、松开后消失,见 real-desktop-selection.png、desktop-after-drag.png。这是功能和最终画面检查,没有将其当作真实桌面的定量时延基准。
|
||||
|
||||
**尚未达到接近原生或定量等同Windows的目标。** 本轮没有对Windows执行相同仪器测试,也没有测真实GNOME桌面框选的光子延迟、物理鼠标或长期运行。当前改善集中于客体渲染;X11捕获、GPU读回和CPU编码仍限制动态响应。进一步目标是验证具有可用硬件编码能力的Linux GPU路径和更低开销的捕获方式,需单独设计与验证,不能仅提高请求FPS或改鼠标速度。
|
||||
|
||||
首次探针实际落在锁屏界面,黑色区域误满足“编号0”条件,未纳入有效数据;立即释放按钮并解锁。探针已加固定8bit签名及按下后的finally释放。CPU-only轮第一次因串流尚未显示测试窗口而被签名检查拒绝,待就绪后复测,拒绝轮未纳入表格。
|
||||
|
||||
## 备份和回退
|
||||
|
||||
PVE首次备份 `/root/vdi-ubuntu-render-20261105/102.conf.before`;客体首次备份 `/home/test2/vdi-terminal/backups/ubuntu-render-20261105/`;盒子首次备份 `/home/armbian/vdi-terminal/backups/ubuntu-render-20261105/desktops.json`。本地保存 vm-config-before.json、vm-config-final.txt、宿主安装日志和各轮原始记录。
|
||||
|
||||
回退需先保存工作、退出Ubuntu串流并正常关闭VM102,只将CPU恢复 `x86-64-v2-AES`、删除新增vga字段,再启动。不要整份覆盖VM配置,避免抹去后续磁盘/网络等修改。最终Sunshine配置与原配置一致,桌面JSON也已恢复;新增图形库无需为了回退显卡而卸载。若要卸载,应先检查是否有其他使用者,不能盲目自动清理宿主依赖。
|
||||
|
||||
技术依据:[QEMU VirtIO GPU](https://www.qemu.org/docs/master/system/devices/virtio/virtio-gpu.html)、[Sunshine捕获/编码与线程配置](https://docs.lizardbyte.dev/projects/sunshine/latest/md_docs_2configuration.html)、[现场固定提交X11捕获实现](https://github.com/LizardByte/Sunshine/blob/63d35f702ee9e362e43263742981836ec0710384/src/platform/linux/x11grab.cpp)。实际PVE源代码的virtio-gl依赖检查和QEMU启动参数也已现场核对。
|
||||
|
||||
## 后续:锁屏Sunshine通知清理
|
||||
|
||||
用户圈出的多组Sunshine卡片实际是 `Stream Started` / `Stream Paused` 通知。GNOME对无法关联桌面应用的通知按进程分组,历次串流服务重启使分组累积。当前检查的4条通知均为Sunshine,已通过GNOME通知面板清除,复核0条可见通知;未清理其他应用的数据。
|
||||
|
||||
Ubuntu配置新增 `system_tray = disabled`,正常退出/从桌面列表重连后,新Sunshine进程日志确认该配置,CLIENT CONNECTED、Sunshine和guard均正常。这同时关闭Sunshine托盘图标及其状态提示,保留串流服务,不改GNOME全局通知和锁屏策略。配置首次备份 `~/vdi-terminal/backups/sunshine-notifications-20261105/sunshine.conf`;要恢复只需删除新增system_tray行,下次正常重连生效。上文“最终Sunshine配置与原配置一致”指本附加操作之前的渲染调优验收时点。
|
||||
@ -1,54 +0,0 @@
|
||||
# Ubuntu 点击与悬停延迟新方案(2026-11-06)
|
||||
|
||||
## 结论
|
||||
|
||||
当前延迟的关键不是鼠标速度,也不是盒子渲染。VM102 仍由 Sunshine 的 `libx264` 软件编码输出,盒子端解码、队列和渲染约为 12ms、0.2ms 和 2ms;已测得 Ubuntu KMS 45FPS 的 hover 回显 P50/P95 约 196/220ms,按钮点击约 304/378ms。要接近 Windows,必须优先改变 Ubuntu 端的“真实硬件编码或捕获到编码的零拷贝路径”。
|
||||
|
||||
Sunshine 官方文档明确指出:Linux 的 X11 捕获 CPU 开销最高,应尽量使用 KMS;硬件编码可使用 VA-API、QuickSync 或 NVENC,KMS 与 VA-API 兼容。当前已保留 KMS + 原生光标组合,但编码仍是软件路径。[Sunshine 配置文档](https://docs.lizardbyte.dev/projects/sunshine/latest/md_docs_2configuration.html)
|
||||
|
||||
## 新计划
|
||||
|
||||
### 1. 先做只读硬件可行性检查
|
||||
|
||||
检查 PVE 宿主的 Intel `00:02.0` 是否支持稳定的 VFIO 直通、是否有可用的 i915/mdev 能力,以及宿主是否依赖这块核显提供 PVE 或其他虚拟机图形服务。检查 Ubuntu 内核、`/dev/dri` 权限、`vainfo` 和 H.264 编码入口。检查只读完成前不改 VM、不重启宿主。
|
||||
|
||||
当前 PCI 盘点显示:Intel 4 代核显在 `00:02.0`,NVIDIA GTX 965M 在 `01:00.0` 且已分配给 Windows VM100,因此不能挪用 NVIDIA;Ubuntu 的 `virtio-gl` 只提供 VirGL 图形翻译,不等于真实硬件编码。[QEMU VirtIO GPU 文档](https://www.qemu.org/docs/master/system/devices/virtio/virtio-gpu.html)
|
||||
|
||||
已完成的只读预检显示 Ubuntu 当前只暴露 `virtio_gpu_dri.so`,没有 `i965`/`iHD` 编码驱动;`i965-va-driver`、`vainfo` 等包在 Ubuntu 软件源中可用,但安装软件包本身不能让 VirGL 变成真实 Intel 编码设备。预检快照见 [pve-readonly-preflight.json](evidence/ubuntu-latency-plan-20261106/pve-readonly-preflight.json)。
|
||||
|
||||
### 2. 首选路径:给 Ubuntu 使用真实 Intel 编码设备
|
||||
|
||||
如果只读检查确认宿主可以安全释放 Intel 核显,则建立 VM102 的备份,安排宿主维护窗口,把 Intel 核显以 VFIO 直通方式交给 Ubuntu,客体安装 Haswell 对应的 VA-API 驱动,先用独立 `vainfo` 和 FFmpeg 编码测试,再让 Sunshine 只使用 `encoder=vaapi`、`capture=kms`。Haswell 属于 `intel-vaapi-driver` 支持的 H.264 编码平台,但必须看到真实 i915 设备和 `VAEntrypointEncSlice`;VirGL 暴露的伪 VAAPI 已经在现场触发过 Mesa/virgl 崩溃,不能继续沿用。[Intel VA-API 驱动支持表](https://github.com/intel/intel-vaapi-driver/blob/master/README)
|
||||
|
||||
验收条件是 Sunshine 日志稳定出现 `Creating encoder [h264_vaapi]`,持续 10 分钟无崩溃、无回退到 `libx264`,并且 2560×1440 的 hover/click/drag 测试延迟显著下降。失败时只恢复 VM 配置和 Ubuntu 驱动,不碰 Windows VM100、麒麟 VM101 或盒子客户端。
|
||||
|
||||
如果宿主不能释放核显,第二选择是增加一块可被 Ubuntu 独占的低功耗硬件编码 GPU。仅升级 VirGL、增加虚拟显存或安装 Mesa 软件包不能替代这一步。
|
||||
|
||||
### 3. 无法获得真实 GPU 时的保底方案
|
||||
|
||||
保留当前 `capture=kms`、`host` CPU、`virtio-gl`、H.264 和原生光标,只做有基准的两项实验:
|
||||
|
||||
1. 评估 QEMU `virtio-gpu` 的 host memory/blob 配置,目标是减少桌面渲染读回;这只可能改善捕获前的渲染,不能把软件编码变成硬件编码。
|
||||
2. 在宿主只有 4 个物理核的前提下,禁止盲目增加 VM vCPU 或继续堆 x264 线程;每次实验必须用同一套 2K 动态窗口和真实 GNOME 点击/拖拽数据验证,P95 变差就回退。
|
||||
|
||||
如果没有真实硬件编码,2K 下无法承诺达到 Windows 的约 90ms。此时应提供两个明确 profile:2K 画质优先,以及 1080p/更高帧率低延迟;由使用场景选择,而不是继续隐藏软件编码瓶颈。
|
||||
|
||||
## 验收与回退
|
||||
|
||||
- 同一盒子、同一网络、同一 60Hz 输入注入,分别记录 hover、button click、masked input、长按框选的 P50/P95。
|
||||
- 同时记录 Sunshine 实际编码器、收流 FPS、主机处理延迟、网络 RTT、盒子解码/渲染耗时。
|
||||
- 只有出现真实 `h264_vaapi`/`h264_qsv` 且延迟和尾部都改善,才保留新路径;出现崩溃、软件回退、画面损坏或 P95 变差立即恢复备份。
|
||||
- 全部测试结束后恢复 Ubuntu 正常 Sunshine 服务、关闭原生光标守护租约,盒子回到桌面列表。
|
||||
|
||||
该方案的核心变化是先验证“真实硬件编码是否可用”,而不是再调鼠标主题、速度、虚拟显存或客户端队列。
|
||||
|
||||
|
||||
## 2026-11-06 直通验证结果
|
||||
|
||||
本轮按计划对 VM102 做了可回退试验:临时使用 `hostpci0=0000:00:02.0,pcie=1,rombar=0` 和 `vga=std`,Ubuntu 成功识别 Intel HD 4600 的 `i915`,`vainfo` 显示 `VAProfileH264High` 与 `VAEntrypointEncSlice`。Sunshine 也能在启动探测中识别 `h264_vaapi`。
|
||||
|
||||
但这台 VM 的桌面显示仍由虚拟 Bochs/Virtual-1 提供。启用 KMS 后 Sunshine 在查找 DRM monitor 时长时间阻塞,Moonlight 的 `launch` 请求超时;重启 Sunshine 时 Xorg/xrandr 又出现长时间卡住,Sunshine 需要强制终止。切换 X11 捕获尚未能形成稳定串流,试验因此判定失败。
|
||||
|
||||
已完成回退并重启宿主恢复 i915:VM102 当前为 `vga=virtio-gl`、无 `hostpci0`,Ubuntu Sunshine 恢复 `capture=x11`、`encoder=software`;VM100、VM101、VM102 均为运行状态,Windows 的 NVIDIA 直通和麒麟配置未修改。完整过程证据见 [intel-vaapi-trial-final.json](evidence/ubuntu-latency-plan-20261106/intel-vaapi-trial-final.json)。
|
||||
|
||||
结论:当前这台宿主上,单纯把 Intel 核显直通给 Ubuntu 不能稳定打通“虚拟显示捕获 -> VAAPI 编码”链路,不能保留为生产优化。要继续把 Ubuntu 的交互回显压到 Windows 水平,需要给 Ubuntu 独占一块能同时提供显示/编码的 GPU,或改用支持零拷贝显示输出的虚拟 GPU 方案;继续调鼠标速度、虚拟显存和客户端队列不会消除这条编码瓶颈。
|
||||
@ -1,28 +0,0 @@
|
||||
# Ubuntu 鼠标按键状态修复(2026-11-06)
|
||||
|
||||
本轮只修复盒子端 Moonlight 的输入状态,不修改 Ubuntu、麒麟或 Windows 虚拟机配置。
|
||||
|
||||
## 根因
|
||||
|
||||
绝对鼠标会话在连接中断、焦点切换或 SDL 队列丢失最后一个 `BUTTONUP` 时,客户端原来只释放键盘按键,未释放远端鼠标按钮。Ubuntu 会继续保持左键按下状态,因此窗口关闭、最大化、最小化按钮和拖拽都会表现为“点不到/卡住”。
|
||||
|
||||
## 修复
|
||||
|
||||
新增 `0006-vdi-release-stuck-mouse-buttons.patch`:
|
||||
|
||||
- 会话退出时显式发送左、中、右、X1、X2 五个鼠标按钮的释放事件;
|
||||
- SDL 窗口失焦时同样清理按钮状态;
|
||||
- 保留现有绝对坐标、KMS 捕获、原生 Ubuntu 光标和拖拽顺序处理。
|
||||
|
||||
## 验证
|
||||
|
||||
- RKMPP 客户端重新编译成功;
|
||||
- SDL 事件边界、输入优先级、绝对坐标移动→按下→移动→释放 1000 轮测试全部通过;
|
||||
- 新客户端通过原生光标握手、KMS 捕获、2560×1440 H.264 硬件解码启动;
|
||||
- 会话退出后 Ubuntu 已恢复普通 Sunshine,原生光标守护为 inactive;
|
||||
- 旧客户端 SHA256:`5d4330f703d7f8e053bc3795ca42bdaf231c9e9dd8ebb00a612013adbcccee03`;
|
||||
- 新客户端 SHA256:`a09140efb4400a63153e2063358dda6ebea166be20c5b9b882e667230b0ac61e`。
|
||||
|
||||
盒子端旧客户端和两台 Linux 原生光标配置已备份到 `~/vdi-terminal/backups/mouse-release-20261106-20261106-095251/`;如果回归出现异常,可以恢复旧二进制和旧校验值。
|
||||
|
||||
这次修复针对“按键状态残留”导致的点击/拖拽失效。画面编码和网络造成的帧延迟仍由 KMS/virtio 读回与 Ubuntu 端 libx264 软件编码决定。
|
||||
@ -1,51 +0,0 @@
|
||||
# Ubuntu 画质、光标样式与响应调优(2026-11-05)
|
||||
|
||||
Ubuntu 已从 1920×1080 升级为真实 **2560×1440** 桌面与视频,分辨率、30Mbps 请求码率和 Windows 对齐。最新用户要求保留原生样式,因此后续已恢复 Adwaita/24px、default 鼠标加速,并部署原生独立光标,见[实施记录](linux-native-cursor-20261105.md)。关闭 GNOME 动画保留。
|
||||
|
||||
以下保留较早的画质实验记录:当时使用 DMZ-White/flat,未部署独立光标,2K动态视频约21–23FPS。该视频数据不代表新方案的本地指针移动延迟;物理输入到屏幕延迟仍未测量,不能称已定量达到Windows。
|
||||
|
||||
## 生效配置与验证
|
||||
|
||||
- Ubuntu `.72/test2`:原 Xorg、Sunshine 2026.914.233613、X11 捕获、libx264 ultrafast/zerolatency;编码线程恢复默认,没有保留较差的单线程实验。
|
||||
- `vdi-sunshine.service` 启动前创建、添加并切换 `2560x1440_60.00`,时序 `241.50 2560 2608 2640 2720 1440 1443 1448 1481 +hsync -vsync`。已实际删除自定义模式后启动服务,验证自动重建2K成功。
|
||||
- GNOME `~/.config/monitors.xml` 保存单屏2K、100%缩放。初次通过 DisplayConfig 持久应用曾等待确认后回退,随后保存明确的显示配置并重新应用;最终锁屏/解锁测试仍为2K、`LockedHint=no`。未重启虚拟机或GDM,整机重启后的恢复尚未验收。
|
||||
- 盒子只修改 Ubuntu 卡片为 `2560×1440 / 30FPS`,以及逐主机文件 `~/.config/vdi-terminal/host-color-range/192.168.1.72` 为 `0`。Windows、麒麟卡片结构比较保持原值,见[隔离核验](evidence/ubuntu-quality-20261105/config-isolation.json)。
|
||||
- 原 RKMPP、DRM PRIME、EGL/GLES、latest-frame、无VSync/无frame-pacing 保留。正常 kiosk 连接的父进程、参数、range 环境和真实2K硬解已核对,见[最终连接](evidence/ubuntu-quality-20261105/final-stream.json)、[客户端](evidence/ubuntu-quality-20261105/final-client.log)、[服务端](evidence/ubuntu-quality-20261105/final-server.log)。最终保留 Ubuntu 普通桌面连接,临时测试窗口已停止。
|
||||
|
||||
最终配置快照:[服务](evidence/ubuntu-quality-20261105/final-sunshine.service)、[Sunshine](evidence/ubuntu-quality-20261105/final-sunshine.conf)、[显示配置](evidence/ubuntu-quality-20261105/final-monitors.xml)、[系统读回](evidence/ubuntu-quality-20261105/final-ubuntu-state.txt)、[桌面截图](evidence/ubuntu-quality-20261105/final-desktop.png)。
|
||||
|
||||
## 同场景短测
|
||||
|
||||
临时 GTK 窗口显示小字号文字、单像素线、彩色文字、12级灰阶和局部运动。以下均通过正常 kiosk 入口,以真实2K视频测试。每轮24次交替点击及24次F9回显;F9测量包括 xdotool 启动、网络、客体处理、编码解码和X11像素轮询,**排除物理USB输入与显示器扫描**。
|
||||
|
||||
| 配置 | 渲染FPS | 解码ms | 队列ms | Sunshine CPU%¹ | F9 P50/P95 ms |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||
| 45FPS请求 / full / 原动画 | 21.49 | 10.48 | 0.53 | 163.1 | 373.2 / 455.9 |
|
||||
| 45FPS请求 / limited / 原动画 | 22.83 | 6.82 | 0.19 | 161.5 | 376.7 / 488.6 |
|
||||
| 45FPS请求 / limited / 单线程 / 无动画(撤回单线程) | 17.98 | 8.24 | 0.14 | 126.9 | 402.9 / 507.9 |
|
||||
| 30FPS请求 / limited / 默认线程 / 无动画(保留) | 21.40 | 6.84 | 0.26 | 156.0 | 320.9 / 418.4 |
|
||||
|
||||
¹ CPU按 `/proc/<pid>/stat` 差值测量,100%为一个逻辑核。原始客户端日志、CPU样本、输入事件及回显样本按表中配置保存在[证据目录](evidence/ubuntu-quality-20261105/),数值汇总见[comparison.json](evidence/ubuntu-quality-20261105/comparison.json)。
|
||||
|
||||
limited 同参数仅小幅提高视频帧率,回显没有改善;30FPS最终配置的合成回显较低,但多项设置和网络条件不同,不能归因于单一参数或称为显著降低物理延迟。单线程方案较差,已恢复默认策略。官方对 [min_threads](https://docs.lizardbyte.dev/projects/sunshine/master/md_docs_2configuration.html) 的说明也要求以实际目标和硬件测试选择线程数。
|
||||
|
||||
测试源本身在 GNOME 软件图形环境下未达到60次/秒绘制,累计绘制约15–20次/秒;上表是 Moonlight 视频帧率,包含可能重复的源画面,不能当作独立运动帧数。输入事件复核时发现,四轮各24次快速点击分别记录到24、24、23、21次,部分出现在输入通道启动阶段,原因未完全确认;单线程轮次还出现相邻点击落在同一处,不能称快速点击完整通过。第一轮F8在源窗口获得焦点前发出,未计为接收成功。
|
||||
|
||||
随后在最终已稳定运行的正常会话中,重新启动独立验证窗口,预热一次点击,以40ms按下保持和250ms间隔发送24次交替点击,**24/24接收、两处坐标交替正确、最大误差不足1px,F8接收**。见[稳定会话结果](evidence/ubuntu-quality-20261105/final-input-check.json)及[事件](evidence/ubuntu-quality-20261105/final-input-source.log)。该轮不证明高速点击或物理延迟。
|
||||
|
||||
四轮共96个F9翻转回显样本均完成、无超时,但其中一轮客体记录了多于注入次数的F9按键事件;探针只观察黑白翻转,没有逐键序号,不能证明严格的一键一回显。因此表格中的F9数据仅供探索,不能用于证明优化降低了输入时延。
|
||||
|
||||
limited 灰阶中心采样约为 `0,8,16,24,31,64,128,192,223,235,247,254`,源为 `0,8,16,24,32,64,128,192,223,235,247,255`,没有把limited数据直接当full显示而造成大范围灰雾或截断。见[采样](evidence/ubuntu-quality-20261105/gray-levels.json)、[full截图](evidence/ubuntu-quality-20261105/2k45-full-normal.png)、[limited截图](evidence/ubuntu-quality-20261105/2k45-limited-normal.png)。单像素和彩色文字仍受H.264 4:2:0压缩限制,不能宣称像素无损。
|
||||
|
||||
## 边界、备份和回退
|
||||
|
||||
Ubuntu 只有QEMU虚拟VGA、`/dev/dri/card0`,没有可用GPU渲染节点,Sunshine实际使用CPU编码;动态场景还观察到GNOME合成和Xorg开销。盒子解码队列已很短,继续提升到Windows水平需要处理客体图形/编码路径;本轮没有部署独立光标协议,也没有叠加假本地箭头。
|
||||
|
||||
本轮未修改PVE显卡、VM硬件、Windows配置、麒麟配置或客户端二进制。测试补装小型 `python3-gi-cairo` 包用于临时绘图,没有整体系统升级。早期独立测试会话受kiosk前景覆盖、分辨率恢复和绘图依赖缺失影响,没有纳入表格;最终有效轮次使用正常入口与实际截图核对。
|
||||
|
||||
备份均为首次备份:
|
||||
|
||||
- Ubuntu `/home/test2/vdi-terminal/backups/ubuntu-quality-20261105/`:原 Sunshine配置、用户服务、四项gsettings原值;`monitors.xml.absent` 表示此前没有显示配置文件。
|
||||
- 盒子 `/home/armbian/vdi-terminal/backups/ubuntu-quality-20261105/desktops.json`;Ubuntu逐主机range文件此前不存在。
|
||||
|
||||
回退时先退出Ubuntu串流,恢复Ubuntu服务/配置及四项gsettings,按备份存在标记恢复或移除 monitors.xml,然后 `systemctl --user daemon-reload` 并重启 `vdi-sunshine.service`。盒子仅恢复备份中的Ubuntu对象,删除 `.72` 的range文件并在无串流时刷新kiosk;不要整文件覆盖后续其他桌面的修改。原服务会将显示设回1080p。
|
||||
@ -1,16 +0,0 @@
|
||||
# Ubuntu 虚拟显存 A/B(2026-11-06)
|
||||
|
||||
本轮只测试 Ubuntu VM102。先备份了 PVE 配置,然后正常关闭并启动虚拟机,将 `virtio-gl` 的显存从默认值临时改为 64MB,再用同一套 2560×1440、45FPS、30Mbps、H.264、RKMPP/EGL、`VDI latest-frame queue` 和 GTK 动态窗口做对照。测试结束后已恢复原配置并再次正常启动。Windows、麒麟和盒子客户端二进制没有改动。
|
||||
|
||||
## 结果
|
||||
|
||||
| 配置 | 收流帧率 | 主机处理延迟平均值 | 解码 | 客户端渲染 |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| `virtio-gl,memory=64`(临时) | 27.49 FPS | 83.4 ms | 11.89 ms | 2.09 ms |
|
||||
| `virtio-gl`(恢复默认) | 32.64 FPS | 60.8 ms | 12.60 ms | 1.97 ms |
|
||||
|
||||
在这次同负载短样本中,64MB 没有改善动态画面,收流帧率下降、主机处理延迟上升,因此没有保留该参数。当前 PVE 配置仍为 `vga: virtio-gl`,Ubuntu 服务为 `vdi-sunshine.service=active`,原生光标守护在空闲时为 `inactive`。
|
||||
|
||||
盒子端解码、帧队列和渲染耗时仍只有约 12ms、0.2ms 和 2ms;剩余点击后画面延迟主要来自 Ubuntu 虚拟机内的 KMS/virtio 读回与 Sunshine 的 libx264 软件编码。继续调高虚拟显存或客户端鼠标速度不能解决这段链路。要接近 Windows 的约 90ms,需要 Linux 可用的硬件视频编码或更低开销的捕获路径。
|
||||
|
||||
原始统计:[vram64-stats.txt](evidence/ubuntu-vram-20261106/vram64-stats.txt)、[vramdefault-stats.txt](evidence/ubuntu-vram-20261106/vramdefault-stats.txt)。
|
||||
@ -1,95 +0,0 @@
|
||||
# 任意 U 盘透传实现(2026-11-04)
|
||||
|
||||
2026-11-05 补充:空的 usb-redir 端点移除已验证,但用户照片显示安全删除菜单还列出
|
||||
显卡、VirtIO 网卡/串口和 SATA 控制器。仅移除 USB 端点不能保证托盘图标消失。
|
||||
固定 PCI 设备属性修复准备与实际验收范围见 `fixed-pci-tray-20261105.md`。
|
||||
该修复随后已在用户授权正常关机再开机后应用:四项 PCI 设备能力从 6 变为 0,
|
||||
设备状态、GPU、系统盘、网络和 Sunshine 正常;真实 U 盘插拔与托盘外观待现场确认。
|
||||
|
||||
在已验证的真实 USB 透传链路上,将盒子端策略从固定 VID/PID/序列号扩展为
|
||||
“任意纯 USB Mass Storage 设备”。键盘、鼠标、Hub 和显示设备仍由盒子本地使用。
|
||||
|
||||
## 行为
|
||||
|
||||
- 一个盒子、一个 VM 同时只导出一个存储设备;多个 U 盘同时插入时按盒子扫描顺序使用,
|
||||
当前设备拔出后再接入下一个。
|
||||
- 设备必须是纯存储设备,所有 USB interface class 都必须为 `08`;键鼠、Hub 和复合设备拒绝。
|
||||
- 盒子检查分区是否本地挂载、是否有 holder 或作为 swap;发现本地占用时等待,不强制卸载。
|
||||
- QEMU 使用 `0x08:-1:-1:-1:1` 过滤器,允许任意 USB Mass Storage,仍拒绝其他 USB 类别。
|
||||
- PVE QMP watcher 在 VM/PVE 重启后重新建立运行时 `usb-redir` 设备。
|
||||
- 盒子同时提供仅回环的存储就绪状态端口。PVE watcher 只在状态为 `ready` 时创建
|
||||
`usb-redir`,连续 3 次确认状态为空后删除端点;因此物理 U 盘拔出后,Windows
|
||||
的安全删除硬件图标也会消失。插拔瞬间使用防抖,避免短暂枚举波动造成误移除。
|
||||
|
||||
## 部署入口
|
||||
|
||||
```sh
|
||||
python tools/deploy-usb-device-poc.py \
|
||||
--pve PVE_HOST --box armbian@BOX_HOST --vm 100 --any-storage
|
||||
```
|
||||
|
||||
部署前应在 Windows 中安全弹出当前 U 盘。部署器会安装盒子监控服务、USB 隧道和
|
||||
`vdi-usb-qmp-100.service`;不会修改 VM 固定配置、GPU、显示或键鼠输入链路。
|
||||
|
||||
旧的精确设备策略仍可用:
|
||||
|
||||
```sh
|
||||
python tools/deploy-usb-device-poc.py \
|
||||
--pve PVE_HOST --box armbian@BOX_HOST --vm 100 \
|
||||
--vendor 0951 --product 1666 --serial SERIAL
|
||||
```
|
||||
|
||||
## 验收
|
||||
|
||||
至少用三只不同品牌或不同 VID/PID 的 U 盘测试:插入后 Windows 出现真实
|
||||
USB Mass Storage 和盘符,读写大文件后校验 SHA-256;再测试安全弹出、拔插、
|
||||
两个 U 盘同时插入,以及 VM/PVE 重启后的自动恢复。当前 Rust 单元测试 10/10、
|
||||
Python QMP 测试 4/4 通过。已现场验证另一只 SanDisk 0781:5591 在 Windows
|
||||
识别为 USBSTOR,F: NTFS 数据分区和 G: FAT32 UQITONG_ESP 分区均 Healthy。
|
||||
多盘吞吐和 VM/PVE 重启后的恢复仍待验收;QMP watcher 已实现,但尚未现场安装。
|
||||
|
||||
## 插入体验优化
|
||||
|
||||
2026-11-04 已将当前 Windows 用户 test 的 StorageOnArrival 从 MSOpenFolder
|
||||
改为 MSPromptEachTime,并保留自动播放开启。双分区装机盘仍显示两个卷,
|
||||
不会再按“打开文件夹”这个默认操作自动打开两个窗口;提示数量和通知外观由 Windows 决定。
|
||||
|
||||
脚本 `tools/set-usb-experience.ps1` 可重复 Apply,并支持 Restore;仅修改三项
|
||||
自动播放首选项,使用首次运行保存的原值回退。Windows 已执行 Apply 并回读成功。
|
||||
备份路径:`C:\ProgramData\VDI\usb-experience-20261104\preferences.clixml`。
|
||||
|
||||
盒子监控程序使用已处理的 USB udev 事件即时唤醒,替代原来的每 2 秒轮询;
|
||||
500ms 兜底扫描负责启动时已有设备和事件监听不可用的情况。活动会话直接核对
|
||||
已导出的 sysfs 设备身份,不再因另一个 USB 设备扫描失败而误断开。
|
||||
快速插盘时须等 `bNumInterfaces` 声明的所有接口出现后才做存储类型判断,
|
||||
防止接口尚未枚举完整的存储/HID 复合设备被误接入。
|
||||
日志包含 `usb_time_ms`、`local_check_ms`、`since_latest_usb_event_ms`;最后一项
|
||||
仅为最近事件至导出调用的时间,不代表从物理插入至 Windows 完全可用的总时间。
|
||||
|
||||
盒子已离线测试并编译、部署(更新时无 U 盘连接),服务 active,udevadm 子进程正常。
|
||||
旧二进制备份在 `/home/armbian/vdi-terminal/backups/usb-events-20261104-110747/`。
|
||||
未重启 VM、Moonlight、Sunshine、Explorer,未修改启动分区和系统原生安全删除行为。
|
||||
随后实测事件至导出调用为 5~8ms(仅盒子内部时间)。
|
||||
|
||||
继续排查发现发行版 usbredirect 0.13.0 的连接回调无条件订阅 G_IO_OUT,
|
||||
现场导出进程曾占约 96% 的一个 CPU 核心。官方 0.15.0 的 update_watch() 按
|
||||
usbredirhost_has_data_to_write() 动态监听;已从官方发布包编译私有版本:
|
||||
|
||||
- 源码:https://www.spice-space.org/download/usbredir/usbredir-0.15.0.tar.xz
|
||||
- 源码 SHA256:`6dc2a380277688a068191245dac2ab7063a552999d8ac3ad8e841c10ff050961`
|
||||
- 私有程序:`/usr/local/libexec/vdi-usbredirect`
|
||||
- 二进制 SHA256:`58b58b9d9ab017d3221e44f83c0794ae2977ef9e020fcb8df959eef997ae8e4a`
|
||||
- 构建脚本:`tools/build-usbredirect-private.sh`;头文件仅下载并解包,没有安装或升级系统 GLib/libusb。
|
||||
- 盒子配置:`/etc/vdi-usb-monitor/device.env` 新增
|
||||
`VDI_USBREDIRECT_BINARY=/usr/local/libexec/vdi-usbredirect`。
|
||||
- 切换前备份:`/home/armbian/vdi-terminal/backups/usb-exporter-20261104-112031/`。
|
||||
|
||||
运行时 CPU 实测降至约 1%。官方 filter 29 项、serializer 1 项测试全部通过;
|
||||
Rust 10 项测试通过。Windows 自动播放设置已回读验证,PowerShell 语法检查通过。
|
||||
用户第一次复测仍反馈菜单慢,后续保持连接并再测后明确反馈“现在已经很快了”。
|
||||
已确认不再自动打开文件夹;右下角提示和弹出菜单速度通过本次用户现场验收,
|
||||
但没有测量从物理插入至菜单可用的完整耗时,也不代表长时间稳定性验收完成。
|
||||
|
||||
回退时先在 Windows 安全弹出并物理拔下 U 盘,再停止盒子 USB 服务,
|
||||
从上述备份恢复二进制和 device.env,启动服务即可。Windows 自动播放可用
|
||||
`C:\ProgramData\VDI\usb-experience-20261104\set-usb-experience.ps1 -Action Restore` 回退。
|
||||
@ -1,117 +0,0 @@
|
||||
# U 盘真实 USB 设备透传(2026-11-03)
|
||||
|
||||
用户明确选择 Windows 必须识别真实 USB,并配合插盘、换口和 Windows 安全弹出后实际拔插。
|
||||
当前为一个盒子、一个登记设备、一个 VM 的 POC,不是多用户生产功能。
|
||||
|
||||
## 已验证的现场结果
|
||||
|
||||
| 项目 | 结果 |
|
||||
| --- | --- |
|
||||
| U 盘 | Kingston DataTraveler 3.0,0951:1666,USB 描述符序列号 408D5C1653D4E44179789251 |
|
||||
| 盒子 | 192.168.1.188,物理路径 3-1,实际 USB2 / 480 Mb/s 链路 |
|
||||
| Windows | PVE starhe / VM100 / 192.168.1.70,USB Mass Storage 状态 OK |
|
||||
| 磁盘 | Kingston DataTraveler 3.0 USB Device,InterfaceType=USB,USBSTOR 实例含原序列号 |
|
||||
| 卷 | KINGSTON(F:),FAT32,Removable,约 57.7 GiB,Healthy |
|
||||
| 文件读写 | 1 MiB 随机文件:源文件、USB 文件、复制回 VM 的文件 SHA-256 一致 |
|
||||
| 实物拔插 | 用户先在 Windows 安全弹出,17:34:43 断开、17:34:53 重新导出,Windows F: 自动恢复 |
|
||||
| 拔插后数据 | 再次读取测试文件,SHA-256 不变 |
|
||||
| 独占 | 盒子 usbfs 持有接口;透传时 Linux 无该 U 盘的本地块设备挂载 |
|
||||
| 原有会话 | Moonlight PID46934 / 2K45 保留,显示守护 active,GTX965M 错误码 0 |
|
||||
|
||||
只新增 `F:\VDI_USB_TEST_20261103-173342\from-windows.bin` 和 `readme.txt`。
|
||||
未格式化、改分区、覆盖或删除原文件。源和回读副本在 VM 的
|
||||
`C:\Windows\TEMP\VDI_USB_TEST_20261103-173342`。
|
||||
|
||||
SHA-256:`8244F1189EFA02118BF0DC4CB68FC8924FE562AC610B68DEEFBE09EC85454D51`。
|
||||
原始验收记录见 [`evidence/usb-device-20261103/`](evidence/usb-device-20261103/)。
|
||||
|
||||
自动检查:盒子 Rust 测试 5/5、管理员 QMP 测试 3/3,通过;Python 编译检查和
|
||||
`git diff --check` 通过。现场还实测了“本地挂载时拒绝导出”,随后正常卸载才导出。
|
||||
|
||||
## 实现
|
||||
|
||||
```text
|
||||
盒子物理 USB -> Rust vdi-usb-monitor -> usbredirect/libusb(回环40001)
|
||||
-> PVE 发起的 SSH 加密隧道 -> QEMU usb-redir(现有 ehci-2.0)
|
||||
-> Windows 自带 USB Mass Storage / USBSTOR -> F:
|
||||
```
|
||||
|
||||
usbredir 独立于 SPICE;显示通道仍为 Moonlight/Sunshine。
|
||||
没有安装 Windows 第三方驱动、开测试签名、替换控制器、重启机器或改 GPU 配置。
|
||||
盒子不调用 PVE API,也不保存 PVE 管理凭据。
|
||||
|
||||
Rust 使用标准库,无第三方 crate;等待设备每 2 秒扫描,活动连接每秒检查身份与地址。
|
||||
按 VID/PID/serial 匹配唯一设备,并使用当前 bus-address 打开精确实例。
|
||||
设备消失、匹配多实例、含非存储接口、已本地挂载、存在块设备 holder 或被用作 swap 时拒绝。
|
||||
udev 规则仅对登记设备设置 UDISKS_IGNORE=1,防止本地自动挂载与 Windows 并发写盘。
|
||||
|
||||
SSH 使用独立密钥、已有可信主机密钥、源 IP 和 permitopen 限制,禁止该密钥执行 shell。
|
||||
USB 监听与隧道两端均为回环地址,未暴露 LAN USB 服务端口。
|
||||
Rust 为终端模块;Python 是管理员部署/QMP 工具;PowerShell 是 Windows 验收工具。
|
||||
|
||||
## 部署和管理
|
||||
|
||||
盒子先安装 `rustc cargo usbredirect`,在 `/home/armbian/vdi-build/usb-monitor`
|
||||
运行 `cargo test --offline`、`cargo build --release --offline`。没有整体升级系统。
|
||||
管理员机器需要 `paramiko`、可信 SSH 主机密钥,以及 PVE SSH 公钥或
|
||||
`VDI_PVE_SSH_PASSWORD` 环境变量。部署器不保存密码。
|
||||
|
||||
```sh
|
||||
python tools/deploy-usb-device-poc.py --pve 192.168.1.51 --box armbian@192.168.1.188 \
|
||||
--vm 100 --vendor 0951 --product 1666 --serial 408D5C1653D4E44179789251
|
||||
```
|
||||
|
||||
确认设备身份、关闭本地文件操作并正常卸载对应分区,然后启动盒子服务:
|
||||
`sudo systemctl start vdi-usb-monitor.service`。本次分区为 `/dev/sda1`,
|
||||
使用了 `udisksctl unmount --block-device /dev/sda1`;其他环境不能盲目照抄设备名。
|
||||
|
||||
在 PVE root 执行:
|
||||
|
||||
```sh
|
||||
python3 /usr/local/libexec/vdi-usb-device-qmp.py attach --vm 100 --vendor 0951 --product 1666
|
||||
python3 /usr/local/libexec/vdi-usb-device-qmp.py status --vm 100
|
||||
```
|
||||
|
||||
QMP 只管理 `vdi_usb_box_100` / `vdi_usb_redir_100`,不操作已有 tablet、GPU、网卡。
|
||||
重复 attach 拒绝覆盖;device_add 失败回收仅本次新建 channel。
|
||||
QEMU filter 内部分隔符为冒号,与 spice-client 的逗号不同。
|
||||
|
||||
| 位置 | 文件 |
|
||||
| --- | --- |
|
||||
| 盒子 | `/usr/local/libexec/vdi-usb-monitor` |
|
||||
| 盒子白名单 | `/etc/vdi-usb-monitor/device.env` |
|
||||
| 盒子挂载策略 | `/etc/udev/rules.d/99-vdi-usb-device-poc.rules` |
|
||||
| 盒子服务 | `/etc/systemd/system/vdi-usb-monitor.service` |
|
||||
| PVE 密钥 | `/etc/vdi-usb-poc/vm100/` |
|
||||
| PVE 隧道 | `/etc/systemd/system/vdi-usb-tunnel-100.service` |
|
||||
| 盒子原始备份 | `/home/armbian/vdi-terminal/backups/usb-device-20261103-172709/` |
|
||||
|
||||
两端服务已 active/enabled,未通过重启验证开机顺序。重新部署前须在 Windows 安全弹出,
|
||||
停止盒子导出服务;部署器拒绝覆盖运行中的服务。
|
||||
|
||||
## 限制
|
||||
|
||||
- 仅登记的这一只 U 盘;其他设备需管理员登记。没有多终端隔离、会话授权/切换或 Qt 状态提示。
|
||||
- 固定 IP、单端口、单 VM POC。QMP 设备仅存在于当前运行实例,VM/PVE 重启后须重新 attach;
|
||||
没有修改原 VM args 或 GPU 启动设置。
|
||||
- 验证了当前 FAT32 U 盘、1 MiB 读写和一次实物热插拔;未测大文件吞吐、NTFS/exFAT、
|
||||
写入中断网/掉电恢复或长期老化。
|
||||
- 最初接口报 USB 枚举 -71,换到 3-1 对应口才成功;并非软件修好了原口,也未验证 USB3 速度。
|
||||
- 使用后正常安全弹出;程序不会重试用户文件写入,不能保证强拔/断网时的数据完整性。
|
||||
|
||||
## 回退
|
||||
|
||||
1. 关闭 U 盘文件,在 Windows 正常安全弹出。
|
||||
2. PVE:`python3 /usr/local/libexec/vdi-usb-device-qmp.py detach --vm 100 --guest-ejected`。
|
||||
3. 盒子:`sudo systemctl disable --now vdi-usb-monitor.service`;PVE:
|
||||
`systemctl disable --now vdi-usb-tunnel-100.service`。
|
||||
4. 仅移除/恢复本功能 udev 规则,`udevadm control --reload-rules`,再拔插让 Linux 本地重新识别。
|
||||
5. 如移除密钥,只删 authorized_keys 中 `vdi-usb-poc-vm100` 对应公钥行;保留其他公钥,
|
||||
不直接覆盖整份可能已经更新的 authorized_keys。
|
||||
|
||||
## 官方依据
|
||||
|
||||
- [usbredir 独立协议](https://www.spice-space.org/usbredir.html)
|
||||
- [Ubuntu usbredirect](https://manpages.ubuntu.com/manpages/noble/man1/usbredirect.1.html)
|
||||
- [QEMU USB 接入](https://www.qemu.org/docs/master/system/devices/usb.html)
|
||||
- [QEMU filter 实现](https://github.com/qemu/qemu/blob/master/hw/usb/redirect.c)
|
||||
@ -1,287 +0,0 @@
|
||||
# VDI Web 管理控制台 MVP 实施方案
|
||||
|
||||
> 版本:v0.1(功能先行,UI 稿待项目经理提供)
|
||||
> 日期:2026-10-28
|
||||
> 依据:`VDI云桌面软件开发方案(1).docx` 第 2、4、5 节及 `VDI云桌面完整方案-云管端.docx` 管理平台模块表。
|
||||
|
||||
## 1. 先明确两个“登录”
|
||||
|
||||
本项目有两个不同的界面,必须分开:
|
||||
|
||||
| 界面 | 使用者 | 运行位置 | 职责 |
|
||||
|---|---|---|---|
|
||||
| 终端 kiosk-shell | 云桌面最终用户 | RK3528 盒子本地全屏 | 首配、设备绑定、用户登录、选择/进入被授权桌面、会话状态 |
|
||||
| Web 管理控制台 console | 超级管理员、运维、只读人员 | PC 浏览器 | 终端、桌面池、用户绑定、GPU、监控、OTA、审计管理 |
|
||||
|
||||
正式文档中的 `console/` 指第二项。它不应运行在盒子上,也不应直接发起 RDP/Moonlight;它通过平台 API 管理资源和下发策略。
|
||||
|
||||
“盒子开机直连 Moonlight”只能作为当前串流技术验证,不是最终产品登录流程。最终终端流程应恢复为:
|
||||
|
||||
```text
|
||||
盒子开机
|
||||
-> kiosk-shell 全屏终端登录/首配页
|
||||
-> vdt-agent 注册并获取终端身份
|
||||
-> 用户登录或终端绑定校验
|
||||
-> 平台返回该用户可用的桌面列表及短期连接信息
|
||||
-> 用户选择桌面
|
||||
-> kiosk-shell 按 protocol=rdp|moonlight 启动会话
|
||||
-> 断开后回桌面列表/登录页
|
||||
```
|
||||
|
||||
## 2. Web 控制台 MVP 目标
|
||||
|
||||
第一版目标不是做漂亮的大屏,而是跑通一个可演示、可替换 UI、可接真实后端的后台系统:
|
||||
|
||||
1. 用户能登录和退出;
|
||||
2. 根据角色控制菜单和按钮;
|
||||
3. 管理员能查看终端、桌面、用户及绑定关系;
|
||||
4. 能对终端下发命令、对桌面执行开关机/重置;
|
||||
5. 能查看基础状态、操作结果和审计记录;
|
||||
6. 后端未完成时使用 Mock API,后端接口上线后不改页面业务逻辑即可切换。
|
||||
|
||||
## 3. 技术栈与工程结构
|
||||
|
||||
按定版文档执行:Vue 3 + Vite + TypeScript 严格模式 + Element Plus。
|
||||
|
||||
建议补充:Pinia(状态)、Vue Router、Axios、Vitest、Playwright、MSW(Mock API)。
|
||||
|
||||
```text
|
||||
console/
|
||||
├─ src/
|
||||
│ ├─ api/ # OpenAPI 生成 SDK 的薄封装
|
||||
│ ├─ assets/
|
||||
│ ├─ components/ # 领域无关组件
|
||||
│ ├─ layouts/ # 登录布局、控制台布局
|
||||
│ ├─ mocks/ # MSW handlers 与演示数据
|
||||
│ ├─ router/ # 路由、鉴权守卫、RBAC meta
|
||||
│ ├─ stores/ # auth、app、notification
|
||||
│ ├─ types/ # 页面 ViewModel;API 类型由生成器管理
|
||||
│ ├─ views/
|
||||
│ │ ├─ login/
|
||||
│ │ ├─ dashboard/
|
||||
│ │ ├─ terminals/
|
||||
│ │ ├─ desktops/
|
||||
│ │ ├─ users/
|
||||
│ │ ├─ gpu/
|
||||
│ │ ├─ monitoring/
|
||||
│ │ ├─ ota/
|
||||
│ │ └─ audit/
|
||||
│ ├─ App.vue
|
||||
│ └─ main.ts
|
||||
├─ e2e/
|
||||
├─ public/
|
||||
├─ .env.development
|
||||
├─ .env.mock
|
||||
├─ package.json
|
||||
└─ vite.config.ts
|
||||
```
|
||||
|
||||
约束:页面不能直接拼 URL 或写 Axios;所有网络调用只经过 `api/`。这样 Mock 与真实后端可无感切换。
|
||||
|
||||
## 4. 页面与优先级
|
||||
|
||||
### M0:登录与应用框架
|
||||
|
||||
- 登录页:用户名、密码、登录按钮、错误状态、加载状态;
|
||||
- 控制台壳:侧边导航、顶部栏、面包屑、当前账号、退出;
|
||||
- 401 自动清理会话并回登录;
|
||||
- 403 权限提示;
|
||||
- 路由级 RBAC:`super_admin`、`operator`、`viewer`;
|
||||
- 开发环境使用 Mock 登录,生产环境只调用真实 API。
|
||||
|
||||
### M1:核心演示闭环
|
||||
|
||||
1. 仪表盘:在线终端、活动会话、运行桌面、告警数量;
|
||||
2. 终端管理:列表、搜索、状态筛选、详情、重启、重置会话;
|
||||
3. 桌面管理:列表、状态筛选、开机、关机、重启、重置;
|
||||
4. 用户与绑定:用户列表、用户详情、终端/桌面绑定与解绑;
|
||||
5. 审计日志:操作者、动作、目标、结果、时间、请求 ID。
|
||||
|
||||
M1 完成后已经可以给项目经理演示完整业务流程。
|
||||
|
||||
### M2:完整文档范围
|
||||
|
||||
- 桌面池:模板、容量、回收策略;
|
||||
- GPU 池:节点/PCI 地址/型号/状态/绑定桌面;
|
||||
- 监控:会话在线数、FPS、RTT、节点水位、告警;
|
||||
- OTA 中心:版本、批次、灰度比例、进度、失败终端;
|
||||
- CSV 用户/绑定批量导入向导;
|
||||
- 配置模板与配置版本下发。
|
||||
|
||||
## 5. 登录与权限方案
|
||||
|
||||
### 登录接口
|
||||
|
||||
```http
|
||||
POST /api/v1/auth/login
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "admin",
|
||||
"password": "******"
|
||||
}
|
||||
```
|
||||
|
||||
建议响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"accessToken": "jwt",
|
||||
"expiresIn": 3600,
|
||||
"user": {
|
||||
"id": "usr_001",
|
||||
"username": "admin",
|
||||
"displayName": "系统管理员",
|
||||
"role": "super_admin"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
建议第一版 access token 仅存内存;刷新 token 使用 `HttpOnly + Secure + SameSite` Cookie。若后端尚未支持 refresh,MVP 可暂存 `sessionStorage`,但不要长期使用 `localStorage` 保存高权限令牌。
|
||||
|
||||
### 权限矩阵
|
||||
|
||||
| 能力 | super_admin | operator | viewer |
|
||||
|---|---:|---:|---:|
|
||||
| 查看所有页面 | 是 | 是 | 是 |
|
||||
| 终端远程命令 | 是 | 是 | 否 |
|
||||
| 桌面生命周期操作 | 是 | 是 | 否 |
|
||||
| 用户/绑定修改 | 是 | 否 | 否 |
|
||||
| GPU 绑定 | 是 | 否 | 否 |
|
||||
| OTA 发布/灰度 | 是 | 是 | 否 |
|
||||
| 只读监控/审计 | 是 | 是 | 是 |
|
||||
|
||||
前端 RBAC 只是交互约束,真正授权必须由 `api-gateway` 再验证,不能只隐藏按钮。
|
||||
|
||||
## 6. 第一版 API 契约
|
||||
|
||||
文档已明确的接口优先沿用:
|
||||
|
||||
```text
|
||||
POST /api/v1/auth/login
|
||||
GET /api/v1/users
|
||||
POST /api/v1/desktop/assign
|
||||
POST /api/v1/pool/{id}/action
|
||||
GET /api/v1/gpu/cards
|
||||
POST /api/v1/gpu/bind
|
||||
GET /api/v1/monitor/query
|
||||
```
|
||||
|
||||
为控制台 MVP 补齐建议接口(需后端确认并写入 OpenAPI):
|
||||
|
||||
```text
|
||||
GET /api/v1/auth/me
|
||||
POST /api/v1/auth/logout
|
||||
GET /api/v1/terminals
|
||||
GET /api/v1/terminals/{id}
|
||||
POST /api/v1/terminals/{id}/commands
|
||||
GET /api/v1/desktops
|
||||
GET /api/v1/desktops/{id}
|
||||
POST /api/v1/desktops/{id}/actions
|
||||
GET /api/v1/bindings
|
||||
DELETE /api/v1/bindings/{id}
|
||||
GET /api/v1/audit-logs
|
||||
```
|
||||
|
||||
所有列表统一查询参数:`page`、`pageSize`、`keyword`、`status`、`sort`;统一响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [],
|
||||
"page": 1,
|
||||
"pageSize": 20,
|
||||
"total": 0,
|
||||
"requestId": "req_xxx"
|
||||
}
|
||||
```
|
||||
|
||||
所有命令型接口必须返回 `commandId`,页面展示“已提交”,再轮询或通过 WebSocket/MQTT 网关显示最终结果,不能把 HTTP 200 当作设备已经执行成功。
|
||||
|
||||
## 7. Mock 先行策略
|
||||
|
||||
后端还没完成时,前端不应硬编码页面数据。使用 MSW 模拟真实 HTTP:
|
||||
|
||||
- 固定 3 个测试账号,对应三角色;
|
||||
- 终端包含在线/离线/会话中/升级中;
|
||||
- 桌面包含运行/停止/错误/任务中;
|
||||
- 所有命令先进入 `pending`,1–3 秒后变成 `success` 或 `failed`;
|
||||
- 登录失败、401、403、网络超时、空列表都要有测试场景;
|
||||
- `VITE_API_MODE=mock|real` 切换数据源。
|
||||
|
||||
项目经理 UI 稿到后,Mock 数据仍然可用于逐页视觉验收。
|
||||
|
||||
## 8. 与终端登录流程的接口边界
|
||||
|
||||
管理员 Web 控制台不直接把 VM 密码写进终端,也不让终端调用 Proxmox API。
|
||||
|
||||
建议终端用户登录流程由 `auth-svc` 单独提供:
|
||||
|
||||
```text
|
||||
POST /api/v1/terminal/session/login
|
||||
GET /api/v1/terminal/session/desktops
|
||||
POST /api/v1/terminal/session/connect-ticket
|
||||
```
|
||||
|
||||
`connect-ticket` 返回短期、一次性连接信息:
|
||||
|
||||
```json
|
||||
{
|
||||
"desktopId": "desk_001",
|
||||
"protocol": "moonlight",
|
||||
"endpoint": "192.168.1.70",
|
||||
"app": "Desktop",
|
||||
"display": { "width": 1920, "height": 1080, "fps": 60 },
|
||||
"ticket": "short-lived-token",
|
||||
"expiresAt": "2026-10-28T16:30:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
这部分不是 `console/` 页面,但必须和 Web 控制台的“用户/终端/桌面绑定”共享同一套后端数据。
|
||||
|
||||
## 9. UI 稿到来前的页面设计原则
|
||||
|
||||
第一版只做稳定工作台,不做营销式页面:
|
||||
|
||||
- 左侧导航 + 顶部工具栏 + 主内容区;
|
||||
- 列表优先,详情使用独立页或抽屉;
|
||||
- 状态使用图标/标签,不依赖单一颜色;
|
||||
- 危险操作二次确认;
|
||||
- 所有异步操作显示提交、执行中、成功、失败四态;
|
||||
- 页面级空态、错误态、无权限态必须完整;
|
||||
- 颜色、字号、间距集中为设计 token,避免 UI 稿到后逐页改 CSS;
|
||||
- 表格列、筛选区、表单字段以配置驱动,减少视觉改版成本。
|
||||
|
||||
## 10. 验收标准
|
||||
|
||||
M1 应达到:
|
||||
|
||||
1. 三种角色都能登录,菜单/按钮权限正确;
|
||||
2. 刷新页面仍能恢复会话,令牌过期自动回登录;
|
||||
3. 终端、桌面、用户/绑定、审计五条流程可演示;
|
||||
4. 命令有执行中与最终结果,不出现“点击无反馈”;
|
||||
5. API 全走统一 SDK/适配层,无页面直连 Axios;
|
||||
6. Vitest 覆盖 auth store、路由守卫、权限判断;
|
||||
7. Playwright 覆盖登录、终端命令、桌面操作、用户绑定四条主路径;
|
||||
8. `npm run lint`、`npm run typecheck`、`npm run test`、`npm run build` 全通过。
|
||||
|
||||
## 11. 推荐实施顺序
|
||||
|
||||
```text
|
||||
第 1 步:初始化 console 工程、路由、布局、设计 token
|
||||
第 2 步:OpenAPI 初稿 + API SDK 生成 + MSW Mock
|
||||
第 3 步:登录、会话恢复、RBAC、错误处理
|
||||
第 4 步:终端管理 + 桌面管理
|
||||
第 5 步:用户与绑定 + 审计日志
|
||||
第 6 步:仪表盘聚合数据
|
||||
第 7 步:Playwright E2E 与生产构建
|
||||
第 8 步:项目经理 UI 稿到后替换视觉层
|
||||
第 9 步:接真实 Go API,关闭 Mock
|
||||
```
|
||||
|
||||
## 12. 当前决策建议
|
||||
|
||||
- 现在先开发 `console/` 的 M0 + M1,不等 UI 稿;
|
||||
- 同时为 `terminal/kiosk-shell` 恢复“登录/桌面列表后连接”的产品流程,不再把开机直连 Moonlight 当最终形态;
|
||||
- Web 控制台第一版只接 Mock,API 契约先和后端负责人确认;
|
||||
- Moonlight/RDP 统一抽象成终端连接票据的 `protocol` 字段,Web 控制台只管理策略,不直接操控串流客户端;
|
||||
- UI 稿到后只调整布局组件与 design tokens,业务路由、权限、API、测试保留。
|
||||
@ -1,35 +0,0 @@
|
||||
# Windows VM 100 异常退出调查(2026-11-04)
|
||||
|
||||
## 结论与限制
|
||||
|
||||
Windows 记录为非正常关机。PVE 侧退出清理紧随 USB 隧道重启,时间上高度相关;
|
||||
USB 数据通道断开是首要待查触发点,但未获取 QEMU 退出码、错误栈或转储,不能确认根因。
|
||||
此前将 QMP 不存在仅解释为 VM 已关机,未解释其原因;本次调查补充了异常退出证据。
|
||||
|
||||
## 证据(北京时间)
|
||||
|
||||
- 20:27:55.286424:开始停止 vdi-usb-tunnel-100.service。
|
||||
- 20:27:55.288011:旧隧道停止。
|
||||
- 20:27:55.291974:新隧道启动。
|
||||
- 20:27:55.357421:VM 100 的 tap100i0 开始被移除。
|
||||
- 20:27:55.592747:新 vdi-usb-qmp-100.service 才启动;虚拟网卡移除早于它启动。
|
||||
- 20:27:56.147457:qmeventd 开始清理 VM 100。
|
||||
- 20:31:06:root@pam 发起 qmstart,20:31:14 返回 OK。
|
||||
- PVE 任务历史未发现该次对应的 qmshutdown/qmstop 请求;这不能排除直接 shell/QMP 操作。
|
||||
- PVE 宿主机从 11 月 3 日 13:40:50 持续运行,本次没有宿主机重启。
|
||||
- Windows 本次启动记录 Kernel-Power 41、EventLog 6008;BugcheckCode=0,
|
||||
查询范围内没有 1074 正常关机发起事件、6006 正常停止或 1001 蓝屏事件。
|
||||
- Windows 6008 内的“上次关机时间”显示 20:02:15,与宿主机退出时间不同;
|
||||
此字段不作为精确退出时刻,时间线以 PVE 精确日志为准。
|
||||
- 查询的宿主机内核日志无 OOM/进程被杀/segfault 证据;常见转储位置未发现本次转储。
|
||||
- 隧道和 VM 分属 system.slice/vdi-usb-tunnel-100.service、qemu.slice/100.scope,
|
||||
隧道无 BindsTo/PartOf 指向 VM,未发现服务依赖直接带停 VM 的配置。
|
||||
|
||||
## 当前状态与后续
|
||||
|
||||
调查仅作只读采集,没有重启隧道/VM、修改 Windows 或 USB 配置,也没有在在线 VM 重现异常。
|
||||
调查结束 VM 100 正常运行;未插 U 盘时 device_present=false、channels=[]。
|
||||
|
||||
后续应在隔离测试 VM 或维护窗口,先记录 QEMU stderr、退出状态和 QMP 事件,
|
||||
再验证 usb-redir 数据连接断开、隧道重启、物理拔盘三种场景;不能直接在用户正在使用的
|
||||
VM 100 上重启隧道复现。当前“未插盘设备移除”通过不等于完整插拔稳定性通过。
|
||||