[SDL] SDL blitting/refresh speed challenges

A.L.Moore lmoore67 at hotmail.com
Thu Mar 22 01:07:38 PDT 2007


Some people are just never satisfied.  Not content with getting cross-platform 
bitmap font handling by grace of SDL and SDL_tff, I'm trying to figure out how 
to make it FAST.

I've spent days on the problem, nothing seems to be working, and trying ideas 
from publicly available SDL projects has gotten me nowhere.  There are only 
two lines of code that are bogging down my text-output module, both pure SDL 
calls.  SDL Gurus, can any of you help me figure out the problem?


Setup:
- Windows XP, with pre-compiled SDL (SDL 1.2.11, SDL_tff 2.0.8) libraries 
and .dlls for Windows, using Visual C++ 6.0.  Code is ANSI C.
- Roughly 2 GHz machine with 1 GB RAM, Radeon 8500 display adapter with 64 MB 
memory, fully updated drivers.  I know the problem isn't with Windows or my 
machine, because the old version of the below code, using the Win 32 API, is 
about seven times as fast.

Objective:
- Print lots of text, using bitmapped monospaced fonts, very quickly.

Problem:
- Everything works ... except the speed.

Comments:
- I've profiled this code extensively.  See the "THIS LINE OF CODE IS SLOW" 
comments near the bottom for the exact places needing optimization.



//
// Make an application window.
//
// Comments:  I seem to be having trouble getting hardware surfaces and SDL 
doesn't 
// find any video memory (possibly my video card isn't recognized).  But so 
simple 
// is what I try to do below that this really should not matter as much as it 
does.
//

flags = SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_ANYFORMAT;

// Create an application surface using the current bit depth
Application_Surface = SDL_SetVideoMode(VideoInfo->current_w, 
	VideoInfo->current_h, 0, flags);

// Handle failure
 if (!Application_Surface) quit(format("Failed to create %d%d window at %d 
bpp!",
	VideoInfo->current_h, VideoInfo->current_w, 
	VideoInfo->vfmt->BitsPerPixel));




// Render the glyphs

//
// Because "TTF_RenderText_Solid" is relatively slow, we cannot simply use it
// in "display_text()".  Instead, we pre-allocate a working glyph surface,
// filled with all possible characters of this font.  This surface should be
// opaque by default.
//
// Comment:  Yes, I started out trying to render text on the fly and quickly 
// discovered that doing that for pages on end would bring my poor ol' CPU to 
// its knees!
//
// "Font" must contain a monospaced font.  It is optional, but highly
// recommended, that all characters between 0 and 255, inclusive, be defined
// (they can be blank).  Characters after 256 are ignored.  XXX XXX
//
static SDL_Surface *render_glyphs(TTF_Font *font, int w, int h)
{
	SDL_Surface *glyphs;
	SDL_Surface *temp_glyph;
	SDL_Rect dst_rect, src_rect;
	int i;

	// Build colors for text and background
	SDL_Color bkgrnd_clr = { 255, 255, 255, 0 };
	SDL_Color text_clr  = { 0, 0, 0, 0 };
	u32b bkgrnd;
	u32b white;


	// Create an empty surface for the glyphs.  Prefer to use video memory
	glyphs = SDL_CreateRGBSurface(SDL_HWSURFACE, w * 256, h, 
		8, 0, 0, 0, 0);

 	// Comments:  I tried using individual glyph surfaces but got no 
efficiency gains.

	// Comments:  I could try using the same bit depth as the application 
surface for (hopefully) faster blitting but, when I tested this code with a 
screen depth of 8 bits (256 colors, things actually went slower.  So I don't 
think this is my problem...


	// White is the first palette entry; black the second
	SDL_SetPalette(glyphs, SDL_LOGPAL, &bkgrnd_clr, 0, 1);
	SDL_SetPalette(glyphs, SDL_LOGPAL, &text_clr, 1, 1);


	// Create colors for "background" and "black"
	bkgrndColor = SDL_MapRGB(glyphs->format, 255, 255, 255);
	textColor = SDL_MapRGB(glyphs->format, 0, 0, 0);

	// Fill in the glyph surface with the background color
	SDL_FillRect(glyphs, NULL, bkgrndColor);

	// Request run-length acceleration (this actually makes no measurable 
difference on my machine)
	SDL_SetColorKey(glyphs, SDL_RLEACCEL, 0);

	// Select our first glyph cell
	dst_rect.x = 0;
	dst_rect.y = 0;
	dst_rect.w = w;
	dst_rect.h = h;

	src_rect.x = 0;
	src_rect.y = 0;
	src_rect.w = w;
	src_rect.h = h;

	// Run along the glyph surface, filling it in with characters
	for (i = 0; i < 256; i++, dst_rect.x += dst_rect.w)
	{
		// Render this glyph in black
		temp_glyph = TTF_RenderGlyph_Solid(font, i, textColor);

		// Glyph not found -- skip it
		if (!temp_glyph) continue;

		// Blit the glyph to our surface
		SDL_BlitSurface(temp_glyph, &src_rect, glyphs, &dst_rect);

		// Erase it with the background color
		SDL_FillRect(temp_glyph, NULL, bkgrndColor);
	}

	// Free the temporary glyph surface
	SDL_FreeSurface(temp_glyph);

	// Return our surface
	return (glyphs);
}




