Sign in to follow this  
Mybowlcut

[SDL] Game lagging on input events

Recommended Posts

I'm having a bit of trouble with my game lagging. It happens only when there are excessive input events (like moving the mouse around quickly). Here is my game loop:
void Game::Run()
{
    bool is_event = false;
    while(!exit)
    { // While the user hasn't quit...
        while(SDL_PollEvent(&sdl_event) != 0)
	{ // There is an SDL event to handle.
	    if(sdl_event.type == SDL_QUIT)
	    { // If the user has Xed out
		exit = true;
		break;
	    }
			
            Update_And_Render(&sdl_event, game_timer.Get_Delta());
        }
    Update_And_Render(NULL, game_timer.Get_Delta());
    }
}






Update_And_Render:
void Game::Update_And_Render(const SDL_Event* event_, double delta)
{
    game_timer.Get_Delta();
    active_screen->Update(event_);
    if(event_) LOG("active_screen->Update() took " + Conversions::to_string(game_timer.Get_Delta()) + " seconds.h");

    active_screen->Render(renderer);
    if(event_) LOG("active_screen->Render() took " + Conversions::to_string(game_timer.Get_Delta()) + " seconds.");

    dialogue_manager.Update(event_);
    dialogue_manager.Render(renderer);

    player_controller.Update(delta);
    player_controller.Update(event_);

    game_timer.Get_Delta();
    level_manager.Update(delta);
    if(event_) LOG("level->Update(d) took " + Conversions::to_string(game_timer.Get_Delta()) + " seconds.");

    level_manager.Update(event_, *player_controller.Get_Player());
    if(event_) LOG("level->Update(e) took " + Conversions::to_string(game_timer.Get_Delta()) + " seconds.");

    level_manager.Render(renderer);
    if(event_) LOG("level->Render() took " + Conversions::to_string(game_timer.Get_Delta()) + " seconds.");

    player_view.Update(delta);
    player_view.Render(renderer, *level_manager.Get_Camera());

    renderer.Draw_Screen();
}






Here is an example of the logging output for one frame:
Wed Jul 22 08:55:26: level->Update(d) took 1.66948e-005 seconds.
Wed Jul 22 08:55:26: level->Update(e) took 0.00102579 seconds.
Wed Jul 22 08:55:26: level->Render() took 0.0452895 seconds.
Wed Jul 22 08:55:26: active_screen->Update() took 9.13991e-006 seconds.h
Wed Jul 22 08:55:26: active_screen->Render() took 0.00123682 seconds.



From the logging that I've done, it seems that the rough average for:
  • level->Update(delta) is 1.6 seconds
  • level->Update(event) is 0.001 seconds
  • level->Render() is 0.046 seconds
  • active_screen->Render() is 0.0012 seconds
  • active_screen->Update() is 5.45 seconds (anywhere from 1.0 seconds to 9.9 seconds!)
The only problem with these results is that the active_screen only has one GUI element that is loaded from XML once at the beginning of the program:
<?xml version="1.0" ?>
<screen name="game_screen">
    <images>
        <image name="game_bg" file_name="GUI\i_bg.png" pos_x="0" pos_y="0" is_colour_key="0" depth="0" />
    </images>
</screen>


And hence only one GUI element is being updated in Screen's Update function:
virtual void Update(const SDL_Event* event_)
{
    update_each_ptr<gui_object_it>(screen_objects.begin(), screen_objects.end(), event_);
}


But, wait for it...
void Image::Update(const SDL_Event* event_)
{
    // Does nothing...
}




So I can't imagine why it would take 9 seconds to update one image. As for level->Update(d) taking 1.6 seconds on average, that is understandable - perhaps if there wasn't only one animated tile being updated:
void Level::Update(double delta)
{
    update_each_ptr_safe(
        clipped_anim_bg_tiles.begin(),
        clipped_anim_bg_tiles.end(),
        delta);

    update_each_ptr_safe(
        clipped_anim_object_tiles.begin(),
            clipped_anim_object_tiles.end(),
            delta);

    update_each_ptr_safe(
        clipped_anim_fg_tiles.begin(),
        clipped_anim_fg_tiles.end(),
        delta);
}


When each animated tile is updated, this gets called:
void Tile::Update(double delta)
{
    if(animated)
    {
        if(animator.Update())
        { // Change frame.
            clip.y = SDL_Tools::calculate_clip_y(animator.Get_Frame(), clip.h);
        }
    }
}


