Rendering stutter & High memory usage

I created a little menu for games 'nd music and such… It’s far from finished but here is a demo:
https://mega.co.nz/#!mp9lCJKC!Vbw-H7RzhbFX9roInJ_Qo_Yt3uUPuaZB2WB9Z9vP1ds
The controls are the left and right buttons on your keyboards.

Now, here’s problem 1: when launching the program there are 5 menu’s you can scroll to. The first time you scroll to a new menu, it tends to stutter.
After you’ve seen the menu 1 time, it doesn’t stutter anymore.

Problem 2: the program is taking up 300mb of RAM while I’m not loading anything into ram at all except for some surfaces that are converted into textures but cleared when done.
However, it doesn’t really go past the ~300mb over time so I don’t think it’s a memory leak.
I can’t detect any form of memory leak in my code either, so I’ll just ask whether other people suffer from this too.

Here’s the rendering code:

Code:
int gfxdraw()
{
//Main loop flag
bool quit = false;
//Event handler
SDL_Event e;
SDL_Rect test;
int timer1 = 0;
int timer2 = 0;
int busy = 0;
int x = 0;
int lasttime = 0;
float movemenu = 0;
while( !quit )
{
//While application is running
//Handle events on queue
while( SDL_PollEvent( &e ) != 0)
{
//User requests quit
if( e.type == SDL_QUIT )
{
quit = true;
}
handleEvent( e );
}
SDL_RenderClear( gRenderer );
//menucontrol
if(menuvalue != oldmenuvalue)
{
if(busy == 1)
{
if(menuvalue < oldmenuvalue)
{
timer2 = SDL_GetTicks();
x += timer2 - timer1;
movemenu = SCREEN_WIDTH * oldmenuvalue + 100.f * oldmenuvalue - (SCREEN_WIDTH + 100.f) / 400.f * x;
test.h = texh[0];
test.w = texw[0];
test.x = texx[0] - oldmenuvalue * ((texw[0] - SCREEN_WIDTH) / 4.f) + (texw[0] - SCREEN_WIDTH) / 4.f / 400.f * x;
test.y = texy[0];
SDL_RenderCopy( gRenderer, gTexture[0], NULL, &test );
for(int i = 1; i < 45; i++)
{
test.h = texh[i];
test.w = texw[i];
test.x = texx[i] - movemenu;
test.y = texy[i];
SDL_RenderCopy( gRenderer, gTexture[i], NULL, &test );
}
timer1 = timer2;
if(x >= 400)
{
oldmenuvalue = menuvalue;
busy = 0;
x = 0;
}
}
if(menuvalue > oldmenuvalue)
{
timer2 = SDL_GetTicks();
x += timer2 - timer1;
movemenu = SCREEN_WIDTH * oldmenuvalue + 100.f * oldmenuvalue + (SCREEN_WIDTH + 100.f) / 400.f * x;
test.h = texh[0];
test.w = texw[0];
test.x = texx[0] - oldmenuvalue * ((texw[0] - SCREEN_WIDTH) / 4.f) - (texw[0] - SCREEN_WIDTH) / 4.f / 400.f * x;
test.y = texy[0];
SDL_RenderCopy( gRenderer, gTexture[0], NULL, &test );
for(int i = 1; i < 45; i++)
{
test.h = texh[i];
test.w = texw[i];
test.x = texx[i] - movemenu;
test.y = texy[i];
SDL_RenderCopy( gRenderer, gTexture[i], NULL, &test );
}
timer1 = timer2;
if(x >= 400)
{
oldmenuvalue = menuvalue;
busy = 0;
x = 0;
}
}
}
else
{
timer1 = SDL_GetTicks();
busy = 1;
lasttime = 1;
}
}
if(oldmenuvalue == menuvalue || lasttime == 1)
{
test.h = texh[0];
test.w = texw[0];
test.x = texx[0] - ((2560.f / 1920.f * SCREEN_WIDTH - SCREEN_WIDTH) / 4.f * oldmenuvalue);
test.y = texy[0];
SDL_RenderCopy( gRenderer, gTexture[0], NULL, &test );
for(int i = 1; i < 45; i++)
{
test.h = texh[i];
test.w = texw[i];
test.x = texx[i] - SCREEN_WIDTH * oldmenuvalue - 100.f * oldmenuvalue;
test.y = texy[i];
SDL_RenderCopy( gRenderer, gTexture[i], NULL, &test );
}
if(lasttime == 1) lasttime = 0;
}
SDL_RenderPresent( gRenderer );
}
return 0;
}

Thanks in advance,

Luca

I will need to see your whole code to be able to help you which means everything from loading textures, handling event from the keyboard, rendering and so on.

I have read through your code now. I can’t find anything that would cause a memory leak or such. But, there’s some stuff that I’m thinking of while viewing your code, which I will talk about in this post.

First of all, the loadMedia is kind of a mess, if I may say so. I refer specifically to this:

Code:

  if(count3 == 2)
  {
     if(count4 == 14)
     {
        count4 = 10;
        count3 = 0;
     }
     else
     {
     count3 = 0;
     count4++;
     }
  }
  else
  {
  count3++;
  } 

I don’t know if you are happy with how it looks but in my opinion there has to be a simpler way of doing… Hm… whatever you’re doing. Also, you’re having two for loops, where the first is looping from 0 to 15 and the second is looping from 15 to 45. Why not have only one for loop that loops from 0 to 45?

And now some questions regarding the rest of the code:

  1. I see that you’ve allocated a SDL_Texture array of 100 textures. Do you use all those or are the size of 100 just in case you need to load 100 textures? You should lower this size to however many textures you need.
  2. I see in your gfxdraw function that you’re looping through 45 textures (the for loop at the end of the code) but there’s only 16 textures in the file you linked to in your first post. Why is that?
    If you have only loaded 16 textures, it means that you’re rendering textures that aren’t loaded/created, since your texture array contains 100 SDL_Texture’s. Also, the texture list isn’t initialized at startup which means that the textures that aren’t loaded/created contains garbage which can cause a lot of weird bugs if you try to render or do something else with them.

And regarding the memory usage when the program is executed. There’s some things you can do to lower the memory usage. The first thing that comes to mind regards the alpha textures (the rectangles in red, blue, green, yellow and such) that you’re lowering the alpha value on and then render. If your only goal is to render them as half transparent rectangles, you can skip using SDL_Texture’s for that totally and switch to using quads (SDL_Rect’s) instead. So what you do is this:

Skip loading the rectangle textures, create the quads and render them, and when you choose the color for them, set the alpha channel of the color to be something around the alpha value you’re currently using for your textures. So, instead of loading and rendering 16 textures, you will only render 6 textures, which is the loading texture, the background texture and the 4 gradient textures that is rendered between the slides (the one that fades from one color into another). You should notice a difference in memory usage by doing this.

Also worth noting:
Textures are only loaded into vram when calling them 1 time by the renderer,
this is when scrolling the menu with intervals of 10 seconds in between scrolling
[Image: http://jesusfuck.me/di/VYL1/Untitled3.png ]

Here is a screenshot of the time it takes for a frame to render (in milliseconds) when you scroll the menu.
Why does it do this :?
[Image: http://jesusfuck.me/di/Q2F6/Untitled2.png ]

gfxMain.h & gfxCords.h only pass on the renderer and the co?rdinates for textures.

And of course the key handle:

Code:
void handleEvent(SDL_Event& e)
{
//If a key was pressed
if( e.type == SDL_KEYDOWN && e.key.repeat == 1 )
{
//Adjust the velocity
switch( e.key.keysym.sym )
{
case SDLK_UP: ; break;
case SDLK_DOWN: ; break;
case SDLK_LEFT: if(menuvalue != 0 && menuvalue == oldmenuvalue)menuvalue–; break;
case SDLK_RIGHT: if(menuvalue != 4 && menuvalue == oldmenuvalue)menuvalue++; break;
}
}
//If a key was pressed
if( e.type == SDL_KEYDOWN && e.key.repeat == 0 )
{
//Adjust the velocity
switch( e.key.keysym.sym )
{
case SDLK_UP: launchexe = true; break;
case SDLK_DOWN: ; break;
case SDLK_LEFT: if(menuvalue != 0 && menuvalue == oldmenuvalue)menuvalue–; break;
case SDLK_RIGHT: if(menuvalue != 4 && menuvalue == oldmenuvalue)menuvalue++; break;
}
}

}

Naith wrote:

I have read through your code now. I can’t find anything that would cause a memory leak or such. But, there’s some stuff that I’m thinking of while viewing your code, which I will talk about in this post.

First of all, the loadMedia is kind of a mess, if I may say so. I refer specifically to this:

Code:

  if(count3 == 2)
  {
     if(count4 == 14)
     {
        count4 = 10;
        count3 = 0;
     }
     else
     {
     count3 = 0;
     count4++;
     }
  }
  else
  {
  count3++;
  } 

I don’t know if you are happy with how it looks but in my opinion there has to be a simpler way of doing… Hm… whatever you’re doing. Also, you’re having two for loops, where the first is looping from 0 to 15 and the second is looping from 15 to 45. Why not have only one for loop that loops from 0 to 45?

And now some questions regarding the rest of the code:

  1. I see that you’ve allocated a SDL_Texture array of 100 textures. Do you use all those or are the size of 100 just in case you need to load 100 textures? You should lower this size to however many textures you need.
  2. I see in your gfxdraw function that you’re looping through 45 textures (the for loop at the end of the code) but there’s only 16 textures in the file you linked to in your first post. Why is that?
    If you have only loaded 16 textures, it means that you’re rendering textures that aren’t loaded/created, since your texture array contains 100 SDL_Texture’s. Also, the texture list isn’t initialized at startup which means that the textures that aren’t loaded/created contains garbage which can cause a lot of weird bugs if you try to render or do something else with them.

And regarding the memory usage when the program is executed. There’s some things you can do to lower the memory usage. The first thing that comes to mind regards the alpha textures (the rectangles in red, blue, green, yellow and such) that you’re lowering the alpha value on and then render. If your only goal is to render them as half transparent rectangles, you can skip using SDL_Texture’s for that totally and switch to using quads (SDL_Rect’s) instead. So what you do is this:

Skip loading the rectangle textures, create the quads and render them, and when you choose the color for them, set the alpha channel of the color to be something around the alpha value you’re currently using for your textures. So, instead of loading and rendering 16 textures, you will only render 6 textures, which is the loading texture, the background texture and the 4 gradient textures that is rendered between the slides (the one that fades from one color into another). You should notice a difference in memory usage by doing this.

I know, I know, the code is a mess. But organizing the code isn’t my top priority yet, I’m new to SDL and prefer to get things working before I inspect the code to see what I can scrape and what could be simplyfied/optimized. That piece of code you posted is an algorithm to check if texture10/11/12/13/14.png should be used for the button shape texture.

A:

  1. I just typed in 100 because it seemed enough at the time, I just wanted to make sure I had enough textures. I may even go past 100 when I put all icons in place.
  2. All button textures you see use the same image file per menu, the reason I made a texture for each button shape is because it was more readable with the way my co?rdinating system works. I’m going to change that in the future.
  3. I use single color textures for button shapes, that could be done with colored rectangles indeed. However, the reason I didn’t do that is because those button shapes don’t have to be a single color, one of my friends is creating a theme for the menu with non-single color button shapes.

Thanks for the help, I’ll try it out and see if it works.

Code:
#include <stdio.h>
#include
#include “gfxMain.h”
#include “gfxCords.h”

float SCREEN_WIDTH; //Screen dimension constants
float SCREEN_HEIGHT;

bool init(); //Starts up SDL and creates window
SDL_Texture* loadTexture( std::string path ); //Loads individual image as texture
void gfxclose(); //Frees media and shuts down SDL
int threadFunction( void* data ); //Loading screen renderer

SDL_Texture* gTexture[100]; //All gfxMainMenu textures
SDL_Texture* gLoadingtexture = NULL; //Loadingtexture
SDL_Window* gWindow = NULL; //The window we’ll be rendering to
SDL_Renderer* gRenderer = NULL; //The window renderer
SDL_sem* gDataLock = NULL; //Data lock to check if program has loaded(used in threadFunction)

bool loaded = false;

bool init()
{
bool success = true; //Initialization flag

if( SDL_Init( SDL_INIT_VIDEO ) < 0 )		//Initialize SDL
{
	printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError() );
	success = false;
}
else
{
	//Set texture filtering to linear
	if( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ) )
	{
		printf( "Warning: Linear texture filtering not enabled!" );
	}
	SDL_ShowCursor(SDL_DISABLE);
	SDL_DisplayMode current;
	SDL_GetDesktopDisplayMode( 0 , &current);
	SCREEN_WIDTH = (float)current.w;
	SCREEN_HEIGHT = (float)current.h;
	//Create window
	gWindow = SDL_CreateWindow( "LDX", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, current.w , current.h, SDL_WINDOW_SHOWN );
	
	if( gWindow == NULL)
	{
		printf( "Window could not be created! SDL Error: %s\n", SDL_GetError() );
		success = false;
	}
	

	else
	{
		//Create renderer for window
		int fullscreentrue = SDL_SetWindowFullscreen(gWindow, SDL_WINDOW_FULLSCREEN);
		if(fullscreentrue != 0)
		{
			printf("Window could not be set to fullscreen! SDL Error: %s\n", SDL_GetError());
			success = false;
		}
		else
		{
			gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
			if( gRenderer == NULL )
			{
				printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError() );
				success = false;
			}
			else
			{
				//Initialize renderer color
				SDL_SetRenderDrawColor( gRenderer, 0x00, 0x00, 0x00, 0xFF );

				//Initialize PNG loading
				int imgFlags = IMG_INIT_PNG;
				if( !( IMG_Init( imgFlags ) & imgFlags ) )
				{
					printf( "SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError() );
					success = false;
				}
			}
		}
	}
}

return success;

}

