Sdl Smooth Scrolling Tips?


Miner49er

Active Member
Joined
Mar 1, 2004
Messages
655
Age
49
Website
www.lessermatters.co.uk
Hi,

I recently added a scrolling message to my program, it's very simple, just displays a load of 32x32 graphics (about 50), and scrolls them on.
My problem, is that although I'm SDL_Delaying for the time it takes everything to draw minus 1000/desiredfps, i.e. it's all in time, every second or so the scrolling jitters (due to the time it takes everything to draw becoming larger than allowed).

It seems obvious that some other process is running is affecting my game. I have built it on windows at work and see exactly the same thing (I use linux at home).

I must be missing something major as I play games that don't do this.

Is my program REALLY inefficient? It doesn't do anything at this point!

Any ideas from you more experienced game coders?
 
1. You might be using an inaccurate timer. I've seen that the "normal" timer in for example Windows has an error margin of 15 milliseconds, which screws up your FPS. Might be the same problem in Linux, don't know. Try to find a better timer that has a higher resolution.

2. You might benefit from creating an average that you use when calculating your frame delta time, especially if you can't get access to a high resolution timer. Take the last 10 frames or so, and measure how long it took to process them. Divide by 10 and you have the delta time for one frame. Use that and it should reduce the effect of sudden speed changes.
 
If rendering is taking too long, then that has little to do with scrolling per se (unless you mean that during a scroll you have to render _Everything_, and normally you're only rendering those ibjects that have changes apparent, in which cas3e yeah, I'm with you.) How about staggering the rendering .. ie: A common thing is for you to render when your frame is up, but consider.. if you've got a frame, and some time, and then a frame, and then some time .. it means you're doing virtually nothing _between_ renders, andf then bursting all your workload _at_ render time.

Consider . if you are double-buffering (say), then you can render off-screen for severeral units of time and then when it comes to showing a frame, you've got one queued up all ready to go. Likewise, rendering the offscreen objects in advance.

Or rendering to a virtual screen that is larger than your real screen, and shifting the visible window over and along it.

Depends on the platformn and how your API and hardware limit you etc, but maybe this will get your creativity going :)

jeff
 
This is all very useful - thanks :)

I am, in fact, blitting an entire screen image every refresh as well as double buffering but the resolution I'm at is 320x240 - I would have thought my pc could handle that!
During the game I'm just blitting one image (screen size) and several very small sprites.

Do you think it's worth bothering with only drawing differences? I don't...

Anyway, cheers for that, I really appreciate it :)
 
I'm doing it this way: in main loop I compute the "lag" (time it took to render last frame) and then multiply speed of movement of any object by the lag.
This gives me the movement delta.

Code:
    int oldticks = SDL_GetTicks();
    while(!done)
    {
        lag = (double)(SDL_GetTicks() - oldticks) / 1000.0;
        oldticks = SDL_GetTicks();
        double deltaX = speedXofSomeObject * lag;
        object.X += deltaX;
        
    }

You can place SDL_Delay() in the loop so you can limit the frames. You can calculate the value of the delay need to maintain a desired framerate.
 
Main loop and time wasting function from Star Hustler, has a ticker running across the bottom, not so smooth but it's fairly consistent. Source is in a source.zip included in hustle-1.0.zip.
Code:
unsigned long Now;
short rate = 55; // frameage 1000/18

unsigned int timeleft(unsigned int x)
{
    static unsigned int next_time = 0;

    Now = SDL_GetTicks();
    if ( next_time <= Now ) 
    {   
        next_time = Now + x;
        return( 0 );
    }
    return( next_time - Now );
}

       
       // main loop
              while( guy.turns )
              {
                  draw();
                  SDL_Flip( Screen );
                  if ( doevents() )
                      guy.turns = 0;
                  SDL_Delay( timeleft( rate ));
              }
 
I'm just rephrasing what Skeezix said, but if you are using a main loop like:
Code:
loop:
  // this is where you actually would like the frame to show up
  erase() 
  update()
  draw()
  // this is where it is shown
  wait()

you'll get small differences to draw times depending on how long the erase-update-draw takes. If, however you use double-buffering like:

Code:
loop:
  eraseDbuf()
  update()
  drawDbuf()
  wait()
  // this is where you actually would like the frame to show up
  flip()
  // this is where it is shown

the difference will be much smaller, because flipping is a fast operation.

EDIT: comments
 
Thanks again everyone.