Which calls this:
bool SDL_Timed_Animator::Update()
{
    if(frame_delay.Finished())
    {
        frame_delay.Start();
        Advance();
        return true;
    }
    return false;
}

Which calls this (when frame_delay.Finished()):
void Animator::Advance()
{
    if(wrap)
    { // Handle wrap.
        frame += frame == qty_frames - 1 ? -(qty_frames - 1) : 1;
    }
    else
    { // Handle rebound.
        if(progression == FORWARD)
        { // Frame reached last frame, rebound.
            if(frame == qty_frames - 1)
            {
                progression = BACKWARD;
            }
        }
        else
        { // Frame reached first frame, rebound.
            if(frame == 0)
            {
                progression = FORWARD;
            }
        }
        // Keep going in whatever current direction is.
        frame += progression;
    }
}

I can post more code up if it is required. Cheers. [Edited by - Mybowlcut on July 21, 2009 7:52:17 PM]

Share this post


Link to post
Share on other sites
You shouldn't be updating your Update_And_Render() function 5 times when there are 4 sdl_events. In fact, Update(&sdl_event) and Render(game_timer.Get_Delta()) should be separate functions. That way when there are 50 events queued in one frame of animation it won't take 51 frames of animation to process them all.

Does this make sense to you?

-edit-
Here's what your Game::Run() should look like:


void Game::Run()
{
bool exit=false;
bool is_event = false;
while(!exit)
{ // While the user hasn't quit...
while(SDL_PollEvent(&sdl_event) != 0)
{ // There is an SDL event to handle.
if(sdl_event.type == SDL_QUIT)
{ // If the user has Xed out
exit = true;
break;
}

Update(&sdl_event);
}
Render(game_timer.Get_Delta());
}
}


Share this post


Link to post
Share on other sites
Quote:
Original post by samuraicrow
You shouldn't be updating your Update_And_Render() function 5 times when there are 4 sdl_events. In fact, Update(&sdl_event) and Render(game_timer.Get_Delta()) should be separate functions. That way when there are 50 events queued in one frame of animation it won't take 51 frames of animation to process them all.

Does this make sense to you?

-edit-
Here's what your Game::Run() should look like:

*** Source Snippet Removed ***
Hey thanks for the reply.

That makes sense to me, but I'm worried that it will mean that an influx of events will see lags in rendering... I'll try it out now. Oh, and why do you pass delta to the render functions? Is there an implied suggestion there or is it just a mistake?

Edit: I tried it out and it seems to work great! Thanks so much! This was one of those issues that I was going to leave for ages because I thought the solution would involve huge amounts of debugging and crap haha.

This is what I've got now:
void Game::Run()
{
bool is_event = false;
while(!exit)
{ // While the user hasn't quit...
while(SDL_PollEvent(&sdl_event) != 0)
{ // There is an SDL event to handle.
if(sdl_event.type == SDL_QUIT)
{ // If the user has Xed out
exit = true;
break;
}

Event_Update(&sdl_event);
}
Delta_Update(game_timer.Get_Delta());
Render();
}
}

void Game::Event_Update(const SDL_Event* event_)
{
if(event_->type == SDL_KEYDOWN && !SEM.Is_Focus())
{
if(event_->key.keysym.sym == SDLK_ESCAPE)
{
Exit();
}
else if(event_->key.keysym.sym == SDLK_i)
{
if(active_screen != &inventory_gui)
{
active_screen = &inventory_gui;
}
else
{
active_screen = &game_screen;
}
}
else if(event_->key.keysym.sym == SDLK_m)
{
int vol = SDL_Audio_Player::Get().Get_Global_Volume();
SDL_Audio_Player::Get().Set_Global_Volume(vol == 0 ? 100 : 0);
}
}

active_screen->Update(event_);
dialogue_manager.Update(event_);
player_controller.Update(event_);
level_manager.Update(event_, *player_controller.Get_Player());
}

void Game::Delta_Update(double delta)
{
player_controller.Update(delta);
level_manager.Update(delta);
player_view.Update(delta);
}

void Game::Render()
{
active_screen->Render(renderer);
dialogue_manager.Render(renderer);
level_manager.Render(renderer);
player_view.Render(renderer, *level_manager.Get_Camera());
renderer.Draw_Screen();
}



[Edited by - Mybowlcut on July 21, 2009 9:29:35 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this