PDA

View Full Version : Pondering a Smoother Scroll (OpenGL)



WILL
19-01-2007, 04:46 PM
Hey all!

I'm fairly new to OpenGL and I've been looking at my recent game (Garland's Quest) and I've noticed that even though it doesn't look quite so terrible, I believe that the scrolling can look much nicer, or at the very least much smoother.

What are some of the things that you do in OpenGL to improve your drawn frames?

Instantly I'm thinking of either double/triple buffering or V-Sync, etc...

dmantione
19-01-2007, 06:27 PM
Well, with OpenGL it is the role of the OpenGL subsystem to draw frames synchrone (allthough you can request to switch vsync on/off through some extension). Just draw and forget about it :)

WILL
19-01-2007, 08:08 PM
My only issue with v-sync is that it limits you to the refresh rate of your monitor does it not?

It would be safe to say that in most cases that frame rate would be a mere 60 FPS, no?

dmantione
19-01-2007, 09:10 PM
Something like 80 Hz would be a bit more comfortable for your eyes... Yes, you will be limited to your refresh rate, but what is the point generating frames that do not get displayed?

WILL
20-01-2007, 02:12 AM
Well I cannot argue with that. However I still want to reduce the glitchyness of the drawing.

I'm not 100% on using vsync as I don't want to waste possible cpu juice just waiting for the next screen refresh.

A possible solution could be to do some buffering, but I can't help but think that I can improve of this method rather than just adding features to suck up more cpu juice.

Is there a way to detect a screen refresh without the pause? Or possibly read the setting from your hardware?

dmantione
20-01-2007, 09:14 AM
I found a piece of gold: http://olofson.net/download/smoothscroll-1.1.tar.gz

It has two modes, OpenGL and SDL. In SDL-mode it doesn't vsync for me, lots of image tearing is visible on the display. The SDL-mode uses about 65% of my cpu. In OpenGL-mode it is vsynced, eveything scrolls smoothly, and only 3,5% cpu is used.

technomage
20-01-2007, 09:25 AM
WILL

looking at the headers you can use the

SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL,1);

To turn on Swap control with SDL, it looks like you can do this even after calling SDL_SetVideoMode

you should also be able to use


var value: Integer;
begin
SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL, value);
end;


to query the swap control flag. I think this only works with SDL 1.2.10 and up

WILL
21-01-2007, 05:53 PM
Well it seems with the double buffering options in it's still not much of a difference. :P

And the damn libsdl.org site is down so forget looking up what the value associated with SDL_GL_SWAP_CONTROL is all about.

WILL
21-01-2007, 06:14 PM
Ah... and my version of JEDI-SDL doesn't have it as the included version of sdl is only 1.2.8.0. :roll:

dmantione
21-01-2007, 06:15 PM
Yes, but you are using plain SDL, right? I am unable to get vsynced output with the SDL version of the example program I linked, only with OpenGL.

WILL
21-01-2007, 06:19 PM
Nope, I'm using OpenGL, but with SDL handling all the window management bits.

dmantione
21-01-2007, 06:22 PM
Hmmm... The OpenGL version is super smooth for me.

WILL
21-01-2007, 06:25 PM
This is my code to initialize video in my game.

// Init Video Mode + Joysticks //
if (SDL_Init(SDL_INIT_VIDEO + SDL_INIT_JOYSTICK) <> 0) then
Halt;

// Set the title bar in environments that support it
SDL_WM_SetCaption('Garland''s Quest ' + EDITION_NAME + ' ' + VERSION_NUMBER, nil);

////////////////////////////////Some Extra Setup\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

//Get Video Info
videoInfo := SDL_GetVideoInfo;
flags := SDL_OPENGL; //Enable OpenGL in SDL
flags := flags or SDL_DOUBLEBUF; //Enable double buffering
flags := flags or SDL_HWPALETTE; //Store the palette in hardware
// flags := Flags or SDL_RESIZABLE; //Enable window resizing?
if (GO_FULLSCREEN) then
flags := flags + SDL_FULLSCREEN;

// This checks to see if surfaces can be stored in memory
if ( videoInfo.hw_available <> 0 ) then
flags := flags or SDL_HWSURFACE
else
flags := flags or SDL_SWSURFACE;

// This checks if hardware blits can be done
if ( videoInfo.blit_hw <> 0 ) then
flags := flags or SDL_HWACCEL;

SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
// SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 1 ); // Needs SDL version 1.2.10.0 and up!

////////////////////////////////////////////////////////////////////////////////

GameScreen := SDL_SetVideoMode(ScreenWidth, ScreenHeight, 32, flags);