Well, it seems that I'm doing everything correctly. Flipping, doubel buffering, waiting a small amount oftime dependent on the time it takes to draw a frame etc.

The only thing I havn't tried is skeezix's idea of buffering up extra frames in any remaining space.

I do find that scrolling with PC's is a little rubbish anyway and some googling seems to confirm that if I'm looking for that 'Arcade Perfect' scroll effect then I'm out of luck. I understand this has something to do with screen syncronisation (tearing) and I'm guessing all the other processes working in the background.

Will the Pandora suffer the same thing or will games have a higher priority (re: processes) and what about screen syncing?
 
One thing SDL does that it doesnt tell you - say the text is 16 bit and the buffer you are rendering to is 32 bit, SDL will convert everything from 16 to 32 bit EVERY TIME you write to the buffer. This is *very* slow.

In short - make sure your text and buffer have the same bit-depth.
 
Hmmm, I think I've covered that by calling this function:

SDL_DisplayFormat

which according to the comment:

/*
* This function takes a surface and copies it to a new surface of the
* pixel format and colors of the video framebuffer, suitable for fast
* blitting onto the display surface. It calls SDL_ConvertSurface()
*
* If you want to take advantage of hardware colorkey or alpha blit
* acceleration, you should set the colorkey and alpha value before
* calling this function.
*
* If the conversion fails or runs out of memory, it returns NULL
*/
 
dsh said:
I believe that if you could post your main loop's code that would allow us to help you more.

Code:
	while(!exitProgram)
	{
		fps.start();

                if(menu->screenMode == PlayingGame) 
			gameLoop();//this checks input and animates one frame
		else
			menuLoop();//this checks input and animates one frame

		if(menu->screenMode == PlayingGame) 
			game->Draw(screen);
		else 	
		 	menu->Draw(screen);
		
		SDL_Flip(screen);

		diff = (1000 / updateFPS) - fps.GetTicks();
		if( diff > 0 )
			SDL_Delay( diff );
	}

Edited: Because I pasted a load of crap by accident. fps class just records the number of ticks (GetTicks return the time passed since Start)
 
Last edited by a moderator:
Miner49er said:
dsh said:
I believe that if you could post your main loop's code that would allow us to help you more.

Code:
	while(!exitProgram)
	{
		fps.start();

                if(menu->screenMode == PlayingGame) 
			gameLoop();//this checks input and animates one frame
		else
			menuLoop();//this checks input and animates one frame

		if(menu->screenMode == PlayingGame) 
			game->Draw(screen);
		else 	
		 	menu->Draw(screen);
		
		SDL_Flip(screen);

		diff = (1000 / updateFPS) - fps.GetTicks();
		if( diff > 0 )
			SDL_Delay( diff );
	}

Edited: Because I pasted a load of crap by accident. fps class just records the number of ticks (GetTicks return the time passed since Start)

I don't see any problems here. Probably the frames take too long to draw. Did you check how much time does it take to draw one frame?

You may also check if you haven't got any float/int implicit conversions taking place. It's a longshot but it's easy to miss and causes strange errors.

Also, how do you store coordinates? They shouldn't be ints, the conversion to int should be just before drawing. Rounding can cause laggy movement.
 
Last edited by a moderator:
dsh said:
Miner49er said:
dsh said:
I believe that if you could post your main loop's code that would allow us to help you more.

Code:
	while(!exitProgram)
	{
		fps.start();

                if(menu->screenMode == PlayingGame) 
			gameLoop();//this checks input and animates one frame
		else
			menuLoop();//this checks input and animates one frame

		if(menu->screenMode == PlayingGame) 
			game->Draw(screen);
		else 	
		 	menu->Draw(screen);
		
		SDL_Flip(screen);

		diff = (1000 / updateFPS) - fps.GetTicks();
		if( diff > 0 )
			SDL_Delay( diff );
	}

Edited: Because I pasted a load of crap by accident. fps class just records the number of ticks (GetTicks return the time passed since Start)

I don't see any problems here. Probably the frames take too long to draw. Did you check how much time does it take to draw one frame?

You may also check if you haven't got any float/int implicit conversions taking place. It's a longshot but it's easy to miss and causes strange errors.

Also, how do you store coordinates? They shouldn't be ints, the conversion to int should be just before drawing. Rounding can cause laggy movement.

