I have created several Winit windows and want them to redraw with vsync.
impl ApplicationHandler<UserLoopEvent> for DreamRunner {
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
// Create a few Windows and store them in HashMap
}
fn window_event(
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
window_id: winit::window::WindowId,
event: winit::event::WindowEvent,
) {
// Select Window by window_id, redraw it, and call window.request_repaint();
}
However, this always keeps redrawing one of the windows and ignoring others. It is as if request_repaint() cancels pending requests on another windows.
I also tried issuing request_repaint in fn about_to_wait( for all windows at once. It works, but clearly loses a lot of performance because it locks together update rates of all Windows. I get 20FPS that way, compared to 160 for a single Window. I need windows to update independently.
Nothing you've said sounds like you have misunderstood something about how to manage multiple windows, and you haven't shown any of your logic for actually deciding to redraw a window or issue the request_repaint(), so it's hard to identify anything that might be the cause of your problem (or that might point to this being a bug in winit).
It would help if you posted enough code to actually compile and run and see the misbehavior. (If your actual drawing code itself is large, leave that out or maybe replace it with softbuffer.)
I don't see any evident bug in your code. What I do notice is that, since you are issuing request_redraw() from inside the RedrawRequested handler only, if any redraw event is lost, that window will stop updating forever. But, it shouldn't be the case that a redraw event is lost — it might be delayed (e.g. if the window is not visible on screen), but not lost. So, perhaps there is a winit bug here. Have you tried running the same program on a different platform / window system?
I suspect that this doesn't mean what you think it means; I think that the lower frame rate you are seeing is because you are successfully making all windows repaint, and paying the cost of computing pixels for all windows instead of one window. There are no strategies for issuing redraws that will get you more total frame throughput. Computing every single pixel of multiple fullscreen windows, on a CPU rather than a GPU, is a significant cost.
You could squeeze more performance out of your pixel calculation, though:
Run it on a background thread rather than your event loop thread. Right now, you are using rayon parallel iteration, which is good, but every time the for_each()finishes, you're dropping back to single-threaded execution temporarily while the event loop cycles. In fact, since this is a continuous animation, you might as well run the entire drawing loop on a separate background thread per window, and ignore the RedrawRequested mechanism entirely.
(There are reasons to want to not ignore it, but they are minimal for this application. The main one is that if RedrawRequested events are not arriving, that means your window is hidden and you should stop drawing to not waste computation. But if your application is expected to always be 100% visible when it is running, that's not a big deal.)
There are costs to using rayon iteration; due to the potential for subdividing the work to run on muiltiple threads, the compiler can't compile a Rayon for_each() to unrolled or SIMD machine code like it can an ordinary iteration. Therefore, it can be useful to use a larger chunk size than “one pixel”, and write an ordinary non-parallel for loop inside for the optimizer to work on. Something roughly like this:
frame.par_chunks_mut(128 * 4).for_each(|chunk| {
for pixel in chunk.chunks_mut(4) {
pixel[0] = 0x00; // R
pixel[1] = time_factor * 40; // G
pixel[2] = 0x00; // B
pixel[3] = 0xff; // A
}
});
The 128 there is a totally made up number and you should try different values (quite likely larger ones) to see what is best.
Thanks for the input.
This, however, is the reason I am hesitant to link to whole projects in early development. I am not going to keep any of that pixel stuff. It is just to see something on the screen. I times it and I am sure it is not the bottleneck.
If I sort out the issue, I was going to create a totally independent thread pool that would prepare actual images in advance, then the draw code in main thread would just send them to pixels one by one.
Wasted all weekend trying to get several fullscreen windows to behave, no luck. Depending oh how I try to redraw them, they either redraw only one at a time, or all but with severe degradation of FPS (talk 4 FPS for 2 windows where 1 window holds 165 FPS with ease). Also, WGPU somehow conflicts with MSI Afterburner FPS overlay, and one of them crashes all the time. Also, WGPU rendering somehow sometimes blocks mouse clicks from being processed by Winit.
Instead. I created Tauri windows, fullscreen, opened HTML canvas full size and started drawing on them with JavaScript. It immediately worked ideally, at full performance, without even taxing PC notably, and with the builtin FPS counter (and a godless ton of documentation to boot). I wanted to draw some mathy thingys on screen, not wrangle with GPUs , and so this will be the tech stack I am using now.