fix(metal): use isVisible instead of occlusionState for window visibility check#9410
fix(metal): use isVisible instead of occlusionState for window visibility check#9410intel352 wants to merge 1 commit intogfx-rs:trunkfrom
Conversation
…lity check occlusionState does not have the NSWindowOcclusionStateVisible bit set when the app is launched from Terminal or other CLI environments on macOS, causing get_current_texture() to return Occluded indefinitely even though the window is visible and interactive. isVisible correctly reflects the window's actual visibility regardless of how the application was launched. Fixes gfx-rs#8309
|
The docs say that Do you have a minimal test case to demonstrate the problem? The wgpu |
|
Good point on Here's a minimal repro using winit + wgpu that demonstrates the Terminal-launch bug: Setup # Cargo.toml
[dependencies]
wgpu = "23"
winit = "0.30"
pollster = "0.3"use winit::{
application::ApplicationHandler,
event::WindowEvent,
event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
};
struct App {
window: Option<std::sync::Arc<Window>>,
surface: Option<wgpu::Surface<'static>>,
device: Option<wgpu::Device>,
queue: Option<wgpu::Queue>,
config: Option<wgpu::SurfaceConfiguration>,
}
impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window = std::sync::Arc::new(
event_loop
.create_window(Window::default_attributes().with_title("wgpu occlusion repro"))
.unwrap(),
);
let instance = wgpu::Instance::default();
let surface = instance.create_surface(window.clone()).unwrap();
let (adapter, device, queue) = pollster::block_on(async {
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
compatible_surface: Some(&surface),
..Default::default()
})
.await
.expect("no adapter");
let (device, queue) = adapter
.request_device(&wgpu::DeviceDescriptor::default(), None)
.await
.expect("no device");
(adapter, device, queue)
});
let size = window.inner_size();
let caps = surface.get_capabilities(&adapter);
let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: caps.formats[0],
width: size.width.max(1),
height: size.height.max(1),
present_mode: wgpu::PresentMode::Fifo,
alpha_mode: caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
self.window = Some(window);
self.surface = Some(surface);
self.device = Some(device);
self.queue = Some(queue);
self.config = Some(config);
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => {
let surface = self.surface.as_ref().unwrap();
match surface.get_current_texture() {
Ok(_frame) => println!("get_current_texture: Ok"),
Err(wgpu::SurfaceError::Occluded) => {
println!("get_current_texture: Occluded <-- bug: window IS visible");
}
Err(e) => println!("get_current_texture: Error({e:?})"),
}
self.window.as_ref().unwrap().request_redraw();
}
_ => {}
}
}
}
fn main() {
let event_loop = EventLoop::new().unwrap();
let mut app = App::new();
event_loop.run_app(&mut app).unwrap();
}Run Better fix proposal Rather than replacing This preserves the original intent while fixing the Terminal regression. The known gap is that a window behind another window still has Happy to update the PR with this approach if it sounds right. |
Summary
occlusionStatedoes not have theNSWindowOcclusionStateVisiblebit set when the app is launched from Terminal or other CLI environments on macOS, causingget_current_texture()to returnOccludedindefinitely — even though the window is visible and interactive.Replaced with
isVisiblewhich correctly reflects the window's actual visibility regardless of how the application was launched.Problem
When a wgpu application is launched from Terminal.app (or any CLI), the Metal backend's occlusion check in
surface.rsalways returnsOccluded:NSWindow.occlusionStateis queriedNSWindowOcclusionStateVisiblebit is never set for Terminal-launched appsget_current_texture()returnsSurfaceError::Occludedevery frameThis affects any wgpu app launched from CLI — not just specific applications.
Fix
Replace
occlusionStatebit-flag check withisVisiblemessage send.isVisiblereturnstruewhen the window is on-screen regardless of launch context.Testing
opencommand: renders correctlyFixes #8309