4
\$\begingroup\$

I guess resizable windows are not being optimized for in game development but still... The following piece of code demonstrates a problem I'm having: resizing the window is very slow. Basically the window trails after my mouse as I'm moving it:

#include <SDL2/SDL.h>
#include <iostream>

int main() {
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);

    SDL_Window* window = SDL_CreateWindow("test", 10, 10, 800, 600, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);

    int count = 0;
    bool repaint = false;
    auto paint = [&](){
        if(!repaint) {
            return;
        }
        repaint = false;
        std::cout<<"Repaint "<<count++<<std::endl;
        SDL_Surface* surf = SDL_GetWindowSurface(window);
        int w, h;
        SDL_GetWindowSize(window, &w, &h);
        SDL_Rect rect = {0, 0, w, h};
        SDL_FillRect(surf, &rect, 0xff0000ff);
        SDL_UpdateWindowSurface(window);
    };
    SDL_Event event;
    bool run = true;
    while(run && SDL_WaitEvent(&event)){
        switch(event.type) {
        case  SDL_QUIT:
            run = false;
            break;
        case SDL_WINDOWEVENT:
            if(event.window.event == SDL_WINDOWEVENT_RESIZED) {
                repaint = true;
            }
            break;
        };
        paint();
    }
    SDL_Quit();
}

...is there some way for me to interfere with SDL's handling of window events that I am missing?

Platform is Linux (X11), have yet to test on windows.

\$\endgroup\$
  • 1
    \$\begingroup\$ What happens if you try non-blocking event handling using SDL_PollEvents()? \$\endgroup\$ – HolyBlackCat Jun 23 '16 at 14:26
  • \$\begingroup\$ Yeah, tried that already and it did not make a difference. \$\endgroup\$ – cptG Jun 23 '16 at 14:56
3
\$\begingroup\$

First, on Linux it appears to matter what Window Manager and Compositor you are using. WindowMaker with opaque windows enabled (not the default) was still fast, the wire windows are always fast because nothing has to repaint.

Second, there are a few things in your program that can be changed. The biggest issue is that you paint after every SDL_WINDOWEVENT event. Some Window Managers are very spammy with these events, taking no long-running action until the spam has died down will resolve this issue.

Third, I modified your program (see below). I also changed it to paint for the first frame. Your Window Manager may be sending you SDL_WINDOWEVENTs when the window is first created, but many Window Managers do not. (WindowMaker does not). I use SDL_WaitEventTimeout() to help with corruption when another window covers, another way is to handle SDL_VIDEOEXPOSE events and repaint on those too. If you wish you can go back to SDL_WaitEvent() and handle the expose events.

#include <SDL2/SDL.h>
#include <iostream>

int main() {
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);

    SDL_Window* window = SDL_CreateWindow("test", 10, 10, 800, 600, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);

    int count = 0;
    bool repaint = true; /* the screen has never been painted, we must paint */
    auto paint = [&](){
    if(!repaint) {
        return;
    }
    repaint = false;
    std::cout<<"Repaint "<<count++<<std::endl;
    SDL_Surface* surf = SDL_GetWindowSurface(window);
    int w, h;
    SDL_GetWindowSize(window, &w, &h);
    SDL_Rect rect = {0, 0, w, h};
    SDL_FillRect(surf, &rect, 0xff0000ff);
    SDL_UpdateWindowSurface(window);
    };
    paint(); /* paint once before events */
    SDL_Event event;
    bool run = true;
    const unsigned frame_wait = 10; /* anything less than 16 ms is good */
    while(run) {
    unsigned tries = 500; /* use UINT_MAX for no effective limit */
    if (SDL_WaitEventTimeout(&event, frame_wait)) {
        /* keep consuming events in a loop until there are no more,
         * or we run out of attempts. */
        do {
        switch(event.type) {
        case  SDL_QUIT:
            run = false;
            break;
        case SDL_WINDOWEVENT:
            if(event.window.event == SDL_WINDOWEVENT_RESIZED) {
            repaint = true;
            }
            break;
        };
        /* TODO: check time stamps to see if we've spent too much real
         * time processing events. Doing that would be more robust than
         * counting tries. */
        } while (tries-- && SDL_PollEvent(&event));
    }
    /* paint once we're done processing potentially spammy events */
    /* TODO: we could skip painting if we know the scene has not changed.
     * This would not likely happen in a game, due to animation.
     * In a utility that only reacts to user events, we'd want to change
     * this loop to use SDL_WaitEvent() instead.
     */
    paint();
    }
    SDL_Quit();
}

Unfortunately using a compositing window manager on Linux hides a lot of the funny behavior of X11. Trying with something like twm or fvwm as well as your favorite modern WM can narrow down issues like these.

\$\endgroup\$
  • 1
    \$\begingroup\$ Great point about the window manager. Awesome WM seems to be a little more spammy than others. The problem ist practically non-existent in xfce for example. I also tried on windows (64bit mingw-w64) and there the resize event is not delivered until the mouse is released (at least in my tests on Win10). Just for testing (obviously not the solution), I modified the code to only repaint after WaitEventTimeout really timed out and that helps reduce the lag. Thanks for the input, that's enough info to flesh out a solution. \$\endgroup\$ – cptG Jun 23 '16 at 18:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.