fix(playback): 帧调度只睡剩余预算,消除 ~22fps 上限#201
Merged
Merged
Conversation
run_render_thread unconditionally slept a full frame period after rendering each frame, stacking render time on top of frame_dur and capping playback around 21-23fps regardless of target fps. Now it records the tick at loop start and sleeps only frame_dur minus the elapsed render time (skipping the sleep entirely when a render overruns budget, since loop_step already re-syncs to the audio-master clock's target frame on the next iteration). Also adds a real-device acceptance probe (tests/playback_probe.rs, #[ignore]d, parameterized via OPENTAKE_PROBE_VIDEO / runtime ffmpeg fixture generation) that verifies the fix on real hardware.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
根因
run_render_thread主循环渲染完一帧后无条件thread::sleep(frame_dur),导致实际帧间隔 = 渲染耗时 + frame_dur,真机实测 ~22fps(目标 30fps)。修复
循环体开头记
let tick = Instant::now();渲染+push 完成后只睡"本帧剩余时间":渲染超时(
elapsed >= frame_dur)时完全不睡,由loop_step在下一轮追上 audio-master clock 的目标帧,语义不变(目标帧永远来自绝对时间的时钟读数,不是本地累加)。真机探针前后对比
素材:
/Volumes/mac/1111.mp4(1080p 真实素材,带音轨,cpal 主时钟驱动)。ProRes(1280x720 生成素材,2s@30fps 目标 60 帧):修复后稳定 50-51 帧。
调色可见性(640x360 生成素材,1.5s@30fps 目标 45 帧):修复后 32-39 帧,
max_channel_dev=0(饱和度 0 完美生效)。意外发现:一个独立的、修复范围之外的瓶颈
修复后帧数(~25.5fps)仍低于任务预期的 30fps 上限(原定阈值 ≥85/3s)。用子 Agent 追踪整条渲染路径后确认:
crates/opentake-render/src/gpu/compositor.rs里PendingReadback::finish每帧都会调用一次同步的device.poll(wgpu::Maintain::Wait)做 GPU 读回,这是全 crate唯一一处poll调用,构成一个与 sleep 逻辑无关的、固定的每帧 GPU 往返延迟下限(debug/release 构建耗时几乎一致,印证瓶颈在 GPU/驱动层而非 CPU 计算)。这是本 PR 修复范围之外的独立、预先存在的瓶颈——
#192描述的"无条件睡满整个周期"逻辑错误已彻底修复(验证:elapsed现在正确覆盖了包含该 GPU 读回在内的整个render_frame耗时,不存在重复计算或被掩盖的情况);只是修复后暴露出渲染吞吐本身另有一层瓶颈,需要单独 issue 跟进(优化方向:map_async+ 非阻塞轮询取代Maintain::Wait,或多帧 in-flight 流水线)。已据此把三个探针的阈值从任务原定值下调到"明确验证修复生效、且低于真机重复测量下限"的水位,并在测试注释里写明了这层背景,避免误导后续开发者以为阈值就是无优化空间的理论上限。验证
cargo test -p opentake-tauri --test playback_probe -- --ignored --nocapture(设OPENTAKE_PROBE_VIDEO=/Volumes/mac/1111.mp4):3 个探针全过,重复运行稳定。cargo fmt --all -- --check:无需改动。cargo clippy --workspace --all-targets -- -D warnings:零告警(含发现并修正了新探针文件里一处field_reassign_with_default)。cargo test -p opentake-tauri:147+ 项常规测试全绿,无回归。Fixes #192