Three findings from one morning, all of them things that were invisible rather than wrong. docs/RUST.md's two new sections have the full account. **`cargo test --workspace` was taking half an hour, and it was debug info.** rustc's default `debug = true`, times eight test binaries each statically linking the whole wgpu + naga + winit + parley graph, means every one of them gets a private copy of that graph's DWARF written into it: the linkers for one run had written ~54 GB between them and were still going at thirty minutes -- the worst single one 16.9 GB for one test binary -- leaving an 88 GB target/. It was not CPU: the machine was 87% idle, and rust-lld's threads were in D state in btrfs `handle_reserve_ticket`, blocked on space reservation at 83% full. So `debug = "line-tables-only"` on both `profile.dev` and `profile.test` -- both, because `cargo test` builds dependencies under one and the test targets under the other. Cold, with all 19 suites run: 69 s and a 3.7 GB target. Backtraces keep file and line; `RUSTFLAGS="-C debuginfo=2"` per run buys back variable inspection when a debugger actually needs it. **The desktop had no logger at all**, so every `log::` call on that side went to `log`'s no-op default -- including the GLES fallback warning added hours earlier. `DefaultApp::run` installs a stderr logger (`src/default/logging.rs`, no new dependency: a level and a line is a page of code against env_logger plus its filter dialect), and the renderer now says which adapter won at `info`. That line is the point: with a silent fallback, a layer-2 screenshot rendered by llvmpipe and one rendered by the host's GPU are the same PNG, and which one it was is exactly what the screenshot is being taken to judge. **`tests/mask_sdf.rs` is a render pass now, not a compute pass.** It asked for `adapter.limits()` because `iris_core::device_limits()` deliberately zeroes the six `max_compute_*` fields -- a decision on record since 2026-09-05, which this quietly worked around instead of following. It now asks for what iris asks for and calls the function from the fragment stage, where the renderer calls it. The compute pass was *not* why it crashed, and the record should not say it was: the rewrite crashes identically. What the crash is: dropping a wgpu device on this VM's Venus adapter segfaults, after the test has produced its answer (worst CPU/shader disagreement 5.8e-6). Narrowed -- plain Vulkan creating and destroying five VkDevices on the same adapter is clean, and the same binary with Vulkan hidden falls back to GL and exits clean. Worked around at `Gpu::leak`, with the reason and the delete-me condition written there. `rigs/virtgpu-probe` is the new rig behind the Venus half: which capsets the host offers (0x16 -- VIRGL, VIRGL2, VENUS; no capset 6, so no DRM native context without host-side work), whether the device has compute (it does: 1024 invocations/workgroup -- the "no compute" finding on record is about the Android emulator's SwiftShader, a different machine), and whether plain Vulkan teardown is clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
65 lines
1.9 KiB
Rust
65 lines
1.9 KiB
Rust
use winit::{
|
|
application::ApplicationHandler,
|
|
event::WindowEvent,
|
|
event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy},
|
|
window::WindowId,
|
|
};
|
|
|
|
pub trait AppState {
|
|
type Event: 'static;
|
|
fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy<Self::Event>) -> Self;
|
|
fn window_event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop);
|
|
fn event(&mut self, event: Self::Event, event_loop: &ActiveEventLoop);
|
|
fn exit(&mut self);
|
|
|
|
fn run()
|
|
where
|
|
Self: Sized,
|
|
{
|
|
App::<Self>::run();
|
|
}
|
|
}
|
|
|
|
pub struct App<State: AppState> {
|
|
state: Option<State>,
|
|
proxy: EventLoopProxy<State::Event>,
|
|
}
|
|
|
|
impl<State: AppState> App<State> {
|
|
pub fn run() {
|
|
// The desktop's `main` in everything but name -- see
|
|
// `super::logging`'s doc for why the logger goes here and what
|
|
// its absence hid.
|
|
super::logging::install(log::LevelFilter::Info);
|
|
let event_loop = EventLoop::with_user_event().build().unwrap();
|
|
let proxy = event_loop.create_proxy();
|
|
event_loop
|
|
.run_app(&mut App::<State> { state: None, proxy })
|
|
.unwrap();
|
|
}
|
|
}
|
|
|
|
impl<State: AppState> ApplicationHandler<State::Event> for App<State> {
|
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
|
if self.state.is_none() {
|
|
let state = State::new(event_loop, self.proxy.clone());
|
|
self.state = Some(state);
|
|
}
|
|
}
|
|
|
|
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
|
|
let state = self.state.as_mut().unwrap();
|
|
state.window_event(event, event_loop);
|
|
}
|
|
|
|
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: State::Event) {
|
|
let state = self.state.as_mut().unwrap();
|
|
state.event(event, event_loop);
|
|
}
|
|
|
|
fn exiting(&mut self, _: &ActiveEventLoop) {
|
|
let state = self.state.as_mut().unwrap();
|
|
state.exit();
|
|
}
|
|
}
|