3
\$\begingroup\$

I have yet another question regarding SDL2.

I have some drawSprite & drawBackround & drawText functions, all draw to the screen. They do this by creating a new surface, turning it into a new texture, rendering the texture, then destroying/freeing everything.

This worked fine until I wanted to put it in a very fast loop. There was a slight delay (or something) which caused images on the screen to flicker. (It would draw a background, draw sprites, draw text, then loop. So this caused the sprites to flicker.)

I looked up the issue, and found that you shouldn't create and destroy a texture every single frame, AKA allocate and deallocate memory every single frame. So, I created some a global texture and surface variables, with the intent to re-assign these variables every time I called any of the drawing functions, and then destroy them when the program closed. (Reduce, Reuse, Recycle!)

This did not work. My memory required by my program ballooned upward into the gigabytes, which made it totally unresponsive. Through some trial and error, I found that I STILL had to run DestroyTexture and FreeSurface functions at the end of each drawing function. I don't understand why this is, since I'm reusing the variables.

Anyways, once I added those back in I've found that my program is working but is MUCH SLOWER in rendering than before!!! Why is this??? What did I do wrong? (again)

\$\endgroup\$
2
\$\begingroup\$

Your drawSprite and drawBackground functions definitely shouldn't be allocating any memory. What are you doing inside them that requires you to allocate memory?

Anyways, I'll start by explaining how manual memory management works. The functionality behind SDL_CreateTexture & SDL_DestroyTexture is very similar to how new and delete work in C++.

Let's do an example:

SDL_Texture* texture = SDL_CreateTexture(...);

Here we create a texture. One texture exists in our program.

SDL_DestroyTexture(texture);

Here we destroy the created texture. No textures exist anymore.
Now let's change things up a little:

texture = SDL_CreateTexture(...);
texture = SDL_CreateTexture(...);

Here we create two textures, so two textures exist in total. But now, if we call SDL_DestroyTexture, we will only delete the latter created texture, since that's what texture points to! There's also no way to access the result of the first SDL_CreateTexture call, so we have a memory leak. A general rule is that there has to be a matching SDL_DestroyTexture call for each SDL_CreateTexture.

Now, text rendering is a little more tricky. Textures required for text rendering are something that change relatively often, and do indeed need to be created as they are rendered. However, there's no need to do this every frame, since it's not likely that you're rendering text for just a single frame.

I personally use an LRU cache for text rendering in SDL. Essentially, in your text rendering function, you check if there exists an item with the same color, text and size in the cache, and if it does, you just fetch the cache item, and blit the texture inside it to the screen. If there isn't a cache item for the text, you create one by using the correct TTF_* calls. That way if you render the same text next frame, there already exists a texture to be used for the rendering. We use an LRU cache with a fixed size so that we don't end up leaking memory. I think I keep 200 cache items around at once in my game.

As a last note, globals are definitely not the way to go. I'm unsure about why you are allocating stuff inside the very basic sprite and background rendering functions, since you should probably just preload the textures you need to the objects that use them at start up.

struct Sprite
{
    float X, Y;
    SDL_Texture* texture;

    ~Sprite() { SDL_DestroyTexture(texture); }
};

Sprite player { 0.0f, 0.0f, SDL_CreateTexture(...) };

while (runGame)
{
    handleEvents();
    updateStuff();

    render(player);
}

Essentially something like this for sprites.

\$\endgroup\$
  • \$\begingroup\$ Thanks for the suggestion! I'll consider adapting this to my system. In my drawing functions, I create a new texture, render that texture, and then destroy that texture. So my idea was to have a SINGLE and GLOBAL SDL_Texture* called "allocatedTexture" which could be created ONCE upon startup and reused constantly by all the drawing functions throughout the entirety of the program, and then destroyed ONCE when the program exits. Why does this cause a memory error? I'm only CREATING one texture, not a bajillion. So why do I need to destroy it to prevent a memory error? \$\endgroup\$ – Eyesight Technology Jan 5 '18 at 17:44
  • \$\begingroup\$ I'd have to see some actual code to identify a memory leak. If you call SDL_CreateTexture outside of the game loop, I don't see why it would leak memory. \$\endgroup\$ – Tyyppi_77 Jan 5 '18 at 17:48
  • \$\begingroup\$ Oh oof. Haha! I'm a newb. I thought creating a texture was declaring a new SDL_Texture, and SDL_CreateTexture didn't create any new memory. So my question is this: How can I re-assign an already created texture? \$\endgroup\$ – Eyesight Technology Jan 5 '18 at 17:51
  • \$\begingroup\$ I'm not sure what you mean by reassigning, but yeah, a variable declaration allocates no memory. \$\endgroup\$ – Tyyppi_77 Jan 5 '18 at 17:52
  • \$\begingroup\$ @Tyyppi__77. Ok, I understand now. So you create a texture like this: SDL_Texture* existingTexture1 = SDL_CreateTexture(blahblahblah); int otherVariable = 0; I want to reassign a texture as if it were a variable. (I guess the proper term would be redefine it?) Like, take the same memory allocation and reassign it. existingTexture = IMG_Load(something.else); otherVariable = 50; \$\endgroup\$ – Eyesight Technology Jan 5 '18 at 17:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.