Hmmm, well as I explained before all menuLoop bit does is blit a background image and display about 50 16x16 images. So it's not doing a great deal.
Thing is, I seem to recall doing a scrolling message using AMOS basic about 20 years ago and getting PERFECT scrolling, no jitter, no worries. Now, I did have a fixed delay then as my brain hadn't figured out the variable-delay-dependant-on-draw-time bit but still... Of course my Amiga would've just had the AMOS process running, so no background threads/processes to cause jitter. This, I think, is the problem I'm encountering.

Also, the co-ords as ints comment: I'm using a 1 to 1 co-ord to pixel ratio, so I don't understand. I guess I could scale the scroller position up, move a fraction then scale down to draw...but I'm scrolling it along 1 pixel at a time right now, so I dont see the benefit.
 
Last edited by a moderator:
Miner49er said:
dsh said:
Miner49er said:
dsh said:
I believe that if you could post your main loop's code that would allow us to help you more.

Code:
	while(!exitProgram)
	{
		fps.start();

                if(menu->screenMode == PlayingGame) 
			gameLoop();//this checks input and animates one frame
		else
			menuLoop();//this checks input and animates one frame

		if(menu->screenMode == PlayingGame) 
			game->Draw(screen);
		else 	
		 	menu->Draw(screen);
		
		SDL_Flip(screen);

		diff = (1000 / updateFPS) - fps.GetTicks();
		if( diff > 0 )
			SDL_Delay( diff );
	}

Edited: Because I pasted a load of crap by accident. fps class just records the number of ticks (GetTicks return the time passed since Start)

I don't see any problems here. Probably the frames take too long to draw. Did you check how much time does it take to draw one frame?

You may also check if you haven't got any float/int implicit conversions taking place. It's a longshot but it's easy to miss and causes strange errors.

Also, how do you store coordinates? They shouldn't be ints, the conversion to int should be just before drawing. Rounding can cause laggy movement.

Hmmm, well as I explained before all menuLoop bit does is blit a background image and display about 50 16x16 images. So it's not doing a great deal.
Thing is, I seem to recall doing a scrolling message using AMOS basic about 20 years ago and getting PERFECT scrolling, no jitter, no worries. Now, I did have a fixed delay then as my brain hadn't figured out the variable-delay-dependant-on-draw-time bit but still... Of course my Amiga would've just had the AMOS process running, so no background threads/processes to cause jitter. This, I think, is the problem I'm encountering.

Also, the co-ords as ints comment: I'm using a 1 to 1 co-ord to pixel ratio, so I don't understand. I guess I could scale the scroller position up, move a fraction then scale down to draw...but I'm scrolling it along 1 pixel at a time right now, so I dont see the benefit.

