XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8

原生鼠标软件呈现节奏验收

NativePointerFrameProbe 默认关闭,由 Kernel 的 F12 raw make 0x58 开始/停止。固定 4096 帧分为 16 个结构体数组,每块约 14 KiB;正常输入和 Present 热路径不分配、不打印。只记录真正增长的 CanvasAdapter.FramesPresented 以及实际拷贝的起止时间。停止后才分块输出串口,单块不超过 16,000 个字符。

协议为 XFE_POINTER_TIMING_BEGIN|1、一条 META、若干 FRAME、XFE_POINTER_TIMING_END。META 包含实际 Present/Observed/Consumed/Lost 的起止计数、时钟频率、起止 ticks、捕获帧数、漏帧/溢出和输入范围。FRAME 包含实际 Present 序号、起止 ticks,以及最新被 UI 消费的输入序号、设备报告 ticks、分发 ticks、位置。遗漏帧、时间倒退、ring overflow、输入丢失、未来输入、错计数等不能通过解析/验收。

TestPointerCadence.py 只连接已经启动、已知 UUID 的私有 QMP,不启动或退出 QEMU,不复制系统镜像/ELF,不修改 DOM/guest 内存。独立入口先验证 UUID、正在运行的状态、精确源镜像 backing 链、镜像 SHA256 和已通过启动记录。--require-usb-mouse 要求当前活动输入设备明确是 USB,不能把 PS/2 的结果当作 USB 验收。实体鼠标兼容性仍需实体电脑测试。

python Tools/TestPointerCadence.py --qmp-port <Port> --qemu-uuid <OwnedUuid> --image <VerifiedSystem.img> --boot-result <PassedResult.json> --serial-log <OwnedSerial.log> --pointer-x <KnownNativeX> --pointer-y <KnownNativeY> --seconds 5 --input-rate 1000 --target-hz 240 --require-usb-mouse

已有 QMP 连接的专用测试可直接导入 run_pointer_cadence(qmp, read_log, screenshot, width, height, pointer, output, seconds=5, input_rate=1000, target_hz=240),避免同时连接 QMP。由原测试负责启动/关闭其自身的私有 QEMU。pointer 是当前由该测试追踪的原生坐标可变列表;screenshot 必须返回实际 QMP framebuffer 图片路径,不得提供宿主截图。

测量窗口只发送有界的真实仿真鼠标报告,不截屏、不逐帧打印。F12 停止后才取两张原生分辨率 PNG;每张的临时 PPM 在转换并关闭图片后删除。PNG 不缩放,不添加宿主光标。截图差异只表示 framebuffer 变化(可能包含时钟),光标外观仍要目视检查;实际鼠标消费与呈现使用 native FRAME 绑定独立确认。

结果明确区分 QMP 排队/ack 速率、guest 设备报告/消费、软件 framebuffer Present、实体屏幕扫描。240 Hz EDID 只是目标元数据。SoftwareCadenceGoalMet 同时要求实际 Present 和新鼠标位置更新达到目标。全窗口 Present 的频率、帧间隔和边界计算保留;新鼠标位置则使用以下独立口径:

  • 基线固定为连续输入窗口内,最早携带本窗口真实新消费报告的实际 Present。probe 初始化的 (0,0) 或旧报告不能充当基线,真实报告位于 (0,0) 则仍然有效。基线本身不计移动,也不能为挑选更快时段而后移。
  • FreshPointerPresentations 只计相对前一实际 Present 同时满足新输入序号和位置变化的真实转换。FreshPointerPresentHz 为转换次数除以“固定基线 Present 完成至末次转换 Present 完成”的实测时间;FreshPointerCadenceWindowSeconds、基线帧序号/ticks 均输出。至少两个转换才有足够节奏证据,重复绘制或无关动画不能冒充鼠标更新。
  • FreshPointerFrameIntervals 包含基线到首个真实转换的间隔,也包含之后所有转换间隔;其间重复位置、复用输入序号和停顿都不能跳过。首输入到基线的 FreshPointerFirstResponseMs 与末次转换到末输入的 FreshPointerTailGapMs 独立保留在 FreshPointerBoundaryGapsMs,分别按目标周期检查,不能用缩短稳态窗口隐藏卡顿。

例如,首个位置在输入后 2 ms 实际显示,之后每 4.1667 ms 发生真实转换,不能把基线排除后,再把首个转换相对首输入的 6.1667 ms 错算成单个移动间隔。若首响应、首个真实转换或尾部停顿超出目标周期,仍然失败。两套频率、全部帧间隔与独立边界必须同时达标,没有降低 240 的阈值。所有慢帧都保留在 p95/p99/max 与超预算帧计数中。软件达标也不等于 VSync/实体 240 Hz:NativeScanoutHzMeasured=null、PhysicalMonitorRefreshMatched=false,不会因 EDID 或截图改变。

工具另外比较真实宿主单调时钟的首个输入排队到末个输入 ack 跨度,与 guest 首末设备报告 tick / 声明 Frequency 的跨度。这个比较始终使用完整输入窗口,不能换成较短的基线子窗口。容差明确为 50 ms 输入边界/排队误差加宿主跨度的 2%;超出时 EvidenceValid=false,禁止据此声称 240。即使偏差在容差内,也按两个完整跨度中的较大值获得保守时间倍率,并将同一倍率用于全窗 Present、基线子窗口的鼠标速率、全部间隔和独立边界;此校正只能降低 FPS、增加 gap,不能把真实 238 FPS 修饰成 240。原始 guest 数值与比较比例、差值、容差都保留。工具口径修复不会改写既有实测日志或结果文件。

离线工具负例:python Tools/TestPointerCadenceGuards.py。Probe 的 host 结构/零分配/分块检查在 XFESystem.NativeInterop.Tests,均不代表已运行实体硬件。

可见 QEMU 启动器的可选 Tools/OpenCSharp.ps1 -TraceConsoleRefresh 只增加 QEMU 的 console_refresh trace,保存到该新会话的 ConsoleRefresh.log,路径记录在 Launch.json/LaunchPlan.json 的 Display.ConsoleRefreshTrace。桌面 Ready 后会保存当时的 ConsoleRefreshSummary.json;这是一份时间点观察,后续跨屏或鼠标活动可能使日志继续增长。可在不操作 VM 的情况下重新读取并另存新摘要:

Tools/CSharpBoot/ReadConsoleRefreshTrace.ps1 -TracePath <Session/ConsoleRefresh.log> -OutputPath <NewSummary.json>

该摘要只列 GUI 轮询计时器公告的配置间隔,不计算或宣称可见 FPS、物理刷新率、VSync。没有记录时保持未知;30/10/4 ms 等间隔都不能单独证明最终画面以对应频率显示。开关不会修改宿主显示模式或关闭任何既有 QEMU,辅助读取脚本也不会启动进程。