Jump to content


Photo

Sdl Smooth Scrolling Tips?


  • Please log in to reply
36 replies to this topic

#1 Miner49er

Miner49er

    GP Mania

  • GP32 Hardcore
  • PipPipPipPipPip
  • 403 posts
  • Gender:Male

Posted 13 October 2009 - 07:55 PM

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?

#2 dflemstr

dflemstr

    It's a ball.

  • GP32 Hardcore
  • PipPipPipPipPipPip
  • 2249 posts
  • Gender:Male
  • Location:Stockholm, Sweden

Posted 13 October 2009 - 08:00 PM

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.

Edited by dflemstr, 13 October 2009 - 08:01 PM.


#3 skeezix

skeezix

    Mega GP Mania

  • GP Guru
  • 5088 posts
  • Gender:Male
  • Interests:Blog: http://www.rjmitchell.ca/~jeff/blog2009/

Posted 13 October 2009 - 08:40 PM

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

#4 Miner49er

Miner49er

    GP Mania

  • GP32 Hardcore
  • PipPipPipPipPip
  • 403 posts
  • Gender:Male

Posted 13 October 2009 - 09:18 PM

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 :-)

#5 dsh

dsh

    GP32 User

  • Members
  • PipPipPip
  • 52 posts
  • Gender:Male
  • Location:Cracow, Poland

Posted 13 October 2009 - 11:08 PM

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.

    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.

Edited by dsh, 14 October 2009 - 10:13 AM.


#6 Sphinxter

Sphinxter

    Says What?

  • GP32 Hardcore
  • PipPipPipPipPipPip
  • 2831 posts
  • Gender:Male
  • Location:Silicon Valley California, USA

Posted 14 October 2009 - 12:40 AM

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.

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 ));
              }

Edited by Sphinxter, 14 October 2009 - 12:54 AM.


#7 B-ZaR

B-ZaR

    A Commando

  • GP32 Hardcore
  • PipPipPipPipPipPip
  • 1397 posts
  • Gender:Male
  • Location:Finland

Posted 14 October 2009 - 07:18 AM

I'm just rephrasing what Skeezix said, but if you are using a main loop like:
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:

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

Edited by B-ZaR, 14 October 2009 - 07:21 AM.


#8 Miner49er

Miner49er

    GP Mania

  • GP32 Hardcore
  • PipPipPipPipPip
  • 403 posts
  • Gender:Male

Posted 14 October 2009 - 08:07 AM

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?

#9 Unfathomable Depths

Unfathomable Depths

    sláinte

  • GP32 Hardcore
  • PipPipPipPipPipPip
  • 877 posts
  • Gender:Male
  • Location:Scotland

Posted 14 October 2009 - 08:13 AM

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.

Edited by Unfathomable Depths, 14 October 2009 - 08:14 AM.


#10 Miner49er

Miner49er

    GP Mania

  • GP32 Hardcore
  • PipPipPipPipPip
  • 403 posts
  • Gender:Male

Posted 14 October 2009 - 08:53 AM

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
*/

#11 dsh

dsh

    GP32 User

  • Members
  • PipPipPip
  • 52 posts
  • Gender:Male
  • Location:Cracow, Poland

Posted 14 October 2009 - 09:14 AM

I believe that if you could post your main loop's code that would allow us to help you more.

#12 Miner49er

Miner49er

    GP Mania

  • GP32 Hardcore
  • PipPipPipPipPip
  • 403 posts
  • Gender:Male

Posted 14 October 2009 - 09:35 AM

I believe that if you could post your main loop's code that would allow us to help you more.


	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)

Edited by Miner49er, 14 October 2009 - 09:40 AM.


#13 dsh

dsh

    GP32 User

  • Members
  • PipPipPip
  • 52 posts
  • Gender:Male
  • Location:Cracow, Poland

Posted 14 October 2009 - 10:09 AM


I believe that if you could post your main loop's code that would allow us to help you more.


	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.

Edited by dsh, 14 October 2009 - 10:14 AM.


#14 Miner49er

Miner49er

    GP Mania

  • GP32 Hardcore
  • PipPipPipPipPip
  • 403 posts
  • Gender:Male

Posted 14 October 2009 - 10:35 AM



I believe that if you could post your main loop's code that would allow us to help you more.


	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.

#15 dsh

dsh

    GP32 User

  • Members
  • PipPipPip
  • 52 posts
  • Gender:Male
  • Location:Cracow, Poland

Posted 14 October 2009 - 10:55 AM




I believe that if you could post your main loop's code that would allow us to help you more.


	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.

Edited by dsh, 14 October 2009 - 10:56 AM.