SDL too slow for a high-res RTS game? [SOLVED]

Started by
11 comments, last by NegativeGeForce 16 years, 10 months ago
Hello, I know, I know... YET another thread about SDL and its performance by me. I apologize for that. Anyways, maybe you remember me posting about speed-issues before, but now it's got me again. This is the situation: I want to draw isometric tiles sized 128x64 for a 1280x1024 game. This means (I checked via debug-features in my project) around 400 tiles on the screen at a time out of 1200 for example for a 50x30 sized map (in tiles). This already slows my SDL application down to 20 to 25 frames. My computer is four years old, but shouldn't it be able to run faster? Here is some code from the tile drawing for you to check for any errors: Note that my tiles do not use an alpha-channel, in that case, I'd know why it's slow... sprite.cpp

void csprite::init ( const char *filename, bool alpha_channel )
// initializes the sprite
{
	if ( alpha_channel )
	// if the image has an alpha-channel
		// load the bitmap
		bitmap = IMG_Load ( filename );

	else
	// if the image does not have an alpha-channel
	{
		SDL_Surface* temp;

		// load temp
		temp = SDL_LoadBMP ( filename );

		if ( temp == NULL )
		// if there was an error
			return_error ( "Failed to initialize sprite..." );

		// load the bitmap
		bitmap = SDL_DisplayFormat ( temp );

		// free temp	
		SDL_FreeSurface ( temp );

		// activate color keying
		if ( SDL_SetColorKey ( bitmap, SDL_SRCCOLORKEY, SDL_MapRGB ( bitmap->format, 255, 0, 128 ) ) < 0 )
		// if there was an error
			return_error ( "Failed to colorkey sprite..." );
	}

	if ( bitmap == NULL )
	// if there was an error
		return_error ( "Failed to initialize sprite..." );

	// set up other variables
	width = bitmap->w;
	height = bitmap->h;
}

void csprite::init ( const char *filename, bool alpha_channel, int _width, int _height )
// initializes the sprite
{
	// initialize sprite
	init ( filename, alpha_channel );

	// set up other variables
	width = _width;
	height = _height;
}

void csprite::draw ( )
// draws the sprite to the screen
{
	// create a source rectangle
	SDL_Rect source;
	source.x = 0;
	source.y = 0;
	source.w = bitmap->w;
	source.h = bitmap->h;
	
	// create a destination rectangle
	SDL_Rect destination;
	destination.x = ( int ) x;
	destination.y = ( int ) y;
	destination.w = width;
	destination.h = height;

	// blit the bitmap to the screen
	if ( SDL_BlitSurface ( bitmap, &source, SDL_GetVideoSurface ( ), &destination ) < 0 )
	// if there was an error
		return_error ( "Failed to draw sprite..." );
}

void csprite::draw ( float _x, float _y )
// draws the sprite to the screen at a given location
{
	// position the sprite
	x = _x;
	y = _y;
	
	// draw it
	draw ( );
}

void csprite::draw ( int _x, int _y )
// draws the sprite to the screen at a given location
{
	// create a source rectangle
	SDL_Rect source;
	source.x = 0;
	source.y = 0;
	source.w = bitmap->w;
	source.h = bitmap->h;
	
	// create a destination rectangle
	SDL_Rect destination;
	destination.x = _x;
	destination.y = _y;
	destination.w = width;
	destination.h = height;

	// blit the bitmap to the screen
	if ( SDL_BlitSurface ( bitmap, &source, SDL_GetVideoSurface ( ), &destination ) < 0 )
	// if there was an error
		return_error ( "Failed to draw sprite..." );
}

void csprite::draw ( float _x, float _y, int x_frame, int y_frame )
// draws the sprite to the screen at a given location using a frame
{
	// position the sprite
	x = _x;
	y = _y;

	// create a source rectangle
	SDL_Rect source;
	source.x = x_frame * width;
	source.y = y_frame * height;
	source.w = width;
	source.h = height;
	
	// create a destination rectangle
	SDL_Rect destination;
	destination.x = ( int ) x;
	destination.y = ( int ) y;
	destination.w = width;
	destination.h = height;

	// blit the bitmap to the screen
	if ( SDL_BlitSurface ( bitmap, &source, SDL_GetVideoSurface ( ), &destination ) < 0 )
	// if there was an error
		return_error ( "Failed to draw sprite..." );
}

void csprite::draw_cropped ( int percentage )
// draws the sprite to the screen cropped at a given location
{
	// create a source rectangle
	SDL_Rect source;
	source.x = 0;
	source.y = 0;
	source.w = ( int ) ( ( 1.0 / 100.0 ) * ( float ) percentage * ( float ) width );
	source.h = bitmap->h;
	
	// create a destination rectangle
	SDL_Rect destination;
	destination.x = ( int ) x;
	destination.y = ( int ) y;
	destination.w = ( int ) ( ( 1.0 / 100.0 ) * ( float ) percentage * ( float ) width );
	destination.h = height;
	
	// blit the bitmap to the screen
	if ( SDL_BlitSurface ( bitmap, &source, SDL_GetVideoSurface ( ), &destination ) < 0 )
	// if there was an error
		return_error ( "Failed to draw sprite..." );
}

void csprite::destroy ( void )
// destroys all used memory of a sprite and frees it
{
	// free the surface
	SDL_FreeSurface ( bitmap );
}




tile.cpp

int tile_offset_x, tile_offset_y;

int tiles_drawn;

ctile tile[TILES_X][TILES_Y];

csprite tileset;

extern screen_width, screen_height;

void ctile::init ( int _x, int _y, tile_type _type )
// initializes the tile
{
	if ( _type == dirt )
	{
		// assign vertical frame
		y_frame = 0;

		// randomize horizontal frame
		x_frame = rand ( ) % 3;
	}

	else if ( _type == grass )
	{
		// assign vertical frame
		y_frame = 1;

		// randomize horizontal frame
		x_frame = rand ( ) % 3;
	}
	/*else
	{
		// assign vertical frame
		y_frame = 2;

		if ( _type == tra_grass_dirt_t )
			x_frame = 0;
		else if ( _type == tra_grass_dirt_tr )
			x_frame = 1;
		else if ( _type == tra_grass_dirt_r )
			x_frame = 2;
		else if ( _type == tra_grass_dirt_br )
			x_frame = 3;
		else if ( _type == tra_grass_dirt_b )
			x_frame = 4;
		else if ( _type == tra_grass_dirt_bl )
			x_frame = 5;
		else if ( _type == tra_grass_dirt_l )
			x_frame = 6;
		else if ( _type == tra_grass_dirt_tl )
			x_frame = 7;
	}*/

	// save position
	x = _x;
	y = _y;

	// save type
	type = _type;
}

void ctile::draw ( void )
// draws the tile
{
	// draw the sprite
	tileset.draw ( ( float ) x, ( float ) y, x_frame, y_frame );
}

void init_tiles ( int start_position_x, int start_position_y )
// inits the tiles
{
	int	row_index = 0;

	tiles_drawn = 0;

	// init tileset
	tileset.init ( "tilesets/jungle.bmp", false, TILE_WIDTH, TILE_HEIGHT );

	for ( int y = 0; y < TILES_Y; y++ )
	// loop through all tiles vertically
	{
		for ( int x = 0; x < ( TILES_X / 2 ); x++ )
		// loop through all tiles horizontally
		{
			if ( row_index == 0 )
				// init tile
				tile[x * 2][y].init ( ( x * TILE_WIDTH ) - start_position_x, ( y * ( ( TILE_HEIGHT + 1 ) / 2 ) ) - start_position_y, grass );

			else if ( row_index == 1 )
				// init tile
				tile[( x * 2 ) + 1][y].init ( ( x * TILE_WIDTH ) + ( TILE_WIDTH / 2 ) - start_position_x, ( ( y - 1 ) * ( ( TILE_HEIGHT + 1 ) / 2 ) ) + ( ( TILE_HEIGHT + 1 ) / 2 ) - start_position_y, grass );
		}

		// alternate index
		if ( row_index == 0 )
			row_index = 1;
		else
			row_index = 0;
	}
}

void draw_tiles ( void )
// draws the tiles
{
	tiles_drawn = 0;

	for ( int y = 0; y < TILES_Y; y++ )
	// loop through all tiles vertically
	{
		for ( int x = 0; x < TILES_X; x++ )
		// loop through all tiles horizontally
		{
			if ( tile_offset_x > 0 )
			// left scrolling
			{
				if ( tile[0][0].x >= -( TILE_WIDTH / 2 ) )
					tile_offset_x = 0;
			}

			else if ( tile_offset_x < 0 )
			// right scrolling
			{
				if ( tile[0][0].x <= -( ( TILES_X / 2 ) * TILE_WIDTH ) - 2 + screen_width )
					tile_offset_x = 0;
			}

			if ( tile_offset_y > 0 )
			// up scrolling
			{
				if ( tile[0][0].y >= -( ( TILE_HEIGHT + 1 ) / 2 ) )
					tile_offset_y = 0;
			}

			else if ( tile_offset_y < 0 )
			// down scrolling
			{
				if ( tile[0][0].y <= -( ( TILES_Y / 2 ) * ( TILE_HEIGHT + 1 ) ) - 2 + screen_height )
					tile_offset_y = 0;
			}

			// update position
			tile[x][y].x += tile_offset_x;
			tile[x][y].y += tile_offset_y;

			if ( tile[x][y].x >= -TILE_WIDTH && tile[x][y].x <= screen_width )
			// if tile is horizontally in view
			{
				if ( tile[x][y].y >= -TILE_HEIGHT && tile[x][y].y <= screen_height )
				// if tile is vertically in view
				{
					// draw tile
					tile[x][y].draw ( );

					tiles_drawn++;
				}
			}
		}
	}
}

void destroy_tiles ( void )
// destroy the tiles
{
	// destroy tileset
	tileset.destroy ( );
}




So, my question: is there any error in there from my side or is SDL just not fast enough for 1280x1024-sized games with tiles? Thanks ahead of time. [Edited by - d h k on May 24, 2007 7:02:50 AM]
Advertisement

.

Okay, then I'll try to work with OpenGL (which I luckily know pretty good). Or would it make sense to switch to hardware-mode and stay with SDL (because I'd really like to stay with it)?

EDIT: Some example code would be well appreciated! :)

.

Yeah, tried that, but didn't seem to change anything.

Okay, so I guess I'll be using OpenGL with SDL from now on.
Quote:EDIT: Some example code would be well appreciated! :)

Hehe, allright! Many posts today. :)

#define SCREEN_WIDTH 1024#define SCREEN_HEIGHT 768#define NUM_TILES 16#define TILE_SIZE 32 /* use 32x32 tiles *//* remember some stuff when we upload textures */struct BitmapStruct {   GLuint texture_number;   GLfloat texcoord[4];};struct BitmapStruct GFX_Tiles[NUM_TILES];SDL_Surface *MainScreen;Uint32 bpp, rmask, gmask, bmask, amask;void InitScreen() {   if (SDL_Init(SDL_INIT_VIDEO) < 0)      exit(1);   putenv("SDL_VIDEO_CENTERED=center");   MainScreen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 0, SDL_OPENGL);   bpp   = MainScreen->format->BitsPerPixel;   rmask = MainScreen->format->Rmask;   gmask = MainScreen->format->Gmask;   bmask = MainScreen->format->Bmask;   amask = MainScreen->format->Amask;   atexit(SDL_Quit);   glEnable(GL_TEXTURE_2D);   glEnable(GL_ALPHA_TEST);   glDisable(GL_DEPTH_TEST);   glAlphaFunc(GL_GREATER, 0);   glClearColor(0.0, 0.0, 0.0, 1.0f);   glMatrixMode(GL_PROJECTION);   glLoadIdentity();   glOrtho(0.0, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0, 0.0, 1.0);   glMatrixMode(GL_MODELVIEW);}/* here you can load all the graphics for your game */void LoadGraphicFiles() {   LoadBitmapToStruct("gfx/tiles.bmp", GFX_Tiles, TILE_SIZE, TILE_SIZE, NUM_TILES);}/* this loads a bitmap file (*.bmp) and uploads it to textures */void LoadBitmapToStruct(char *src_file, struct BitmapStruct *array, int width, int height, int frames) {   SDL_Surface *image, *temp;   int i, x = 0, y = 0, w, h;   SDL_Rect area;   GLuint texture;   image = SDL_LoadBMP(src_file);   if (!image)      exit(1);    SDL_SetColorKey(image, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(image->format, 255, 0, 255));   w = power_of_two(width);   h = power_of_two(height);   for (i = 0; i < frames; i++) {      array.texcoord[0] = 0.0f;      array.texcoord[1] = 0.0f;      array.texcoord[2] = (GLfloat)width / w;      array.texcoord[3] = (GLfloat)height / h;   /* blit from bitmap to temporary surface */      temp = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, bpp, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);      area.x = x * width;      area.y = y * height;      area.w = width;      area.h = height;      SDL_BlitSurface(image, &area, temp, NULL);   /* create GL texture */      glGenTextures(1, &texture);      glBindTexture(GL_TEXTURE_2D, texture);      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp->pixels);      array.texture_number = texture;      SDL_FreeSurface(temp);      if ((x * width) + width < image->w)         x++;      else {         y++;         x = 0;      }   }   SDL_FreeSurface(image);}static int power_of_two(int input) {   int value = 1;   while (value < input)      value <<= 1;   return value;}/* this replaced the SDL_BlitSurface() function */void GLBlit(struct BitmapStruct *array, int x, int y, int w, int h) {   GLfloat texMinX, texMinY;   GLfloat texMaxX, texMaxY;/* easier to understand the coordinates for the texture */   texMinX = array->texcoord[0];   texMinY = array->texcoord[1];   texMaxX = array->texcoord[2];   texMaxY = array->texcoord[3];   glBindTexture(GL_TEXTURE_2D, array->texture_number);/* draw polygon */   glBegin(GL_TRIANGLE_STRIP);   glTexCoord2f(texMinX, texMinY); glVertex2i(x,     y);   glTexCoord2f(texMaxX, texMinY); glVertex2i(x + w, y);   glTexCoord2f(texMinX, texMaxY); glVertex2i(x,     y + h);   glTexCoord2f(texMaxX, texMaxY); glVertex2i(x + w, y + h);   glEnd();}/* easier call to GLBlit() */void BlitTile(int tile, int x, int y) {   GLBlit(&GFX_Tiles[tile], x, y, TILE_SIZE, TILE_SIZE);}


