Increased CPU usage over time.

Hello guys,

First of all, sorry if this has been asked before, but I couldn’t find any answer related to my issue.
So I have this application, rendering to screen every 40ms (25fps).
One of the elements being rendered is a “wall clock”. This is the piece of code that takes care of creating the textures and copy it to the renderer.

if (viewport->main_window->common_camera_label_font && viewport->timestamp_text) {
        SDL_Color rgbwhite = {191, 191, 191};
        SDL_Surface* textsurface = TTF_RenderText_Blended(viewport->main_window->common_camera_label_font, viewport->timestamp_text, rgbwhite); 
        SDL_Texture* timestamp_texture = SDL_CreateTextureFromSurface(viewport->main_window->window_renderer, textsurface);

        SDL_Rect timestamp_box = {
            viewport->x_pos + viewport->width - textsurface->w - 5,
            viewport->y_pos + 5,
            textsurface->w,
            textsurface->h
        };
        SDL_RenderCopy(renderer, timestamp_texture, NULL, &timestamp_box);
        SDL_DestroyTexture(timestamp_texture);
        SDL_FreeSurface(textsurface);
    }

SLD_RenderPresent() is then called later once I’ve gone through all UI elements.

This seems to work fine, but over a long period of time, there’s a CPU usage increase (about 10% over a day). If I disable this piece of code, that does not happen.

If I would have to guess, it looks like there’s some internal list that gets bigger and bigger and iterating over it get more and more expensive?
idk… just spitballing.

Any ideas on what might be happening and the best way to fix it?
TIA

I’m told memory fragmentation isn’t a real problem and one shouldn’t be scared by it, but…

creating a texture and destroying it every time you render seems… deadly
try to cache the texture object, reusing it, make it big enough to handle most cases, then watch for these events:

  • SDL_EVENT_RENDER_TARGETS_RESET or SDL_RENDER_TARGETS_RESET
  • SDL_EVENT_RENDER_DEVICE_RESET or SDL_RENDER_DEVICE_RESET
2 Likes

Just do not destroy textures every draw. The best way is to create some cache and wrappers to check if the state has changed, then update the texture (for example, text has changed, or font size, etc.).
Also you are right, there is an internal linked list for textures (and other objects).
See the code:

static int SDL_DestroyTextureInternal(SDL_Texture *texture, bool is_destroying)
{
...
        if (texture == renderer->target) {
            SDL_SetRenderTargetInternal(renderer, NULL); // implies command queue flush

            if (texture == renderer->logical_target) {
                // Complete any logical presentation
                SDL_RenderLogicalPresentation(renderer);
                FlushRenderCommands(renderer);
            }
        } else {
            FlushRenderCommandsIfTextureNeeded(texture); // <-----------!!!!!!!!!
        }
    }
...
1 Like

While you have the code running, check your task manager. Does the RAM used by your program also increase over time?

1 Like

I have to mention that there is an order that those functions are meant to be called in, and I’m surprised that you are getting the clock to render to the screen at all.
It is good practice to free the surface pointer since the data is copied to the texture like you are doing, but ideally you would have the texture pointer ‘exist’ at least until the SDL_RenderPresent(renderer) function is called.

Here’s an example of how I would expect the program to flow:

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <string>

int main()
{
	SDL_Init(SDL_INIT_VIDEO);
	TTF_Init();

	SDL_Window * window = SDL_CreateWindow("title", 0, 0, 900, 900, SDL_WINDOW_RESIZABLE);
	SDL_Renderer * screen = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);

	// Inititalize object states
	SDL_Rect pos = {10, 10, 300, 50};
	SDL_Color color = {30, 200, 30, 255};
	TTF_Font * font = TTF_OpenFont("roboto.ttf", 42);
	if(!font)
	{
		SDL_Log("Failed to open font");
	}

	// allow the texture to exist for an entire loop,
	// see 'update elements' section in code below.
	SDL_Texture * texture = NULL;

	size_t frameCount = 0;
	size_t tick = 0;
	size_t anotherSecond = tick + 1000;
	bool run = true;
	while(run)
	{
		SDL_Event ev;
		while(SDL_PollEvent(&ev))
		{
			switch(ev.type)
			{
				case SDL_KEYDOWN:
					switch(ev.key.keysym.sym)
					{
						case SDLK_ESCAPE:
							run = false;
							break;
					}
					break;
				case SDL_QUIT:
					run = false;
					break;
			}
		}
		// update elements

		frameCount ++;
		tick = SDL_GetTicks();
		if(tick > anotherSecond)
		{
			if(texture)
			{
				SDL_DestroyTexture(texture);
			}
			std::string fpsStr = "FPS: ";
			fpsStr += std::to_string(frameCount);
			SDL_Surface * temp = TTF_RenderText_Solid(font, fpsStr.c_str(), color);
			if(temp)
			{
				texture = SDL_CreateTextureFromSurface(screen, temp);
				pos.w = temp->w;
				pos.h = temp->h;
				SDL_FreeSurface(temp);
				SDL_RenderCopy(screen, texture, NULL, &pos);
			}
			else
			{
				SDL_Log("TTF failed to create text surface");
			}
			anotherSecond += 1000;
			frameCount = 0;
		}

		// draw elements
		SDL_SetRenderDrawColor(screen, 110, 120, 125, 255);
		SDL_RenderClear(screen);
		SDL_SetRenderDrawColor(screen, 255, 200, 50, 255);
		SDL_RenderCopy(screen, texture, NULL, &pos);
		SDL_RenderPresent(screen);
	}
	SDL_DestroyTexture(texture);
	TTF_Quit();
	SDL_Quit();
}

To run this please replace the font name in TTF_OpenFont("roboto.ttf", 42); with one that you have available.

1 Like

@elefantinho @rmg.nik @Sward
Thank you all for your valuable input.
I changed the code to keep the texture in memory and just create a new surface everytime the text (timestamp) changes. So a bit smarter and less brute force.
It has been running for a few hours and I don’t notice any CPU usage increase.

@Sward reagrding the destruction of the texture before SDL_RenderPresent() was called. Yes, indeed it seemed to work fine. When reading the documentation for SDL_RenderCopy() it says “Copy a portion of the texture to the current rendering target.” so I assumed that I could discard the texture since a copy was created. So…

1 Like

I was certain that your method was undefined behavior.
It took me double checking the behavior in the source code and running several test versions to get my brain to work with your method.

Having spent a good chunk of time on it, you are correct, functionally there is little difference between calling SDL_RenderCopy() then destroying the texture before SDL_RenderPresent() and my suggestion of holding onto the texture until after SDL_RenderPresent().

As long as the textures aren’t being shared between objects, it boils down to a stylistic choice.
I do actually tend to use a lot of texture-sharing and static images so I might not change my own style right away, but this was something interesting to learn for temporary textures.

Thank you for the refresher.

1 Like