previews: the glow goes, the eq steps, and every clip lands before you scroll to it
The track preview modal could hold a machine at 25% GPU. Three separate causes, each measured in isolation with a kill-style ladder and, once the GPU-engine counter proved to be clock-relative (a lighter workload read HIGHER), with clock-immune process CPU-seconds. Idle drain. The scrim's backdrop-filter is re-executed on every presented frame -- a backdrop filter is never cached -- so its cost is presents/sec times window area. Two infinite animations kept the modal presenting at the display's refresh rate forever: the eq capsule's bars animated `height` (a layout property, so style/layout/paint on the main thread every vsync) and the cover glow's halo-breathe kept a blur(48px) copy recompositing. Either one alone kept the pipeline hot, which is why killing one changed nothing. The glow is deleted outright at Kyle's request; the eq bars now scale with a stepped transform whose three delays are multiples of the step interval, so all three jump on the same instants and one present serves them all. Playing steady state: 7.2-10.5% of a core down to 1.4-2.2%. Paused: 0.2%. Scrolling. The animations turned out to be innocent here -- reverting them moved the number not at all. The cost was per-step work: re-decoding the full artwork for the background wash, a main-thread accent scan, a new cover mount, three times a second. The wash is now a 96x58 canvas with the blur/saturate/brightness baked in at draw time and cached per artwork, and an overlay preloader walks the manifest once at open -- nearest to the active card first, capped at 60 -- filling the decoded-image, wash and accent caches ahead of the user. A rapid-nav jump-cut was tried and rejected on sight; the animations stay full at every rate. Backend. preview_resolve swept the clip directory on every call, and any landing outside the +/-1 prefetch window paid a full Deezer mint, download, validate, write and loudness measure. The fetch-store-measure core is factored out and a new preview_prewarm walks the session's track ids at open, paced 250ms on top of the existing Deezer throttle and starting 1.5s in so the first clip owns the network, halted by a generation guard on dispose. A burst landing on a never-visited track is now audible in 154ms off local disk. The sweep is throttled to ten minutes, and dispose deletes prewarmed files instead of orphaning them until the hourly reap. The debug overlay's GPU row was summing the counters machine-wide, so it read roughly double the app's share with Discord and DWM folded in. It now reports `app X% - sys Y%`, the app figure restricted to our process tree, matching Task Manager's per-process rows.
This commit is contained in:
1 parent
eba29824c9
commit
69f64c11eb
8 files changed
+440
-116
No files matched your search
@@ -61,7 +61,12 @@ pub struct DebugStats {
|
||||
pub cpu_process: f32,
|
||||
pub cpu_system: f32,
|
||||
/// GPU 3D-engine utilization (Task-Manager-style), if counters opened.
|
||||
/// MACHINE-WIDE — includes DWM, browsers, Discord, everything.
|
||||
pub gpu_percent: Option<f32>,
|
||||
/// Same counters, restricted to OUR process tree — the app's true GPU
|
||||
/// share, matching Task Manager's per-process rows. The machine-wide
|
||||
/// number above routinely reads 2× this on a busy desktop.
|
||||
pub gpu_app_percent: Option<f32>,
|
||||
/// Process tree working set / private commit, bytes.
|
||||
pub mem_working_set: u64,
|
||||
pub mem_private: u64,
|
||||
@@ -159,10 +164,14 @@ pub fn debug_stats(app: tauri::AppHandle, state: State<'_, DiagState>) -> DebugS
|
||||
None => (None, None, None),
|
||||
};
|
||||
|
||||
let tree_u32: Vec<u32> = tree.iter().map(|p| p.as_u32()).collect();
|
||||
let gpu_sample = inner.gpu.as_mut().and_then(|g| g.sample(&tree_u32));
|
||||
|
||||
DebugStats {
|
||||
cpu_process,
|
||||
cpu_system: inner.sys.global_cpu_usage(),
|
||||
gpu_percent: inner.gpu.as_mut().and_then(|g| g.sample()),
|
||||
gpu_percent: gpu_sample.map(|(sys, _)| sys),
|
||||
gpu_app_percent: gpu_sample.map(|(_, app)| app),
|
||||
mem_working_set,
|
||||
mem_private,
|
||||
mem_system_used: inner.sys.used_memory(),
|
||||
@@ -257,7 +266,10 @@ impl GpuCounter {
|
||||
}
|
||||
}
|
||||
|
||||
fn sample(&mut self) -> Option<f32> {
|
||||
/// Returns (machine_total, app_tree_total). Instance names carry the
|
||||
/// owning pid — "pid_1234_luid_..._engtype_3D" — so the app's share is
|
||||
/// the sum over instances whose pid is in our process tree.
|
||||
fn sample(&mut self, app_pids: &[u32]) -> Option<(f32, f32)> {
|
||||
use windows::Win32::System::Performance::{
|
||||
PdhCollectQueryData, PdhGetFormattedCounterArrayW, PDH_FMT_COUNTERVALUE_ITEM_W,
|
||||
PDH_FMT_DOUBLE,
|
||||
@@ -292,14 +304,32 @@ impl GpuCounter {
|
||||
return None;
|
||||
}
|
||||
let mut total = 0.0f64;
|
||||
let mut app = 0.0f64;
|
||||
for i in 0..count as usize {
|
||||
total += (*items.add(i)).FmtValue.Anonymous.doubleValue;
|
||||
let item = &*items.add(i);
|
||||
let v = item.FmtValue.Anonymous.doubleValue;
|
||||
total += v;
|
||||
if !item.szName.is_null() {
|
||||
if let Some(pid) = instance_pid(item.szName) {
|
||||
if app_pids.contains(&pid) {
|
||||
app += v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(total.min(100.0) as f32)
|
||||
Some((total.min(100.0) as f32, app.min(100.0) as f32))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse the pid out of a "pid_1234_luid_…" counter-instance name.
|
||||
unsafe fn instance_pid(name: windows::core::PWSTR) -> Option<u32> {
|
||||
let s = name.to_string().ok()?;
|
||||
let rest = s.strip_prefix("pid_")?;
|
||||
let digits: String = rest.chars().take_while(|c| c.is_ascii_digit()).collect();
|
||||
digits.parse().ok()
|
||||
}
|
||||
|
||||
impl Drop for GpuCounter {
|
||||
fn drop(&mut self) {
|
||||
use windows::Win32::System::Performance::PdhCloseQuery;
|
||||
|
||||
Reference in new issue
Block a user