//
// Display a single line of text on screen.  NEEDS EFFICIENCY WORK.
//
// (the code to allow centering, sizing, etc. has been removed for simplicity, 
// as has the error-checking code)
//
static void display_text(various parameters)
{
	// This is our display window.  It contains sizing, font, and other 
information
	DISPLAY_WINDOW *window;

	SDL_Rect dst_rect;
	SDL_Rect src_rect;

	int i;

	// This is our pre-rendered glyph surface
	// glyphs

	// This is our application screen.  It needs to show lots of text
	// Application_Surface

	// Adjust the text color by modifying its palette entry
	// (this line of code uses very little CPU time)
	SDL_SetPalette(glyphs, SDL_LOGPAL, input_color, 1, 1);

	// Remove color key, if any
	if (glyphs->flags & (SDL_SRCCOLORKEY))
	{
		// Remove any color key 
		SDL_SetColorKey(glyphs, SDL_RLEACCELOK, 0);
	}


	// Source rectangle (size of a single glyph)
	src_rect.x = 0;
	src_rect.y = 0;
	src_rect.w = window->font_wid;
	src_rect.h = window->font_hgt;

	// Destination rectangle (size of a text cell)
	dst_rect.x = window->window_left + window->border_left + 
		(col * window->cell_wid);
	dst_rect.y = window->window_top  + window->border_top  + 
		(row * window->cell_hgt);
	dst_rect.w = window->cell_wid;
	dst_rect.h = window->cell_hgt;


	// Advance along the string
	for (i = 0; i < n; i++, dst_rect.x += dst_rect.w)
	{
		// Jump to the glyph
		src_rect.x = ch * src_rect.w;

		// Blit the character to screen
		// THIS LINE OF CODE IS SLOW
		(void)SDL_BlitSurface(glyphs, &src_rect, Application_Surface, 
			&dst_rect);

		// Comments:  I've tried RLE acceleration and testing both 
hardware and software surfaces, to no avail
	}

	// Update the full text area
	// THIS LINE OF CODE IS SLOW
	SDL_UpdateRect(Application_Surface,
		window->window_left + win_ptr->border_left + 
		(col * window->cell_wid),
		window->window_top  + win_ptr->border_top  + 
		(row * window->cell_hgt),
		window->cell_wid * n,
		window->cell_hgt);

	// Comments:  I tried to optimize the above by creating a queue of 
update rects and calling "SDL_UpdateRects" only when the queue is full (or the 
text has finished printing), but amazingly, things got even slower.  At that 
point, I realized I had no clue how to optimize SDL and decided to ask for 
help.
}





More information about the SDL mailing list