SDL event loop CPU usage


#1

Hello,

Is it possible to organize event loop so that it consumes 0% CPU when there are no events?
Currently I’m doing it like this:

#include <SDL2/SDL.h>

#define WIN_H 300
#define WIN_W 400

enum error {
    EINIT = 1,
    EWINDOW,
    ERENDER,
};

int main(int argc, char* argv[]) {
    SDL_Window*   window   = NULL;
    SDL_Renderer* renderer = NULL;

    int status = 0;

    do {
        if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
            status = EINIT;
            break;
        }

        window = SDL_CreateWindow(
            "Demo EventLoop",
            SDL_WINDOWPOS_CENTERED,
            SDL_WINDOWPOS_CENTERED,
            WIN_W,
            WIN_H,
            SDL_WINDOW_RESIZABLE);

        if (!window) {
            status = EWINDOW;
            break;
        }

        renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);

        if (!renderer) {
            status = ERENDER;
            break;
        }

        SDL_Event e;
        while (SDL_WaitEvent(&e)) {
            if (e.type == SDL_QUIT) {
                break;
            }
            SDL_Delay(50);
        }
    } while (0);

    if (renderer) {
        SDL_DestroyRenderer(renderer);
    }

    if (window) {
        SDL_DestroyWindow(window);
    }

    SDL_Quit();

    return status;
}

This demo application consumes 1.8-2.0% CPU when there are no events. Seems like I’m doing something wrong. I would be very appreciated for advice about how to do the event loop right.

Vladimir


#2

If you do not create games, I think you should use a system API like WhiteForSingleObject(s) for Windows
And make your own event handler.


#3

FIX: WaitForSingleObject(s)


#4

PS: In your separete thread window handler


#5

That doesn’t seem wrong to me. SDL_WaitEvent still needs to do some work to actually check if there are events. Maybe there are some optimizations you could make if you made a custom event solution like ANTA suggested, but you’ll need to evaluate if the effort of making the custom solution is worth 1-2% CPU usage.


#6

Hello, thank You for the reply!
I’m creating a terminal emulator (just for fun) for UNIX (OS X/Linux) and I tried to use SDL_CondWait (with SDL_CondSignal in SDL_AddEventWatch) in main loop, but in that case application is displayed as “not responding”.


#7

I think 1-2% CPU is high consumption for an idle application. On laptops it can cause additional battery usage. As I could see in Activity Monitor, normally an idle application consumes 0% CPU.


#8

SDL_WaitEvent polls for events in a loop with SDL_Delay(10) in between to keep cpu usage down. It doesn’t actually make your app go idle and use something like a wait handle. So that explains the cpu usage.


#9

Thank You!
Yes, it explains. But is it possible to reduce CPU usage? Additional SDL_Delay reduces CPU usage, bot not to 0 and application becomes not so responsive. Solution with wait/signal a condition variable requires additional initialized thread which will listen for events actually by the same manner like in the main loop, so it seems like I will get 1-2% CPU usage, not from main thread but from a background thread. I noticed I nice functions SDL_AddEventWatch/SDL_SetEventFilter but seems like they are called from the same thread (at least if the following code does not contain a critical error):

#include <stdio.h>

#include <SDL2/SDL.h>

#define WIN_H 300
#define WIN_W 400

enum error {
    EINIT = 1,
    EWINDOW,
    ERENDER,
    EMUTEX,
    ECOND,
};

typedef struct {
    SDL_mutex* mutex;
    SDL_cond*  cond;
} condition_t;

int onevent(void* userdata, SDL_Event* event) {
    condition_t* c = (condition_t*)userdata;
    if (c) {
        printf("[%ld] %s\n", SDL_ThreadID(), "signaling event ...");
        SDL_LockMutex(c->mutex);
        SDL_CondSignal(c->cond);
        SDL_UnlockMutex(c->mutex);
        printf("[%ld] %s\n", SDL_ThreadID(), "signaling event ok");
    }
    return 1;
}

void render(SDL_Renderer* renderer, SDL_Surface* screen) {
    SDL_Texture* scene = SDL_CreateTextureFromSurface(renderer, screen);

    SDL_RenderClear(renderer);
    SDL_RenderCopy(renderer, scene, NULL, NULL);
    SDL_RenderPresent(renderer);

    SDL_DestroyTexture(scene);
}

int main(int argc, char* argv[]) {
    SDL_Window*   window   = NULL;
    SDL_Renderer* renderer = NULL;
    SDL_Surface*  screen   = NULL;
    SDL_mutex*    mutex    = NULL;
    SDL_cond*     cond     = NULL;

    int status = 0;

    do {
        if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
            status = EINIT;
            break;
        }

        window = SDL_CreateWindow(
            "Demo EventLoop",
            SDL_WINDOWPOS_CENTERED,
            SDL_WINDOWPOS_CENTERED,
            WIN_W,
            WIN_H,
            SDL_WINDOW_RESIZABLE);

        if (!window) {
            status = EWINDOW;
            break;
        }

        renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);

        if (!renderer) {
            status = ERENDER;
            break;
        }

        screen = SDL_CreateRGBSurface(0, WIN_W, WIN_H, 32, 0, 0, 0, 0);
        if (!screen) {
            break;
        }

        SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));

        mutex = SDL_CreateMutex();
        if (!mutex) {
            status = EMUTEX;
            break;
        }

        cond = SDL_CreateCond();
        if (!cond) {
            status = ECOND;
            break;
        }

        condition_t c = { .mutex = mutex, cond = cond };
        // SDL_SetEventFilter(onevent, &c);
        SDL_AddEventWatch(onevent, &c);

        SDL_LockMutex(mutex);

        render(renderer, screen);
        while (1) {

            SDL_Event e;
            if (SDL_PollEvent(&e)) {
                if (e.type == SDL_QUIT) {
                    break;
                }
            } else {
                printf("[%ld] %s\n", SDL_ThreadID(), "waiting for event ...");
                SDL_CondWaitTimeout(cond, mutex, 100);
                printf("[%ld] %s\n", SDL_ThreadID(), "waiting for event ok");
            }

            render(renderer, screen);
        }
        SDL_UnlockMutex(mutex);

        // SDL_Event e;
        // while (SDL_WaitEvent(&e)) {
        //     if (e.type == SDL_QUIT) {
        //         break;
        //     }
        //     SDL_Delay(50);
        // }
    } while (0);

    if (cond) {
        SDL_DestroyCond(cond);
    }

    if (mutex) {
        SDL_DestroyMutex(mutex);
    }

    if (screen) {
        SDL_FreeSurface(screen);
    }

    if (renderer) {
        SDL_DestroyRenderer(renderer);
    }

    if (window) {
        SDL_DestroyWindow(window);
    }

    SDL_Quit();

    return status;
}