Description
Description
When running a modified version of the standalone/02_hello_window
example, which disables window decorations and calls winit::window::Window::drag_resize_window
on left click, the window content is not updated immediately when the window is resized. Instead, it updates once you stop resizing for a fraction of a second. The attached video shows the issue while running on Ubuntu 24.04.2 LTS using an NVIDIA GeForce RTX™ 4060 Ti graphics card.
Repro steps
Example code:
use std::sync::Arc;
use winit::{
application::ApplicationHandler,
event::{WindowEvent, MouseButton},
event_loop::{ActiveEventLoop, ControlFlow, EventLoop},
window::{Window, WindowId, ResizeDirection},
};
struct State {
window: Arc<Window>,
device: wgpu::Device,
queue: wgpu::Queue,
size: winit::dpi::PhysicalSize<u32>,
surface: wgpu::Surface<'static>,
surface_format: wgpu::TextureFormat,
}
impl State {
async fn new(window: Arc<Window>) -> State {
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::default());
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions::default())
.await
.unwrap();
let (device, queue) = adapter
.request_device(&wgpu::DeviceDescriptor::default())
.await
.unwrap();
let size = window.inner_size();
let surface = instance.create_surface(window.clone()).unwrap();
let cap = surface.get_capabilities(&adapter);
let surface_format = cap.formats[0];
let state = State {
window,
device,
queue,
size,
surface,
surface_format,
};
// Configure surface for the first time
state.configure_surface();
state
}
fn get_window(&self) -> &Window {
&self.window
}
fn configure_surface(&self) {
let surface_config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: self.surface_format,
// Request compatibility with the sRGB-format texture view we‘re going to create later.
view_formats: vec![self.surface_format.add_srgb_suffix()],
alpha_mode: wgpu::CompositeAlphaMode::Auto,
width: self.size.width,
height: self.size.height,
desired_maximum_frame_latency: 2,
present_mode: wgpu::PresentMode::AutoVsync,
};
self.surface.configure(&self.device, &surface_config);
}
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.size = new_size;
// reconfigure the surface
self.configure_surface();
}
fn render(&mut self) {
// Create texture view
let surface_texture = self
.surface
.get_current_texture()
.expect("failed to acquire next swapchain texture");
let texture_view = surface_texture
.texture
.create_view(&wgpu::TextureViewDescriptor {
// Without add_srgb_suffix() the image we will be working with
// might not be "gamma correct".
format: Some(self.surface_format.add_srgb_suffix()),
..Default::default()
});
// Renders a GREEN screen
let mut encoder = self.device.create_command_encoder(&Default::default());
// Create the renderpass which will clear the screen.
let renderpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &texture_view,
depth_slice: None,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
// If you wanted to call any drawing commands, they would go here.
// End the renderpass.
drop(renderpass);
// Submit the command in the queue to execute
self.queue.submit([encoder.finish()]);
self.window.pre_present_notify();
surface_texture.present();
}
}
#[derive(Default)]
struct App {
state: Option<State>,
}
impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
// Create window object
let window = Arc::new(
event_loop
.create_window(Window::default_attributes().with_decorations(false))
.unwrap(),
);
let state = pollster::block_on(State::new(window.clone()));
self.state = Some(state);
window.request_redraw();
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
let state = self.state.as_mut().unwrap();
match event {
WindowEvent::CloseRequested => {
println!("The close button was pressed; stopping");
event_loop.exit();
}
WindowEvent::RedrawRequested => {
state.render();
// Emits a new redraw requested event.
state.get_window().request_redraw();
}
WindowEvent::Resized(size) => {
// Reconfigures the size of the surface. We do not re-render
// here as this event is always followed up by redraw request.
state.resize(size);
}
WindowEvent::MouseInput {
button: MouseButton::Left,
..
} => {
state.get_window().drag_resize_window(ResizeDirection::East).unwrap();
}
_ => (),
}
}
}
fn main() {
// wgpu uses `log` for all of our logging, so we initialize a logger with the `env_logger` crate.
//
// To change the log level, set the `RUST_LOG` environment variable. See the `env_logger`
// documentation for more information.
env_logger::init();
let event_loop = EventLoop::new().unwrap();
// When the current loop iteration finishes, immediately begin a new
// iteration regardless of whether or not new events are available to
// process. Preferred for applications that want to render as fast as
// possible, like games.
event_loop.set_control_flow(ControlFlow::Poll);
// When the current loop iteration finishes, suspend the thread until
// another event arrives. Helps keeping CPU utilization low if nothing
// is happening, which is preferred if the application might be idling in
// the background.
// event_loop.set_control_flow(ControlFlow::Wait);
let mut app = App::default();
event_loop.run_app(&mut app).unwrap();
}
Expected vs observed behavior
The window content should render the same on window resize regardless of whether decorations are disabled or not.
Extra materials
https://github.com/user-attachments/assets/d07f408e-5fbe-4d77-983d-ec337ee35303
Platform
wgpu version: trunk
(3f96ba29804acf7710098ed1c2b352f8b8d07dc7
)
OS: Ubuntu 24.04.2 LTS
GPU: NVIDIA GeForce RTX™ 4060 Ti
Metadata
Metadata
Assignees
Labels
Type
Projects
Status