Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Win32 window resizing doesn't draw new frames #1293

Open
AnyOldName3 opened this issue Oct 4, 2024 · 3 comments
Open

Win32 window resizing doesn't draw new frames #1293

AnyOldName3 opened this issue Oct 4, 2024 · 3 comments

Comments

@AnyOldName3
Copy link
Contributor

As discussed a few days ago, resizing the window on Windows is supposed to continue drawing frames during the resize, but doesn't, and instead, the last frame just gets bilinearly scaled to fit the window until the mouse is released.

I've done some investigation, and the reason for the current behaviour is that the default window message handlers aren't leaving the stack until the resize has finished. That means that the resize events just get queued up in a buffer and then all happen in quick succession between frames. That's particularly bad as each of the buffered resize events triggers regeneration of all pipelines, and those new pipelines will go unused except for the final resize, although with #1268 or equivalent, that would stop happening.

If we were to do things by the book, then instead of having the current

        while (viewer->advanceToNextFrame())
        {
            viewer->handleEvents();
            viewer->update();
            viewer->recordAndSubmit();
            viewer->present();
        }

main loop, we'd have something more like Win32_Window.cpp's

    while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
    {
        if (msg.message == WM_QUIT)
        {
            // somehow close all windows
            events.emplace_back(vsg::CloseWindowEvent::create(this, event_time));
        }
        else
        {
            TranslateMessage(&msg);
            DispatchMessageW(&msg);
        }
    }

and all the calls to the viewer would be done in response to a WM_PAINT message.

Windows is sending a WM_PAINT message several times during a resize to tell us to draw a new frame, but because the viewer->handleEvents() call hasn't returned, we can't.

For comparison, I checked what the OSG did, and its behaviour was arguably worse - it didn't do the bilinear scaling, but did still stop rendering frames, so you'd end up with this ugly mess:
image

vsgQt's vsgqtviewer avoids the problem because it basically doesn't use a typical VSG main loop, instead letting Qt have control over the main loop. Qt internally has something relatively similar to a by-the-book Win32 window event pump as its main loop, and fits its own work within it, e.g. interleaving the timer event that triggers a redraw into its event queue.

I'll look into ways to solve this and hope there'll be something that doesn't require the message pump to become the main loop.

@robertosfield
Copy link
Collaborator

Would it be possible to have a dedicate background thread associated with Win32_Window.cpp that polls the events?

@AnyOldName3
Copy link
Contributor Author

The messages arrive in the thread that created the window. I don't know if we could move loads of stuff to a helper thread so that everything was dealt with by a background thread - that seems complicated and potentially fragile. I did have a look at what glfw does in case it's got a brilliant system to make the problem go away, but at least with how VulkanTutorial uses it, it's got the same problem as we do where new frames aren't drawn during the resize.

@AnyOldName3
Copy link
Contributor Author

I've had a look at the typical approaches for getting rid of this problem with GLFW, and they are:

  • It's got a callback it fires when the window is resized which applications can use to draw a new frame to fit the new size.
  • When a resize starts, you can start a second frame loop on another thread and stop it once the resize has finished. Obviously, this needs synchronisation if you're going to respond to events coming from the main thread. I guess it's kind of equivalent to moving all the window stuff to a background thread.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants