rust-windowing / winit

Window handling library in pure Rust
https://docs.rs/winit/
Apache License 2.0
4.58k stars 878 forks source link

Not receiving `RedrawRequested` events for all windows that `.request_redraw()` #3648

Open ickk opened 2 months ago

ickk commented 2 months ago

Description

When I create multiple windows with winit, and for each window call window.request_redraw() in the AboutToWait handler, I do not receive the corresponding WindowEvent::RedrawRequested for all of the windows that requested.


minimal reproduction:

use winit::{
    event::{Event, WindowEvent},
    event_loop::EventLoop,
    window::WindowBuilder,
};

const WINDOW_COUNT: usize = 5;

fn main() {
    let event_loop = EventLoop::new().unwrap();

    let windows = Vec::from_iter(
        std::iter::repeat_with(|| WindowBuilder::new().build(&event_loop).unwrap()).take(WINDOW_COUNT),
    );

    let mut ticks = 500;
    event_loop
        .run(|event, elwt| match event {
            Event::AboutToWait => {
                for (i, window) in windows.iter().enumerate() {
                    eprintln!("request_redraw for window {i}",);
                    window.request_redraw();
                }
                ticks -= 1;
                if ticks <= 0 {
                    elwt.exit()
                }
            }
            Event::WindowEvent {
                window_id,
                event: WindowEvent::RedrawRequested,
            } => {
                let i = windows.iter().position(|w| w.id() == window_id).unwrap();
                eprintln!("received RedrawRequested for window {i}");
            }
            _ => (),
        })
        .unwrap();
}

output observed:

..
request_redraw for window 0
request_redraw for window 1
request_redraw for window 2
request_redraw for window 3
request_redraw for window 4
received RedrawRequested for window 4
received RedrawRequested for window 3
request_redraw for window 0
request_redraw for window 1
request_redraw for window 2
request_redraw for window 3
request_redraw for window 4
received RedrawRequested for window 4
received RedrawRequested for window 3
..

that is, I only receive the RedrawRequested event for the last 2 windows.


If I also call request_redraw() inside the RedrawRequested handler for the given window, as follows:

            Event::WindowEvent {
                window_id,
                event: WindowEvent::RedrawRequested,
            } => {
                let i = windows.iter().position(|w| w.id() == window_id).unwrap();
                eprintln!("received RedrawRequested for window {i}, requesting redraw for window {i}");
                windows[i].request_redraw();
            }
            _ => (),

then I observe the following:

..
request_redraw for window 0
request_redraw for window 1
request_redraw for window 2
request_redraw for window 3
request_redraw for window 4
received RedrawRequested for window 4, requesting redraw for window 4
received RedrawRequested for window 4, requesting redraw for window 4
request_redraw for window 0
request_redraw for window 1
request_redraw for window 2
request_redraw for window 3
request_redraw for window 4
received RedrawRequested for window 4, requesting redraw for window 4
received RedrawRequested for window 4, requesting redraw for window 4
..

that is, I receive two consecutive RedrawRequested events (sometimes just 1), but only ever for the last window.


Neither approach behaves as I would expect. I would expect to reliably receive a single RedrawRequested event for each window

Windows version

Microsoft Windows [Version 10.0.19045.4291]

Winit version

v0.29.15

ickk commented 2 months ago

I skimmed the source, and I think this might be because of the way winit tries to get RedrawWindow/WM_PAINT to throttle redraws?

kchibisov commented 2 months ago

Yes, redraws are throttled by the compositor usually, though, WM_PAINT looks so broken that winit should likely use something else.

geom3trik commented 6 days ago

I'm getting the same problem. I would have assumed that redraw events are only throttled per window? Is there any work around to this or does it require a patch to winit?

kchibisov commented 6 days ago

I mean if you want to draw when your window is not visible and compositor thinks that you shouldn't, then just draw unconditionally after timeout or so. In general if you don't get the event back it means that you shouldn't draw at all, because the window is either obscured or something like that, so user wont ever see anything anyway.

ickk commented 6 days ago

Is there any work around to this

I think what might mostly work is to send request_redraw for only your main window, and then draw to all windows when you receive it

kchibisov commented 6 days ago

But why would you want to do that though? If you want to draw when you're suggested to not do so then just draw unconditionally?

daxpedda commented 5 days ago

I don't believe the premise is that RedrawRequested is not sent because windows are obscured, the premise is that its a bug. In which case OP was just looking for a workaround.

@ickk it would be good to know if you are indeed not getting RedrawRequested events because wundows are obscured. Can you confirm that all windows are visible?

ickk commented 5 days ago

it would be good to know if you are indeed not getting RedrawRequested events because wundows are obscured. Can you confirm that all windows are visible?

I can confirm the minimal reproduction included in the issue body produces the same result even when all windows are fully visible