Assuming all tiles are 32x32, you can now draw tiles by just doing:

BlitTile(number_for_the_file, xpos, ypos);

I hope this works. :)
SDL in hardware mode may be usable (AFAIK hardware is only supported in fullscreen) but it will come down to the fill rate of your video card which can vary greatly between cards.

One thing I can see that might speed things up (admitedly probably not much if at all) is calculating the source rectangle for a sprite once during initialization instead of each time you draw a sprite (potentially quite a few times per frame).

That being said, OpenGL is definitely the way to go with something of this size (meaning 1280x1024). You will also find that OpenGL will give you some more options with alpha blending, scaling, rotation and even primitives like lines and circles since they can all be done in hardware.
Evillive2
Well, I already switched to OpenGL now, no turning back... :)

One problem with your code though:

In LoadBitmapToStruct ( )
/* blit from bitmap to temporary surface */      temp = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, window.bpp, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);      area.x = x * width;      area.y = y * height;      area.w = width;      area.h = height;      SDL_BlitSurface(image, &area, temp, NULL);   /* create GL texture */      glGenTextures(1, &texture);      glBindTexture(GL_TEXTURE_2D, texture);      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp->pixels); // <- CRASH!*!*!**!*!*!*!*!      array.texture_number = texture;      SDL_FreeSurface(temp);


The marked line makes my application quit instantly with no errors from SDL or anything. Just quits. What could be the problem? The rest works fine. If I comment that line out, I can see OpenGL drawing the tiles, just they are white because of the missing texture. Any help?

Thanks for the help so far.
Sounds like temp->pixels isn't pointing to anything - check it against NULL. While you're there, check the error code returned by SDL_BlitSurface - if it's -1, then the blit failed. Also you could check temp itself against NULL after creating it, as SDL_CreateRGBSurface returns NULL on failure.

Then at least you'll know where the problem is.
Okay, thanks, I did that.

Turns out, temp does not point to NULL anywhere, but in fact this returns an error:

if ( temp == NULL )		  return_error ( "Failed to create surface..." );      area.x = x * width;      area.y = y * height;      area.w = width;      area.h = height;      	  if ( !SDL_BlitSurface(image, &area, temp, NULL))		return_error ( "Failed to create texture..." ); // this returns an error   /* create GL texture */      glGenTextures(1, &texture);      glBindTexture(GL_TEXTURE_2D, texture);


What is wrong with SDL_BlitSurface(image, &area, temp, NULL) though? Seems right to me.

EDIT: Sorry, that got me again. The test is wrong, BlitSurface returns 0 on SUCCESS and -1 on FAILURE. I changed that and now no errors are returned, yet still the game quits when calling that line marked above with glTexImage2d.

This topic is closed to new replies.

Advertisement