GameScreen is the surface of which resides as my primary screen buffer. (or secondary, depending on how you think about double buffering :P) And I've obviously only just added the SDL_GL_SWAP_CONTROL line.

dmantione
21-01-2007, 07:01 PM
The smoothscroll doesn't use the swap control flag. Lucky for me, since I'm using SDL 1.2.7. It basically does what you do and then after drawing a frame it calls SDL_GL_Swapbuffers.

Wiering
21-01-2007, 07:55 PM
On my machine, OpenGL generally runs smoother than DirectX, especially for games running in a window on the desktop. With DirectX, you always loose a few frames once in a while and get some tearing, even with no other programs running. With OpenGL, after starting up (10-20 seconds) the game just runs perfectly smooth.

Unfortunately, many people don't seem to have the right drivers installed for OpenGL, so if you release a game, many people may have trouble running it. Also, for it to run perfectly smooth, people need to enable the vsync option in their driver settings. This is often disabled by default.

Anyway, the Delphi/OpenGL example for Tile Studio (http://tilestudio.sourceforge.net/) can scroll perfectly smooth (here at least). It uses sub-pixel scrolling too, like the smoothscroll example, but a little more advanced :)

In the smoothscroll demo, there are very few tiles and most are always used with the same surrounding tiles to prevent problems at the edges. But normally, if you make a game using a map of tiles, you'll want to be able to place any two tiles next to each other without running into problems. In the Tile Studio demo, this problem is solved by having separate edges and corners for combinations of neighboring tiles on your map(s). For example, if you have tiles of 32x32 pixels, TS will generate new tiles of them (with the most often occuring edges and corners, 34x34 pixels) and some extra strips for edges with other tiles (32x2 and 2x32) and other corners (2x2).

WILL
21-01-2007, 09:20 PM
Hey Mike, thanks for the extra info. Though I'm not quite sure how that technique would get rid of the tearing issue. The only thing to do it seems is use vsync. :?

I went into my OpenGL settings on my system and set VSync to 'On Always'. (it seems somehow JEDI-SDL forces it on in the software layer.) That did the trick. No more tearing and it looks a bit better.

As my game's code doesn't seem to run each frame past 60 FPS, it seems like a decent alternative. Though other than using SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); how do I force VSync in my game? I seriously doubt that Dom would have released JEDI-SDL without the ability to put OpenGL into VSync mode. :roll:

cragwolf
21-01-2007, 09:28 PM
WILL, get rid of these lines, you don't need them when using the SDL_OPENGL flag, and they might be screwing up the smoothness of your scrolling.



flags &#58;= flags or SDL_DOUBLEBUF; //Enable double buffering
flags &#58;= flags or SDL_HWPALETTE; //Store the palette in hardware

// This checks to see if surfaces can be stored in memory
if &#40; videoInfo.hw_available <> 0 &#41; then
flags &#58;= flags or SDL_HWSURFACE
else
flags &#58;= flags or SDL_SWSURFACE;

// This checks if hardware blits can be done
if &#40; videoInfo.blit_hw <> 0 &#41; then
flags &#58;= flags or SDL_HWACCEL;

technomage
21-01-2007, 11:11 PM
Though other than using SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); how do I force VSync in my game? I seriously doubt that Dom would have released JEDI-SDL without the ability to put OpenGL into VSync mode. :roll:

Under windows there is a wgl extension to handle swapcontrol. Under linux there is a glX extension,you will have to implement it manually for each platform.

deathshadow
09-02-2007, 07:14 PM
My only issue with v-sync is that it limits you to the refresh rate of your monitor does it not?

It would be safe to say that in most cases that frame rate would be a mere 60 FPS, no?

You know, I see statements like that and I have to laugh...

Yes, it limits you to the refresh rate - and that's a GOOD thing! Why would you waste processor time rendering frames that will never even be SHOWN?

Literally, if you render 170fps and your refresh rate is only 85hz, you're only showing HALF of what you render... a total waste.

Refresh rate is how often the screen is redrawn - any 'tearing' you see is because you are trying to render a frame in the middle of the screen redraw - there is NO POINT to rendering faster than the refresh rate - EVER, unless of course you WANT it to look like crap when anything moves.

Just as there's little reason to ever doing anything higher than 60hz for the majority of people, since even then it's just a barely noticable flicker... and anything higher than 85hz is bragging, not fact since that's outside the realm of human vision (just as anything over 18khz is outside the realm of hearing)

WILL
09-02-2007, 07:36 PM
Though I completely agree with that point of view. There is a single obstacle when you are enabling vsync. If you're game's main run cycle is frame based then you will be restricted to 60 game cycles. (or whatever else your monitor runs at)

