0
\$\begingroup\$

When I wrote my first application in SDL, it looked like this:

while (!quit)
{
    SDL_PollEvent(&event);

    switch (event)
    {
         // ...
    }
}

But then one time I left my app running while I went to do something else, and when I came back my laptop was boiling hot. I checked the system monitor and one of my cores was maxed out by my program! So I did some research, and now my loop looks like this:

while (!quit)
{
    SDL_WaitEvent(&event);

    switch (event)
    {
         // ...
    }
}

Great, so now my program doesn't max the CPU. But now I need to introduce rendering, and the problem is, currently this loop runs one iteration per event. If input and other events don't occur, my program won't render the next frame, no matter where the rendering code goes in the loop. So that's no good.

I could have threads, but that seems like a lot of extra complexity when I could just do:

while (!quit)
{
    SDL_PollEvent(&event);

    switch (event)
    {
         // ...
    }

    SDL_Delay(10)
}

This won't melt the CPU, and I can render at about 100 frames per second.

My questions are:

  1. Which approach "should" I use? I don't see any reason to not just use (3).

  2. What's the point of SDL_WaitEvent, if you can't use it the moment you want to do anything other than handle events in your game loop?

  3. I assume SDL_WaitEvent does something like approach (3) under the hood, so even though ostensibly approach (3) should introduce more latency, in practice it won't. Is this correct? How low should the integer passed to SDL_Delay be to get a responsive app?

\$\endgroup\$
3
\$\begingroup\$

That's an interesting problem, I handle my SDL events like this,

while (SDL_PollEvent(&e) != 0) 
{
    // handle events here
}

This will handle all the SDL events in the queue, and if their aren't any events in the queue it just ignores trying to handle the events in the queue.

P.S. at the end of each iteration of my game loop (The handle user input, update game world, render world) I just delay the loop so that it won't iterate more than 60 times a second.

// Timing variables
const int FPS = 60;
const int frameDelay = 1000 / FPS;
Uint32 frameStart;
int frameTime;

// Game Loop
while(playing)
{
    // Stores the number of ticks at the start of the loop
    frameStart = SDL_GetTicks();
    
    // HandleEvents
    // Update World
    // Render World

    // This measures how long this iteration of the loop took
    frameTime = SDL_GetTicks() - frameStart;

    // This keeps us from displaying more frames than 60/Second
    if(frameDelay > frameTime)
    {
        SDL_Delay(frameDelay - frameTime);
    }
}
    
     
\$\endgroup\$
1
\$\begingroup\$

LazyFoo's Tutorials use SDL_PollEvent in within it's own loop inside the main loop, to ensure all events are handled and not only the 1st one.

The main loop would look like this:

//Main loop flag
bool quit = false;

//Event handler
SDL_Event e;

//While application is running
while( !quit )
{
    //Handle events on queue
    while( SDL_PollEvent( &e ) != 0 )
    {
        //User requests quit
        if( e.type == SDL_QUIT )
        {
            quit = true;
        }
    }

    // everything else
}

You might also like to take a look at the Calculating framerate and Capping Framerate tutorials.

\$\endgroup\$
1
\$\begingroup\$

As you pointed out, SDL_WaitEvent is not suitable if you need to draw even if there are no input events. That is not desirable for games. But it could be useful, for example, if you have an application that only does UI, because you only need to refresh when the user interacts with the UI.

For games, very often we use a technique called VSync. This technique will make your loop refresh at the same rate as your monitor. So if your monitor has a refresh rate of 60Hz, the loop will match that. Additionally, it will prevent screen tearing artifacts.

In SDL you can enable VSync in different ways depending on what you are doing:

  1. If you are using SDL's renderers, you can use the SDL_RENDERER_PRESENTVSYNC flag.
  2. If you are using OpenGL you can call SDL_GL_SetSwapInterval(1).

Alternativelly, you can use SDL_Delay. But, instead of waiting a fixed amount of time, you should wait depending on how much time you have spent doing computations, in order to keep a constant frame time. This approach is what @sketBURD 's answer is about.

\$\endgroup\$

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.