Frame rate drops when moving scene

Hello altogether,

first of all, I am kind of new to game development, so please don’t beat me for making beginners mistakes [Wink]
Also english is not my mother-tongue. If something I write is unclear to you, please feel free to ask and I’ll try to explain what I mean :slight_smile:

I have a tiled background where I add some objects (trees). (Here is a Screenshot: http://imgur.com/RaxKOrX.)
I can move the scene by pressing W, A, S or D.

When everything is standing still, the “Game” runs at 120 frames per second both with and without objects visible.
When I move the scene without any objects visible in it (only background), the frame rate drops to 60.
When showing some objects (which are moved with the scene, too) the frame rate drops down to 40 and I guess it would drop even further when I add more objects.

To show the objects, I use the two methods listed below.

Code:

void Map::BuildMap()
{
// Build background tiles
SDL_Rect rSourceTile;
SDL_Rect rTargetTile;

// Initialize SDL_Rects
rSourceTile.x = 0;
rSourceTile.y = 0;
rSourceTile.w = 0;
rSourceTile.h = 0;

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


int init_y = (int)(fOffsetY * -1) % iBackgroundTileSize;
int init_x = (int)(fOffsetX * -1) % iBackgroundTileSize;
int iSpaceToBorderRight = 0;
int iSpaceToBorderBottom = 0;
float fTmp = 0.f;

for (int y = init_y; y < iHeight; y += iBackgroundTileSize)
{
	for (int x = init_x; x < iWidth; x += iBackgroundTileSize)
	{
		rTargetTile.x = x;
		rTargetTile.y = y;

		iSpaceToBorderRight = iWidth - x;
		iSpaceToBorderBottom = iHeight - y;

		// Correct X and Width of the tiles (especially the bordertiles)
		if (x < 0)
		{
			fTmp = (x * -1) / ((float)iBackgroundTileSize / 100); // how much percent are the negative pixels on iBackgroundTileSize?
			fTmp = fTmp * ((float)MSurfBackgroundTile->w / 100); // how much pixels are that much percent?

			rSourceTile.x = (int)fTmp;
			rSourceTile.w = MSurfBackgroundTile->w - (int)fTmp;
			rTargetTile.x = 0;
			rTargetTile.w = iBackgroundTileSize - (x * -1);
		}
		else if (iSpaceToBorderRight < iBackgroundTileSize)
		{
			fTmp = iSpaceToBorderRight / ((float)iBackgroundTileSize / 100); // how much percent are iSpaceToBorderRight pixels on iBackgroundTileSize?
			fTmp = fTmp * ((float)MSurfBackgroundTile->w / 100); // how much pixels are that much percent?

			rSourceTile.x = 0;
			rSourceTile.w = (int)fTmp;
			rTargetTile.w = iSpaceToBorderRight;
		}
		else
		{
			rSourceTile.x = 0;
			rSourceTile.w = MSurfBackgroundTile->w;
			rTargetTile.w = iBackgroundTileSize;
		}

		// Correct Y and Height of the tiles (especially the bordertiles)
		if (y < 0)
		{
			fTmp = (y * -1) / ((float)iBackgroundTileSize / 100); // how much percent are the negative pixels on iBackgroundTileSize?
			fTmp = fTmp * ((float)MSurfBackgroundTile->h / 100); // how much pixels are that much percent?

			rSourceTile.y = (int)fTmp;
			rSourceTile.h = MSurfBackgroundTile->h - (int)fTmp;
			rTargetTile.y = 0;
			rTargetTile.h = iBackgroundTileSize - (y * -1);
		}
		else if (iSpaceToBorderBottom < iBackgroundTileSize)
		{
			fTmp = iSpaceToBorderBottom / ((float)iBackgroundTileSize / 100); // how much percent are iSpaceToBorderBottom pixels on iBackgroundTileSize?
			fTmp = fTmp * ((float)MSurfBackgroundTile->h / 100); // how much pixels are that much percent?

			rSourceTile.y = 0;
			rSourceTile.h = (int)fTmp;
			rTargetTile.h = iSpaceToBorderBottom;
		}
		else
		{
			rSourceTile.y = 0;
			rSourceTile.h = MSurfBackgroundTile->h;
			rTargetTile.h = iBackgroundTileSize;
		}

		SDL_BlitScaled(MSurfBackgroundTile, &rSourceTile, MSurface, &rTargetTile);
	}
}

}

void Map::BuildMapObjects()
{
int iObjectCount = vObjects.size();

MObj* pMObj;
SDL_Rect rDest;

int iMapPosX = (int)fOffsetX;
int iMapPosY = (int)fOffsetY;

for (int i = 0; i < iObjectCount; i++)
{
	pMObj = vObjects.at(i);
	MOSurface = pMObj->obj->GetObjectSurface();

	// Only draw if the object is inside the visible area of the map
	// TODO: Fix bug (disappearing object at top right border)
	if ((pMObj->x >= iMapPosX && pMObj->x <= iMapPosX + iWidth &&
			 pMObj->y >= iMapPosY && pMObj->y <= iMapPosY + iHeight) ||
			(pMObj->x +  MOSurface->w >= iMapPosX && pMObj->x +  MOSurface->w <= iMapPosX + iWidth &&
			 pMObj->y + MOSurface->h >= iMapPosY && pMObj->y + MOSurface->h <= iMapPosY + iHeight))
	{
		rDest.x = pMObj->x - iMapPosX;
		rDest.y = pMObj->y - iMapPosY;
		rDest.w = MOSurface->w;
		rDest.h = MOSurface->h;

		SDL_BlitSurface(MOSurface, NULL, MSurface, &rDest);
	}
}

}

It would be really nice, if you could help me stabilizing the frame rate and/or optimizing my code. :slight_smile:

Best regards,
Daniel

To show the objects, I use the two methods listed below.

I see you are using scaled blits in SDL 1.2 API, that kills the performance
for sure.

If you want to use SDL 1.2 surface API you should scale ALL your tiles
offline, for instance on window resize event.

Also it’s important that all of your surfaces are in the video display
format (use SDL_DisplayFormat() on the surfaces after loading them from the
sources images).

Anyway, please remember that SDL 1.2 API is mostly software, so if you want
better performances you could use SDL 2.0 hardware accelerated
SDL_Textures, that will give also the advantage that scaling operations are
"free" in the texture API and you don’t have to optimize them.–
Bye,
Gabry

Thank you for your reply!

Well, actually I am using SDL 2.0 (I forgot to mention that, sorry), so according to the Wiki CategoryAPI (API By Name) there is no SDL_DisplayFormat().

I think I did not describe correctly what my code does (since it’s only a part of the whole program).
The two methods I posted blit the images to the main surface of the map (MSurface) and another method (which I didn’t mention in the first post) creates a texture from MSurface and copies it to the main renderer.
I did it this way because I could not find any SDL-function in the wiki to scale a texture - now I see, I simply didn’t read well enough.
After reading your answer I re-read the Wiki SDL_RenderCopy() and saw a little something I did miss last time: “The texture will be stretched to fill the given rectangle.”

Thank you very much for pointing me in the right direction! :slight_smile:
I will try to rewrite the methods to use textures instead of surfaces. After doing so I will post an update on how the frame rate is using textures.

Wow! Using textures instead of surfaces did do the trick!
The frame rate is now stable between 115 and 120 :slight_smile:

But now I have another problem because of the textures (or because of my brain).
This is the method where the error occurs:

Code:

void Map::BuildMapObjects()
{
SDL_SetRenderTarget(MRenderer, MTexture);
SDL_Texture* BuildTexture = NULL;

int iObjectCount = vObjects.size();

MObj* pMObj;
SDL_Rect rDest;

int iMapPosX = (int)fOffsetX;
int iMapPosY = (int)fOffsetY;

for (int i = 0; i < iObjectCount; i++)
{
	pMObj = vObjects.at(i);
	BuildTexture = pMObj->obj->GetObjectTexture();

	// Only draw if the object is inside the visible area of the map
	// TODO: Fix bug (disappearing object at top and left border)
	if ((pMObj->x >= iMapPosX && pMObj->x <= iMapPosX + iWidth &&
			 pMObj->y >= iMapPosY && pMObj->y <= iMapPosY + iHeight) ||
			(pMObj->x + pMObj->w >= iMapPosX && pMObj->x + pMObj->w <= iMapPosX + iWidth &&
			 pMObj->y + pMObj->h >= iMapPosY && pMObj->y + pMObj->h <= iMapPosY + iHeight))
	{
		rDest.x = pMObj->x - iMapPosX;
		rDest.y = pMObj->y - iMapPosY;
		rDest.w = pMObj->w;
		rDest.h = pMObj->h;

		if (BuildTexture == NULL)
			printf("BuildTexture of %s is NULL: %s\n", pMObj->name.c_str(), SDL_GetError());
		else printf("BuildTexture of %s OK (not NULL)\n" , pMObj->name.c_str());
		
		if (SDL_RenderCopy(MRenderer, BuildTexture, NULL, &rDest) < 0)
			printf("RenderCopy Error at %s: %s\n", pMObj->name.c_str(), SDL_GetError());
		else printf("success\n");
		
	}
}
printf("\n");

SDL_SetRenderTarget(MRenderer, NULL);
SDL_DestroyTexture(BuildTexture);

}

When I first start the app, everything is okay, the objects (trees) are rendered and the console window tells me

Code:

BuildTexture of Tree1 OK (not NULL)
success
BuildTexture of Tree2 OK (not NULL)
success
BuildTexture of Tree3 OK (not NULL)
success
BuildTexture of Tree4 OK (not NULL)
success
BuildTexture of Tree5 OK (not NULL)
success

But as soon as I move the scene a bit the trees disappear and the output changes to

Code:

BuildTexture of Tree1 OK (not NULL)
RenderCopy Error at Tree1: Invalid texture
BuildTexture of Tree2 OK (not NULL)
RenderCopy Error at Tree2: Invalid texture
BuildTexture of Tree3 OK (not NULL)
RenderCopy Error at Tree3: Invalid texture
BuildTexture of Tree4 OK (not NULL)
RenderCopy Error at Tree4: Invalid texture
BuildTexture of Tree5 OK (not NULL)
RenderCopy Error at Tree5: Invalid texture

It seems that the texture is invalid although it clearly is not NULL, but I can’t figure out why.
Do you have an idea what causes the error?

Although the error occurs at that function, it clearly cannot be within that function.
Either the renderer’s data has been lost (is possible on some systems, with certain renderers; but I would hope SDL would be smart enough to report a more specific error in that case) or the texture is invalidated by your code in some other function.

And yes, scaling in surfaces is slow because surfaces use software rendering (in SDL2, you should only be using surfaces to hold image data that needs to be processed or saved for later; rendering should all be done with textures).------------------------
Nate Fries

SDL_SetRenderTarget(MRenderer, NULL);
SDL_DestroyTexture(BuildTexture);
}

I think the problem is that you always destroy the last texture you have
allocated, usually textures are never destroyed…

Gabriele Greco wrote:

I think the problem is that you always destroy the last texture you have allocated, usually textures are never destroyed…??

That was exactly the problem. I figured it out just a few minutes before reading your post :smiley:
Thank you anyways! :slight_smile:

By destroying BuildTexture I destroyed the main texture of the object (because it’s a pointer pointing to the main texture).
The scene is only rebuilt when anything has changed (e.g. when the scene is moved), so the texture didn’t exist after the initial build and the error occured when the scene tried to rebuild.
I removed SDL_DestroyTexture and it works fine now.------------------------
Kind regards,
Daniel