I think I'm asking for a time-based movement response just by mentioning it. :p

But how do you avoid your game from just sitting there after calling a flip when you've got more processing to do beyond just the graphics? And is there really only one way to do it?


I however do not need such things as 50 FPS suites my current game just fine. So I'm forcing vsync. (unless those of you are truly hurting for it to be off have it set of always off)

However I believe I've found my function! ;)
wglSwapIntervalEXT(1);

This bad boy will turn on vsync for all you 60Hz fans out there. :thumbup: a 0 value however will turn it off.

deathshadow
09-02-2007, 07:49 PM
Though I completely agree with that point of view. There is a single obstacle when you are enabling vsync. If you're game's main run cycle is frame based then you will be restricted to 60 game cycles. (or whatever else your monitor runs at)

Well, technically restricted by whatever refresh rate the user has it set to - which could be anywhere from 43hz to 120hz. (though I doubt anyone actually still runs 43hz interlaced...)

It's a bad timer to use because you have no idea what the users refresh rate is - and should your render time take LONGER than the refresh rate, your game will slow down not 'keeping up' with realtime.

Which is why most games scale their movement and responses to a system timer (WM_Timer good as always) independant of the rendering engine. You render where it is at the start of the rendering process - you don't update where it is at the same time, you use a separate timer event for that... therin if the frame rate drops because the scene is too complex, the game keeps going at the same speed ;)

That and the fact you could make the physics/movement a separate thread, making better use of all these modern multi-core processors. :)

WILL
09-02-2007, 07:51 PM
Darn I'm getting a memory error after adding it. :?

I've added glext to the uses clause and it compiled and ran fine. However once I start to run the game it crashes with a SIGSEG (memory) error.

Is there a specific order that I have to switch this on and off in? ie. before/after window creation or gl initialization...

WILL
09-02-2007, 07:55 PM
Which is why most games scale their movement and responses to a system timer (WM_Timer good as always) independant of the rendering engine. You render where it is at the start of the rendering process - you don't update where it is at the same time, you use a separate timer event for that... therin if the frame rate drops because the scene is too complex, the game keeps going at the same speed ;)

That and the fact you could make the physics/movement a separate thread, making better use of all these modern multi-core processors. :)
So in your honest opinion you think that the only way to break the game cycle limit past the monitor's refresh rate is to go multi-threaded?

JSoftware
09-02-2007, 08:05 PM
SIGSEG? Isn't that the linux exception equivalent to an Access violation?

If so then it's because you try to use a windows gl(wgl) extension. If you are indeed using windows do you then remember to load the extension? Most glext units i've seen uses dynamic loading of dll's

WILL
09-02-2007, 08:11 PM
SIGSEG? Isn't that the linux exception equivalent to an Access violation?

Sorry you caught me. It was 'SIGSEGV'. :P


If so then it's because you try to use a windows gl(wgl) extension. If you are indeed using windows do you then remember to load the extension? Most glext units i've seen uses dynamic loading of dll's

Hmm... good question. As far as I know, I've never had to dynamically load anything for JEDI-SDL's GL functions. But then I've never used any of the glext units till now either.

whats the filename I'm looking for?

JSoftware
09-02-2007, 08:14 PM
Can you try calling something like Load_WGL_EXT_swap_control(); or glext_LoadExtension('WGL_EXT_swap_control');?

deathshadow
09-02-2007, 08:40 PM
So in your honest opinion you think that the only way to break the game cycle limit past the monitor's refresh rate is to go multi-threaded?

Not what I meant - I meant to avoid wasting CPU time that could be better spent elsewhere on faster machines, and prevent the game from running slower on slower machines.

If the machine isn't fast enough to run the game at 60fps, you should be dropping frames but still have the game run as fast as on any other computer. This is how the big name 3d games all work - the game doesn't slow down just because it's not fast enough, it just starts dropping frames - likewise the game doesn't speed up just because it can render 120fps... you have to scale your movement to the time available.

Multithreading is a simple way to do it, and takes advantage of the new multi-core machines, or even older multi-processor machines, but it's certainly not the only way... You could set up a timer, then when you go to update your movement you scale it to the time elapsed since the last time your movement was done. Delphi's TTimer object is ideal for both approaches... If you set it to 60 times a second and handled all movement in the onTimer method, problem solved (I'd actually run 240 there so as to allow faster FPS to have something to draw). You could even set the timer to say... 512 times a second, just increment a byte timer in your ontimer method, and scale movement based on how many 'ticks' have gone by at render time.

The key is to make 100% sure the game runs at the same speed regardless of the hardware it's on or how complex a scene is... and refresh rate doesn't do that.