void gfxclose()
{
//Free loaded image
for(int i = 0; i < 100; i++)
{
SDL_DestroyTexture( gTexture[i] );
gTexture[i] = NULL;
}
SDL_DestroyTexture( gLoadingtexture );
gLoadingtexture = NULL;
SDL_DestroySemaphore( gDataLock );
//Destroy window
SDL_DestroyRenderer( gRenderer );
SDL_DestroyWindow( gWindow );
gWindow = NULL;
gRenderer = NULL;

//Quit SDL subsystems
IMG_Quit();
SDL_Quit();

}

SDL_Texture* loadTexture( std::string path )
{
//The final texture
SDL_Texture* newTexture = NULL;

//Load image at specified path
SDL_Surface* loadedSurface = IMG_Load( path.c_str() );
if( loadedSurface == NULL )
{
	printf( "Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError() );
}
else
{
	SDL_SemWait(gDataLock);
	//Create texture from surface pixels
    newTexture = SDL_CreateTextureFromSurface( gRenderer, loadedSurface );
	if( newTexture == NULL )
	{
		printf( "Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError() );
	}
	//Get rid of old loaded surface
	SDL_FreeSurface( loadedSurface );
	loadedSurface = NULL;
	SDL_SemPost(gDataLock);
}

return newTexture;

}

int secondthread(void* data)
{
bool quit = false;
SDL_Rect texLoadCords;
texLoadCords.h = texLoadh;
texLoadCords.w = texLoadw;
texLoadCords.x = texLoadx;
texLoadCords.y = texLoady;

while(!quit)
{
	SDL_SemWait(gDataLock);
	SDL_RenderClear( gRenderer );
	SDL_RenderCopy( gRenderer, gLoadingtexture, NULL, &texLoadCords );
	if(loaded) quit = true;
	
	SDL_RenderPresent( gRenderer );
	SDL_SemPost(gDataLock);
}
return 0;

}

bool loadMedia()
{
//Loading success flag
bool success = true;
char string1[64] = “texture”;
char string2[64] = “.png”;
char integerstring[32] = “fill”;
int count3 = 0;
int count4 = 10;
//Load PNG texture
SDL_BlendMode blending;
gDataLock = SDL_CreateSemaphore(1);
gLoadingtexture = loadTexture(“LDX.png”);

SDL_Thread* threadID = SDL_CreateThread(secondthread, "LoadGFX", (void*)NULL);
for(int i = 0; i < 15; i++)
{
	sprintf(integerstring, "%d", i);
	strcat(string1, integerstring);
	strcat(string1, string2);
	gTexture[i] = NULL;
	gTexture[i] = loadTexture( string1 );
	strcpy(string1, "texture");
	if( gTexture[i] == NULL )
	{
		printf( "Failed to load texture image!\n" );
		success = false;
	}
	else
	{
		if(i < 10 && i > 0)
		{
			SDL_SetTextureBlendMode( gTexture[i], SDL_BLENDMODE_BLEND );
			SDL_SetTextureAlphaMod( gTexture[i], 100 );
		}
		if(i < 15 && i > 9)
		{
			SDL_SetTextureBlendMode( gTexture[i], SDL_BLENDMODE_BLEND );
			SDL_SetTextureAlphaMod( gTexture[i], 70 );
		}
	}
	printf("texture created \n");
}
for(int i = 15; i < 45; i++)
{
	sprintf_s(integerstring, "%d", count4);
	strcat_s(string1, integerstring);
	strcat_s(string1, string2);
	gTexture[i] = NULL;
	gTexture[i] = loadTexture( string1 );
	strcpy_s(string1, "texture");
	if( gTexture[i] == NULL )
	{
		printf( "Failed to load texture image!\n" );
		success = false;
	}
	else
	{
		SDL_SetTextureBlendMode( gTexture[i], SDL_BLENDMODE_BLEND );
		SDL_SetTextureAlphaMod( gTexture[i], 70 );
	}
	if(count3 == 2)
	{
		if(count4 == 14)
		{
			count4 = 10;
			count3 = 0;
		}
		else
		{
		count3 = 0;
		count4++;
		}
	}
	else
	{
	count3++;
	}
	printf("texture created2 \n");

}
SDL_SemWait(gDataLock);
loaded = true;
SDL_SemPost(gDataLock);
SDL_WaitThread( threadID, NULL );
return success;

}

int gfxload()
{
gLoadingtexture = NULL;
gLoadingtexture = loadTexture(“LDX.png”);

return 0;

}

int gfxinit()
{
if( !init() )
{
printf( “Failed to initialize!\n” );
}
else
{
gfxcords();
//Load media
if( !loadMedia() )
{
printf( “Failed to load media!\n” );
}
else
{

		gfxdraw();
	}
}
gfxclose();
return 0;

}

I think I kind of found what my first problem is about, when launching my program it uses ~ 20MB video memory. When scrolling one to the right, the video memory usage goes up.
When scrolling another one to the right, the video memory usage goes up again until it reaches about 300mb video memory usage.
This means textures aren’t loaded into video memory before I’ve rendered them at least once.
How can I make sure all textures are loaded into video memory beforehand?

I just solved 1 of the problems, the stuttering, by setting gTexture[45 to 99] = NULL.
I previously just left them blank, setting them to NULL seems to solve the problem. (Even though they aren’t rendered)
However, the program is still taking up 300 MB of memory.

Loose call, my eyes tricked me.

Hm, I don’t know how many textures you’re rendering in each slide at the moment, but I guess the long time (in milliseconds) it takes to render a menu slide is because the amount of textures being rendered.

Btw, I noticed a thing you haven’t added in your code, which I think you should. I think you should add a ’ SDL_Delay(1) ’ at the end of your main loop, which will let the computer do something else for a millisecond, which will lower the CPU-usage of the program.

Secondly, do you render the menu slides (and their textures) that aren’t visible on the screen or only the current one the user is at? If you do (rendering them all, I mean), I think you should fix the code so that the only slide that are rendered is the current slide the user is on.

And just to specify what I mean with the SDL_Delay(1). You should add ’ SDL_Delay(1) ’ at the end of the while loop in the gfxdraw function, before ’ return 0 '. If you don’t want to add it now, remember to add it later when you start cleaning up- and optimizing your code. :slight_smile:

If I understand you correctly, you’re loading an image multiple times (from disk) and creating multiple surfaces / textures containing the same image. Correct?

Can you perhaps send me a link to the program (with all the needed textures) so I can check how much lag it is and such? And I don’t mean the version you linked to in your first post. I mean the newest version of the program.

Yes, but I was wrong. I rewrote the code a bit so the small button shapes aren’t loaded at all.
Now, none of the images get loaded or used twice and even then I still use 100mb RAM/VRAM.

Oh forgot to answer your question, I currently render all menus to the screen, visible or not.
It was easier that way and since the textures of the menu are ~3mb all together, and it just being a simple 2d application, I thought it wouldn’t make a big difference in resource usage.
Not expecting it would somehow take up 300mb vram for no reason, I think it is a bug in the png->surface where loading the same image multiple times results in an overflow or something.
Why? The stutter only seems to happen when the small button shapes slide into the screen, and those all use the same image.

I normally have vsync enabled so SDL_RenderPresent( gRenderer ); will function as a delay.

The only reason I can find to why there’s so much memory be?ng used is because the images are stored as raw data within RAM/VRAM. I’ll try to clip my textures into 1 texture and render only what I need. We’ll see if that makes any difference.