If the time needed to draw the frame is low as you say then I highly doubt that any applications running in the background could cause that (if you're not running movie encoding or something very, very resource intensive). The priority and scheduling tricks are reserved for extremely low latency applications which do something hundreds of thousands times a second or so - that's when the system scheduler can cause jitter, not in your case. I had to use this tricks when I was doing RGB LED PWM modulation by LPT port. The modulation pulses where generated by the CPU...

Just check your CPU load graph.
 
Last edited by a moderator:
dsh said:
Miner49er said:
dsh said:
Miner49er said:
dsh said:
I believe that if you could post your main loop's code that would allow us to help you more.

Code:
	while(!exitProgram)
	{
		fps.start();

                if(menu->screenMode == PlayingGame) 
			gameLoop();//this checks input and animates one frame
		else
			menuLoop();//this checks input and animates one frame

		if(menu->screenMode == PlayingGame) 
			game->Draw(screen);
		else 	
		 	menu->Draw(screen);
		
		SDL_Flip(screen);

		diff = (1000 / updateFPS) - fps.GetTicks();
		if( diff > 0 )
			SDL_Delay( diff );
	}

Edited: Because I pasted a load of crap by accident. fps class just records the number of ticks (GetTicks return the time passed since Start)

I don't see any problems here. Probably the frames take too long to draw. Did you check how much time does it take to draw one frame?

You may also check if you haven't got any float/int implicit conversions taking place. It's a longshot but it's easy to miss and causes strange errors.

Also, how do you store coordinates? They shouldn't be ints, the conversion to int should be just before drawing. Rounding can cause laggy movement.

Hmmm, well as I explained before all menuLoop bit does is blit a background image and display about 50 16x16 images. So it's not doing a great deal.
Thing is, I seem to recall doing a scrolling message using AMOS basic about 20 years ago and getting PERFECT scrolling, no jitter, no worries. Now, I did have a fixed delay then as my brain hadn't figured out the variable-delay-dependant-on-draw-time bit but still... Of course my Amiga would've just had the AMOS process running, so no background threads/processes to cause jitter. This, I think, is the problem I'm encountering.

Also, the co-ords as ints comment: I'm using a 1 to 1 co-ord to pixel ratio, so I don't understand. I guess I could scale the scroller position up, move a fraction then scale down to draw...but I'm scrolling it along 1 pixel at a time right now, so I dont see the benefit.

If the time needed to draw the frame is low as you say then I highly doubt that any applications running in the background could cause that (if you're not running movie encoding or something very, very resource intensive). The priority and scheduling tricks are reserved for extremely low latency applications which do something hundreds of thousands times a second or so - that's when the system scheduler can cause jitter, not in your case. I had to use this tricks when I was doing RGB LED PWM modulation by LPT port. The modulation pulses where generated by the CPU...

Just check your CPU load graph.

Running here at work (on windows Vista, ughh), it's using 3% CPU on a 3GigHz machine! It's not running too fast for the Delay but STILL it jitters. I reckon this can only mean that the SDL_GetTicks() isn't accurate enough. Is it worth trying a higher resolution timer?
 
Last edited by a moderator:
There is no way that would tax a reasonably modern PC.

On the GP2X SDL_Flip waits for the vertical blank. I'm not sure it does on Windows. No idea about Linux.
In saying that I've never had a problem on Windows with jittering or tearing.

The only other thing I can think of trying is to use SDL_HWSURFACE if you're not already.

The only thing I do different from your example is I call SDL_Flip at the very end of the loop. That wouldn't make any difference though, I don't think.

*edit* No I don't. Here's my wait loop:
Code:
SDL_Flip(screen);
		
	    //Cap the frame rate
        while( fps.get_ticks() < 1000 / FRAMES_PER_SECOND )
        {
            SDL_Delay(1);
        }
 
Unfathomable Depths said:
There is no way that would tax a reasonably modern PC.

On the GP2X SDL_Flip waits for the vertical blank. I'm not sure it does on Windows. No idea about Linux.
In saying that I've never had a problem on Windows with jittering or tearing.

The only other thing I can think of trying is to use SDL_HWSURFACE if you're not already.

The only thing I do different from your example is I call SDL_Flip at the very end of the loop. That wouldn't make any difference though, I don't think.

*edit* No I don't. Here's my wait loop:
Code:
SDL_Flip(screen);
		
	    //Cap the frame rate
        while( fps.get_ticks() < 1000 / FRAMES_PER_SECOND )
        {
            SDL_Delay(1);
        }

Well, My collegue here at work thinks that It looks fine. I know it doesn't look 100% but maybe I should move on for now. perhaps I can get some feedback once I've finished.
 
Last edited by a moderator:
Something like this is a common approach:

while ( application_running )
{
RenderEverythingFromLastFrame( );

UpdateAllMyGameObjects( );

BuildUpEverythingThatNeedsRenderingThisFrame( );

WaitForCurrentRenderToFinish( );

OptionallyWaitForVSync( );
}

The idea being you start the render for the previous frame straight away, if you are using a graphics API that blocks then it would need to be done on a seperate render thread (if you are using one that doesn't block and just internally buffers the commands this isn't neccessarily). While the previous frame is rendering you can do all your game logic, and build up the next frames render calls - obviously you have to make sure anything that gets used by the render is double buffered (so you aren't changing values of data currently being drawn). Once you have done everything you need to, you just wait for the draw to finish, vsync if desired, and then start again (flipping any double buffered stuff you are using). With this approach the graphics/processing can run in parallel with each other, with a loop more like:

while ( application_running )
{
UpdateAllMyGameObjects( );

BuildUpEverythingThatNeedsRenderingThisFrame( );

RenderEverythingForThisFrame( );

WaitForCurrentRenderToFinish( );

OptionallyWaitForVSync( );
}

Your render can't even start until all your processing is done, then once you start rendering you have to wait for it to finish before you can start the next frames processing.

Maybe this doesn't help you so much, but it is what I'd do anyway!

Steve
 
And the base of it it all is.. from your description and window size, there is no way it should be too much load for anything :) On a 100mhz GP32 I was trendering a full screen background and 50 or 100 little crappy sprites without slowing it down much (20fps or so) .. There must be somethign very unexpected going on for it to slow down any modern machine.

(ie: Like all your artwork is being scaled and rotated and colour-shifted every render, or you have an audio thread kicking in and consumingall your cpu or something :) Are you playing mp3s in background?

jeff
 
Back
Top