vsg-dev / VulkanSceneGraph

Vulkan & C++17 based Scene Graph Project
http://www.vulkanscenegraph.org
MIT License
1.29k stars 207 forks source link

Win32 window resizing doesn't draw new frames #1293

Open AnyOldName3 opened 3 days ago

AnyOldName3 commented 3 days ago

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 commented 3 days ago

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

AnyOldName3 commented 3 days ago

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 commented 12 hours ago

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