From 60c06565c9b40e89886ff5d08f95fd3eb5b953d2 Mon Sep 17 00:00:00 2001 From: dafioram Date: Thu, 5 Nov 2026 15:02:44 +0000 Subject: [PATCH 1/9] Stop showing the DX12 shader model in the adapter panel The panel printed "Compute shaders yes (Sm5)" for DX12 adapters. wgpu reports a default shader model there rather than the driver's, so the Sm5 looked measured and was not. The panel now shows only the yes/no answer for compute, which comes from the real COMPUTE_SHADERS capability. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01DS52oJk4dJpQPT7u2QpTr8 --- src/app.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app.rs b/src/app.rs index 4df30e7..2f2a1b6 100644 --- a/src/app.rs +++ b/src/app.rs @@ -525,8 +525,10 @@ impl App { row("Backend", gpu::backend_str(i.backend).into()); row( "Compute shaders", + // The shader model is not shown: on DX12 wgpu reports a default + // value, not the driver's, so only the yes/no answer is real. if a.has_compute() { - format!("yes ({:?})", a.downlevel.shader_model) + "yes".into() } else { "NO — OpenGL needs 4.3 for compute".into() }, From 20ea223ddffa569ae42629d1a5dba9e856e34172 Mon Sep 17 00:00:00 2001 From: dafioram Date: Thu, 5 Nov 2026 15:28:08 +0000 Subject: [PATCH 2/9] Add a tested-hardware section to the README Lists the real GPUs the app has been run on, with OS and driver: a GTX 760 (Kepler) on Windows 10, and an RX 6750 XT on Windows 11. Also notes that CI runs on software devices (Mesa llvmpipe and lavapipe, Microsoft's WARP), which check code paths but not real-GPU speed. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01DS52oJk4dJpQPT7u2QpTr8 --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 706656d..45d376e 100644 --- a/README.md +++ b/README.md @@ -261,6 +261,17 @@ The newest 30 are kept. The GUI log panel has **Copy**, **Clear** and | VRAM result shows WARN | lower "VRAM to test" to the estimate in the result | | DX12 matmul pauses several seconds before its result; log says FXC | copy `dxcompiler.dll` next to the program; see DX12 shader compiler | +## Tested hardware + +Real GPUs the app has been run on: + +| GPU | OS | Driver | Notes | +|---|---|---|---| +| NVIDIA GeForce GTX 760 (Kepler) | Windows 10 | 475.14 (NVML); 30.0.14.7514 (DX12) | DX12: transfer, bandwidth, matmul and VRAM tests. DX12 FP16 is skipped: Kepler has no native 16-bit shader support. | +| AMD Radeon RX 6750 XT | Windows 11 | not recorded | | + +The GPU runs above were done by the maintainer. CI also runs on software devices that are not GPUs: Mesa llvmpipe and lavapipe on Linux (Vulkan and OpenGL), and Microsoft's WARP on Windows (DX12). These check the code paths but say nothing about real-GPU speed. + ## Development ``` From 86b58ced54e99dbd781b2f72aa3da7ca6962a5f6 Mon Sep 17 00:00:00 2001 From: dafioram Date: Thu, 5 Nov 2026 15:31:09 +0000 Subject: [PATCH 3/9] Trim the tested-hardware section to devices and OS The section is for coverage only, so it lists the GPUs and their OS without driver or test details. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01DS52oJk4dJpQPT7u2QpTr8 --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 45d376e..6f73eb0 100644 --- a/README.md +++ b/README.md @@ -263,14 +263,14 @@ The newest 30 are kept. The GUI log panel has **Copy**, **Clear** and ## Tested hardware -Real GPUs the app has been run on: +Devices the app has run on: -| GPU | OS | Driver | Notes | -|---|---|---|---| -| NVIDIA GeForce GTX 760 (Kepler) | Windows 10 | 475.14 (NVML); 30.0.14.7514 (DX12) | DX12: transfer, bandwidth, matmul and VRAM tests. DX12 FP16 is skipped: Kepler has no native 16-bit shader support. | -| AMD Radeon RX 6750 XT | Windows 11 | not recorded | | +| GPU | OS | +|---|---| +| NVIDIA GeForce GTX 760 | Windows 10 | +| AMD Radeon RX 6750 XT | Windows 11 | -The GPU runs above were done by the maintainer. CI also runs on software devices that are not GPUs: Mesa llvmpipe and lavapipe on Linux (Vulkan and OpenGL), and Microsoft's WARP on Windows (DX12). These check the code paths but say nothing about real-GPU speed. +CI also runs without a GPU, on Mesa llvmpipe and lavapipe (Linux) and Microsoft's WARP (Windows). ## Development From 7dac51194dab33179279c2070d40b92fcaac8d3e Mon Sep 17 00:00:00 2001 From: dafioram Date: Thu, 5 Nov 2026 15:59:06 +0000 Subject: [PATCH 4/9] Add driver versions to the tested-hardware table GTX 760: NVIDIA 475.14, which Windows reports as driver 30.0.14.7514 for DX12. RX 6750 XT: driver version not yet recorded. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01DS52oJk4dJpQPT7u2QpTr8 --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6f73eb0..3822639 100644 --- a/README.md +++ b/README.md @@ -265,10 +265,10 @@ The newest 30 are kept. The GUI log panel has **Copy**, **Clear** and Devices the app has run on: -| GPU | OS | -|---|---| -| NVIDIA GeForce GTX 760 | Windows 10 | -| AMD Radeon RX 6750 XT | Windows 11 | +| GPU | OS | Driver | +|---|---|---| +| NVIDIA GeForce GTX 760 | Windows 10 | NVIDIA 475.14 (Windows driver 30.0.14.7514) | +| AMD Radeon RX 6750 XT | Windows 11 | — | CI also runs without a GPU, on Mesa llvmpipe and lavapipe (Linux) and Microsoft's WARP (Windows). From c76c8cbe32a98c0d00f3ba65982e85c3bece4348 Mon Sep 17 00:00:00 2001 From: dafioram Date: Thu, 5 Nov 2026 23:27:23 +0000 Subject: [PATCH 5/9] Show each adapter's driver in --list --list is the quick way to see which adapters a machine has, so it now prints the driver under each one. Normal test runs print the same output as before. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01DS52oJk4dJpQPT7u2QpTr8 --- src/cli.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cli.rs b/src/cli.rs index c72293d..2540693 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -192,6 +192,9 @@ pub fn main(args: &[String]) -> i32 { out!("Adapters:"); for a in &adapters { out!(" {}", a.label()); + if list_only { + out!(" driver: {}", gpu::driver_text(&a.info)); + } } if list_only { if let Some(note) = gpu::dx12_compiler_note() { From 578b90a75916bdb31eaeb9e4b6fe1a525db5db52 Mon Sep 17 00:00:00 2001 From: dafioram Date: Thu, 5 Nov 2026 23:45:56 +0000 Subject: [PATCH 6/9] Format the NVML memory sensor line like the other memory readouts The sensor diagnostics printed memory as a raw tuple, for example "memory: (701284352, 2147483648)". It now reads "memory: 668.7 MiB used / 2.00 GiB", using the same byte formatter as the VRAM readouts. This line is shown in the GUI log and in the CLI log file. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01DS52oJk4dJpQPT7u2QpTr8 --- src/sensors.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sensors.rs b/src/sensors.rs index 57bad27..6319a8d 100644 --- a/src/sensors.rs +++ b/src/sensors.rs @@ -340,7 +340,14 @@ impl Sensor { "utilization", d.utilization_rates().map(|u| (u.gpu, u.memory)), )); - v.push(show("memory", nvml_memory(&d).map(|m| (m.used, m.total)))); + v.push(match nvml_memory(&d) { + Ok(m) => format!( + "memory: {} used / {}", + crate::gpu::fmt_bytes(m.used), + crate::gpu::fmt_bytes(m.total) + ), + Err(e) => format!("memory: unavailable ({e})"), + }); v.push(show("core clock", d.clock_info(Clock::Graphics))); v.push(show("memory clock", d.clock_info(Clock::Memory))); v.push(show("fan", d.fan_speed(0))); From df54973a7f57144637144b7cb6e14954690dfba7 Mon Sep 17 00:00:00 2001 From: dafioram Date: Fri, 6 Nov 2026 14:08:02 +0000 Subject: [PATCH 7/9] Add GTX 1060 6GB and RX 6750 XT driver to the tested-hardware table GTX 1060 6GB on Windows 10, NVIDIA 560.94 (Windows driver 32.0.15.6094). RX 6750 XT on Windows 11, AMD 26.7.1 (Windows driver 32.0.21045.1000 for DX12). Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01DS52oJk4dJpQPT7u2QpTr8 --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3822639..8a5a1f6 100644 --- a/README.md +++ b/README.md @@ -268,7 +268,8 @@ Devices the app has run on: | GPU | OS | Driver | |---|---|---| | NVIDIA GeForce GTX 760 | Windows 10 | NVIDIA 475.14 (Windows driver 30.0.14.7514) | -| AMD Radeon RX 6750 XT | Windows 11 | — | +| NVIDIA GeForce GTX 1060 6GB | Windows 10 | NVIDIA 560.94 (Windows driver 32.0.15.6094) | +| AMD Radeon RX 6750 XT | Windows 11 | AMD 26.7.1 (Windows driver 32.0.21045.1000) | CI also runs without a GPU, on Mesa llvmpipe and lavapipe (Linux) and Microsoft's WARP (Windows). From fffb8c5a06826e615cdd1b35d959235e5dfd7c83 Mon Sep 17 00:00:00 2001 From: dafioram Date: Fri, 6 Nov 2026 14:23:44 +0000 Subject: [PATCH 8/9] Take the VRAM spill snapshot after the shaders are compiled The VRAM test took its system-memory snapshot before compiling its shaders. On the GTX 1060 (Vulkan) the system-RAM count then rose by a fixed 64 MiB at every test size, so every run was flagged as spilled, even at 100 MiB, and the estimate of what fit in VRAM was wrong. The snapshot now comes right before the chunks are allocated, so only memory from the chunks counts toward spill. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01DS52oJk4dJpQPT7u2QpTr8 --- src/bench.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bench.rs b/src/bench.rs index 6fcd09b..12a1da5 100644 --- a/src/bench.rs +++ b/src/bench.rs @@ -1308,7 +1308,6 @@ impl Bench { // Measure right before allocating: earlier readings go stale as other // apps (and earlier tests) allocate and free memory. let available = self.vram_available(); - let dxgi_before = crate::dxgi::query(&self.g.info).ok(); let target = if cfg.vram_auto { match &available { Some((free, src)) => { @@ -1371,6 +1370,10 @@ impl Bench { "errs", )?; + // Snapshot here, after the shaders and pipelines exist. Taken earlier, it + // counted their system memory as spill: on the GTX 1060 (Vulkan) the + // system-RAM count rose by a fixed 64 MiB at every test size. + let dxgi_before = crate::dxgi::query(&self.g.info).ok(); // Allocate chunks until the target is reached or the driver refuses. let mut chunks: Vec = Vec::new(); let mut total = 0u64; From 76c25c961edda66ce553af594f1139ff0e108075 Mon Sep 17 00:00:00 2001 From: dafioram Date: Fri, 6 Nov 2026 14:36:06 +0000 Subject: [PATCH 9/9] Judge the VRAM test from chunk speeds, and warn on partial allocations The spill check also counted DXGI's system-RAM growth. That growth includes a fixed 64 MiB on the GTX 1060 that is not the test's memory, so runs warned even when every chunk filled at full speed. - DXGI's counters are now logged for reference only. - A warning comes only from chunks that fill at under 40% of the fastest chunk's speed. The message gives how many chunks slowed and where in the test they started, not a "fit" estimate. The start point is not a capacity figure. - A run that stops early on an allocation error is now WARN, not PASS, since it did not test its full target. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01DS52oJk4dJpQPT7u2QpTr8 --- src/bench.rs | 76 +++++++++++++++++++--------------------------------- 1 file changed, 28 insertions(+), 48 deletions(-) diff --git a/src/bench.rs b/src/bench.rs index 12a1da5..1d1f31e 100644 --- a/src/bench.rs +++ b/src/bench.rs @@ -1492,21 +1492,18 @@ impl Bench { // Chunks under 40% of the fastest chunk's speed are almost certainly // not in VRAM (system RAM over PCIe is several times slower). Tiny // chunks are skipped because fixed overhead dominates their timing. - // On Windows, DXGI says directly how much of this process's GPU memory - // sits in system RAM; growth during the test is spillover. - let spilled = match (dxgi_before, crate::dxgi::query(&self.g.info).ok()) { - (Some(a), Some(b)) => { - let grew = b.nonlocal_usage.saturating_sub(a.nonlocal_usage); - self.log(format!( - "DXGI during test: {} in VRAM, {} in system RAM ({} more than before the test)", - fmt_bytes(b.local_usage), - fmt_bytes(b.nonlocal_usage), - fmt_bytes(grew) - )); - grew - } - _ => 0, - }; + // DXGI's counters are logged for reference only. On the GTX 1060 a fixed + // 64 MiB of system memory shows up during the test, and its VRAM figure + // exceeds the card, so the result is judged from the chunk speeds. + if let (Some(a), Some(b)) = (dxgi_before, crate::dxgi::query(&self.g.info).ok()) { + let grew = b.nonlocal_usage.saturating_sub(a.nonlocal_usage); + self.log(format!( + "DXGI during test (for reference): {} in VRAM, {} in system RAM ({} more than before the test)", + fmt_bytes(b.local_usage), + fmt_bytes(b.nonlocal_usage), + fmt_bytes(grew) + )); + } let fastest = chunk_speed.iter().cloned().fold(0.0, f64::max); let is_slow = |speed: &[f64], i: usize| chunks[i].size() >= chunk / 2 && speed[i] < 0.4 * fastest; @@ -1535,42 +1532,24 @@ impl Bench { .collect::>() .join(", ") )); - // Spillover is reported if either signal sees it: slow chunks, or - // DXGI counting at least one chunk's worth of new system-RAM usage. - let spill_seen = spilled >= chunk; - let paging = if slow.is_empty() && !spill_seen { + // Spillover shows as chunks that fill far slower than the fastest chunk. + // Where the slow ones start says how far into the test it happened. It + // is not a measure of how much VRAM the card has. + let paging = if slow.is_empty() { String::new() } else { - let fast_bytes: u64 = (0..chunks.len()) - .filter(|i| !slow.contains(i)) - .map(|i| chunks[i].size()) - .sum(); - // Best estimate of what really fit in VRAM. - let fit = if spill_seen { - fast_bytes.min(total.saturating_sub(spilled)) - } else { - fast_bytes - } / MIB; - let mut why = Vec::new(); - if !slow.is_empty() { - why.push(format!( - "{} of {} chunks filled at under 40% of the fastest chunk's speed", - slow.len(), - chunks.len() - )); - } - if spill_seen { - why.push(format!( - "Windows reports {} of this test's memory in system RAM", - fmt_bytes(spilled) - )); - } + let slow_from_mib = chunks[..slow[0]].iter().map(|c| c.size()).sum::() / MIB; self.log(format!( - "WARNING: part of the test ran in system RAM, not VRAM ({}). About {fit} MiB fit in VRAM; \ - set 'VRAM to test' to that (or use Auto) for a pure VRAM test.", - why.join("; ") + "WARNING: {} of {} chunks filled at under 40% of the fastest chunk's speed, \ + starting about {slow_from_mib} MiB into the test; part of it may have run in system RAM.", + slow.len(), + chunks.len() )); - format!(" — only ~{fit} MiB fit in VRAM; rest spilled to system RAM") + format!( + " — {} of {} chunks slowed, starting about {slow_from_mib} MiB in", + slow.len(), + chunks.len() + ) }; // Speeds come from the fastest pass. Averaging in a slow first pass @@ -1587,7 +1566,8 @@ impl Bench { ), status: if total_errors > 0 { Status::Fail - } else if !paging.is_empty() { + } else if !paging.is_empty() || !note.is_empty() { + // A run that could not allocate its full target did not test it all. Status::Warn } else { Status::Pass