perf(latency): tier-0 attribution + tier-1 send-path levers from the latency plan
ci / web (push) Successful in 49s
ci / docs-site (push) Successful in 54s
decky / build-publish (push) Successful in 18s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 9s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 7s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 8s
apple / swift (push) Successful in 1m23s
ci / rust (push) Failing after 4m15s
arch / build-publish (push) Failing after 4m35s
deb / build-publish (push) Failing after 4m4s
docker / deploy-docs (push) Successful in 24s
ci / bench (push) Successful in 5m35s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Failing after 3m45s
windows-host / package (push) Failing after 9m8s
release / apple (push) Successful in 5m49s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Failing after 5m25s
flatpak / build-publish (push) Failing after 8m3s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m58s
android / android (push) Successful in 14m9s
apple / screenshots (push) Successful in 6m28s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 7m0s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 9m28s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 12m26s

design/latency-reduction-2026-07.md T0.1/T0.2/T1.2/T1.3:

- T1.2 rate-capped front-loaded pacing: the paced overflow's budget is now
  min(0.9x slack, overflow wire time at ~3x the live encoder bitrate)
  (PUNKTFUNK_PACE_FACTOR, 0 = legacy deadline-only spread). A 300 KB-1 MB
  frame's tail leaves in ~2-5 ms instead of smearing across ~15 ms at 60 fps;
  GameStream schedule byte-identical (pins unchanged).
- T1.3 data-first wire order: packetize emits every block's data shards before
  any parity (per-block parity pools keep all blocks' parity alive for the
  second pass), so lossless completion stops waiting behind the parity tail.
  EOF = last emitted packet; receiver already order-agnostic.
- T0.1 staged 0xCF: HostTiming gains an append-extensible per-stage tail
  (queue/encode/pace us; seal+channel-wait derived as residual) - no cap bit
  needed, old peers read the 13-byte prefix. Joined client-side into
  Stats::host_{queue,encode,xfer,pace}_ms, the OSD detailed tier, and the
  probe's report.
- T0.2 true on-glass present timing: VK_KHR_present_id/present_wait enabled
  when supported; a PresentTimer waiter thread resolves each present id to
  real visibility, replacing the submit-time display stamp (which undercounts
  by up to a refresh and hides a silent-FIFO standing queue).

Validated on .21: core 185 + host 185 tests, pf-presenter 19, clippy
-D warnings across all five touched crates; loss-harness recovery curve
unchanged; C ABI harness round-trips.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 19:12:36 +02:00
parent c28b10a5b9
commit aedee2a4e3
15 changed files with 779 additions and 94 deletions
+39 -2
View File
@@ -129,17 +129,30 @@ impl Presenter {
let dev_props = unsafe { instance.get_physical_device_properties(pdev) };
let dev_is_13 = vk::api_version_major(dev_props.api_version) > 1
|| vk::api_version_minor(dev_props.api_version) >= 3;
let mut have_pid = vk::PhysicalDevicePresentIdFeaturesKHR::default();
let mut have_pwait = vk::PhysicalDevicePresentWaitFeaturesKHR::default();
let mut have_f11 = vk::PhysicalDeviceVulkan11Features::default();
let mut have_f12 = vk::PhysicalDeviceVulkan12Features::default();
let mut have_f13 = vk::PhysicalDeviceVulkan13Features::default();
// Present-id/present-wait (on-glass timing, latency plan T0.2): query the feature
// structs only when the device lists both extensions.
let present_wait_exts =
has(ash::khr::present_id::NAME) && has(ash::khr::present_wait::NAME);
let mut have_f2 = vk::PhysicalDeviceFeatures2::default()
.push_next(&mut have_f11)
.push_next(&mut have_f12)
.push_next(&mut have_f13);
if present_wait_exts {
have_f2 = have_f2.push_next(&mut have_pid).push_next(&mut have_pwait);
}
unsafe { instance.get_physical_device_features2(pdev, &mut have_f2) };
// Copy the one base-features fact out NOW: `have_f2` mutably borrows the 11/12/13
// structs through its pNext chain, so any later use of it would pin those borrows.
// Copy the one base-features fact out NOW: `have_f2` mutably borrows the chained
// structs through its pNext chain, so any later use of it would pin those borrows
// every read of a chained struct below must come after this, have_f2's last use.
let have_shader_int16 = have_f2.features.shader_int16;
let present_wait_ok = present_wait_exts
&& have_pid.present_id == vk::TRUE
&& have_pwait.present_wait == vk::TRUE;
let features_ok = have_f11.sampler_ycbcr_conversion == vk::TRUE
&& have_f12.timeline_semaphore == vk::TRUE
&& have_f13.synchronization2 == vk::TRUE;
@@ -224,6 +237,15 @@ impl Presenter {
);
}
// Present-id/present-wait: enable when fully supported — the presenter then runs
// the on-glass PresentTimer; otherwise the display stamp stays submit-time.
if present_wait_ok {
dev_exts.push(ash::khr::present_id::NAME.as_ptr());
dev_exts.push(ash::khr::present_wait::NAME.as_ptr());
}
let mut en_pid = vk::PhysicalDevicePresentIdFeaturesKHR::default().present_id(true);
let mut en_pwait = vk::PhysicalDevicePresentWaitFeaturesKHR::default().present_wait(true);
// Enable only the features the video path needs, and only where supported
// (harmless when the path is off; reported to FFmpeg via device_features).
let mut en_f11 = vk::PhysicalDeviceVulkan11Features::default()
@@ -240,6 +262,9 @@ impl Presenter {
.push_next(&mut en_f11)
.push_next(&mut en_f12)
.push_next(&mut en_f13);
if present_wait_ok {
en_f2 = en_f2.push_next(&mut en_pid).push_next(&mut en_pwait);
}
en_f2.features.shader_int16 = if pyrowave_ok { vk::TRUE } else { vk::FALSE };
let priorities = [1.0f32];
@@ -265,6 +290,15 @@ impl Presenter {
}
.context("vkCreateDevice")?;
let swap_d = ash::khr::swapchain::Device::new(&instance, &device);
let present_timer = present_wait_ok.then(|| {
super::present_timing::PresentTimer::spawn(ash::khr::present_wait::Device::new(
&instance, &device,
))
});
tracing::info!(
present_wait = present_wait_ok,
"on-glass present timing (VK_KHR_present_wait)"
);
let hdr_metadata_d =
has_hdr_metadata.then(|| ash::ext::hdr_metadata::Device::new(&instance, &device));
let queue = unsafe { device.get_device_queue(qfi, 0) };
@@ -441,6 +475,9 @@ impl Presenter {
staging: None,
video: None,
submitted: false,
present_timer,
next_present_id: 0,
last_presented: None,
};
p.recreate_swapchain(window)?;
Ok(p)