[SDL] Problems in Full Screen

David Olofson david at olofson.net
Fri May 23 03:54:02 PDT 2003


On Friday 23 May 2003 06.10, eDU! wrote:
[...]
> Can you tell me what could be the possible problem? Im sure that
> its not an sdl problem, and its just the way im "showing" the
> images. Maybe the background image. I can't guess it.

When you use double buffering, there are basically two possible ways 
it can be arranged:

	1) True page flipping, with two hardware surfaces that
	   are swapped when you SDL_Flip(). You render into one
	   while the other one is being displayed.

	2) "Fake" page flipping, with one hardware surface for
	   the display, and one software surface for rendering.
	   When you SDL_Flip(), the software back surface (or
	   "shadow surface") is copied into the front hardware
	   surface.


When you're deailing with full screen scrollers and the like, where 
the whole screen is repainted every frame, the difference is 
irrelevant. Both arrangements will work fine.

However, as soon as you start doing partial updates, it makes a big 
difference!

If you're on a "case 2" display, there's no problem; each flip will 
just make sure the current version of your screen becomes visible. 
You get to keep the back surface as a base for further rendering.

Now, if you do this on a "case 1" display, this no longer works. When 
you flip, the page you just rendered into is *swapped* with the one 
that was displayed. Of course, you still get your new frame 
displayed, but your new back buffer is the *previous* frame you 
flipped into display. You now have to perform updates for two frames 
to catch up; one to get back to the state of the frame you just 
flipped into display, and one to turn that into the next frame.


In short, if you set up a double buffered screen and get a hardware 
surface, you need to do all updates twice per frame, or deal with the 
"old back buffer" in some other way.


More specifically, you need to blit the background image twice, with 
an SDL_Flip() in between, and then you need to do something about the 
sprite removal.

The safe way (works with both styles of double buffering) is to 
remember all sprite "dirty rects" for the last two frames. Before you 
blit the sprites for a new frame, restore all dirty rects from *both* 
buffers. Then blit your sprites, overwriting the oldest set of dirty 
rects.

A more efficient way is to restore using only the older set of dirty 
rects. (The newer one *shouldn't* be relevant until the next frame, 
since it describes the page that's currently being displayed.) 
However, do keep in mind that this breaks down unless you *really* 
have a true hardware pageflipping ("case 1") display.

Don't start whining about modern video subsystems being a PITA just 
yet, though. There's a third, hybrid method that covers both cases 
without much extra cost: Instead of just restoring every single dirty 
rect in the two sets, make a first pass where you merge overlapping 
rects, to avoid updating some areas multiple times.

In many cases when dealing with sprites, and you don't have tons of 
them (ie not too many overlapping sprites), you can just consider the 
last two positions of each sprite, and create a dirty rect that 
covers both positions. As long as sprites move a reasonable speeds, 
this means the dirty rect is only a few pixels bigger than the 
sprite. Just in case, you could calculate the area of the resulting 
dirty rect, and if it's more than twice the area of the sprite's 
bounding rect, keep the separate dirty rects instead.

In Project Spitfire/DOS (double buffered h/w scrolling with an extra 
buffer for scheduled background rendering), I used a variant of the 
"use only the older diry rects set" method. I didn't use dirty rects 
at all, but rather kept a local tiled map for each buffer. This map 
would first be filed with the index of an illegal (non-drawing) 
background tile. When rendering sprites into the buffer, I would copy 
tile indices from the level map into the buffer for each tile that 
the sprite would overdraw. To remove the sprites, I just scanned the 
"dirty map" and repainted any valid tiles found in it. Very simple 
and low cost way of avoiding overdraw when removing sprites.


Of course, there's always the fool proof, totally portable, but not 
too fast brute force approach: Always repaint the whole screen 
whether you really need to or not.

If you're scrolling the whole screen, you normally need to do this 
anyway. For something like a Break-Out clone or something more like a 
normal application (buttons, sliders and stuff), it would be a huge 
waste of resources, and it would multiply your minimum system 
requirements by an insane factor for no real reason.

So, you need to be aware of both methods, and pick the one that makes 
most sense for each application.


Hmm... I didn't realize there was this much to say about basic sprite 
rendering. It's all obvious enough to me that I keep forgetting, I 
guess. Maybe I should hack some basic "sprites on still background" 
examples, and HTMLize the above to go with it? This is an FAQ entry, 
by all means.


//David Olofson - Programmer, Composer, Open Source Advocate

.- The Return of Audiality! --------------------------------.
| Free/Open Source Audio Engine for use in Games or Studio. |
| RT and off-line synth. Scripting. Sample accurate timing. |
`-----------------------------------> http://audiality.org -'
   --- http://olofson.net --- http://www.reologica.se ---





More information about the SDL mailing list