2
\$\begingroup\$

I've been creating a 2D game in sdl2 in C and within my main game loop, I have found a section of code that causes a massive memory leak in my program, roughly leaking 100MB of memory per second.

The relevant code is below:

renderTexture(textBox(buffer[0], TTF_OpenFont(fontLocation, 14), color, renderer, 200), renderer, 20, 240, NULL);
renderTexture(textBox(buffer[1], TTF_OpenFont(fontLocation, 14), color, renderer, 200), renderer, 287,240, NULL);
renderTexture(textBox(buffer[2], TTF_OpenFont(fontLocation, 14), color, renderer, 200), renderer, 554,240, NULL);
renderTexture(textBox(buffer[3], TTF_OpenFont(fontLocation, 14), color, renderer, 200), renderer, 821,240, NULL);

renderTexture(textBox(buffer[4], TTF_OpenFont(fontLocation, 14), color, renderer, 200), renderer, 20,450, NULL);
renderTexture(textBox(buffer[5], TTF_OpenFont(fontLocation, 14), color, renderer, 200), renderer, 287,450, NULL);
renderTexture(textBox(buffer[6], TTF_OpenFont(fontLocation, 14), color, renderer, 200), renderer, 554,450, NULL);
renderTexture(textBox(buffer[7], TTF_OpenFont(fontLocation, 14), color, renderer, 200), renderer, 821,450, NULL);

renderTexture(textBox(buffer[8], TTF_OpenFont(fontLocation, 14), color, renderer, 200), renderer, 20,660, NULL);
renderTexture(textBox(buffer[9], TTF_OpenFont(fontLocation, 14), color, renderer, 200), renderer, 287,660, NULL);
renderTexture(textBox(buffer[10], TTF_OpenFont(fontLocation, 14), color, renderer, 200), renderer, 554,660, NULL);
renderTexture(textBox(buffer[11], TTF_OpenFont(fontLocation, 14), color, renderer, 200), renderer, 821,660, NULL);

When this segment is placed into the main game loop as follows the memory leak occurs:

  while (testState == 1) {
        renderTexture(textBox(buffer[0], TTF_OpenFont(fontLocation, 14), color, renderer, 200), renderer, cooridnates[0][0], cooridnates[1][1], NULL);

        SDL_RenderPresent(renderer);

        while (SDL_PollEvent( & buttonPress)) {
            // get global mouse state for all subroutines to use
           // SDL_GetMouseState( & xMouse, & yMouse);
            if (SDL_GetMouseState( & xMouse, & yMouse) & SDL_BUTTON(1) == 1) {
                for(Z = 0; Z <= 11; Z++) {
                    if (isPressed(boundaries[Z][0],boundaries[Z][1],boundaries[Z][2],boundaries[Z][3]) == 1) {
                        boxState[Z] = 1;
                    } else boxState[Z] = 0;
                }
            }

            //asm
            if (xMouse > 10 && xMouse <= 210 && yMouse > 10 && yMouse <= 210) {
                if (SDL_GetMouseState( & xMouse, & yMouse) & SDL_BUTTON(1) == 1) {
                    renderTexture(asmSheet, renderer, 10, 10, & asmClips[0]);
                    saveFile = fopen(savePath, "w");
                    for(Z = 0; Z <= 11; Z++){
                        fprintf(saveFile,"@\n%s\n",buffer[Z]);
                    }
                    fclose(saveFile);
                } else {
                    renderTexture(asmSheet, renderer, 10, 10, & asmClips[1]);
                }
            } else {
                renderTexture(asmSheet, renderer, 10, 10, & asmClips[2]);
            }

            //start
            if (xMouse > 210 && xMouse <= 410 && yMouse > 10 && yMouse <= 210) {
                if (SDL_GetMouseState( & xMouse, & yMouse) & SDL_BUTTON(1) == 1) {
                    renderTexture(startSheet, renderer, 210, 10, & startClips[0]);

                } else {
                    renderTexture(startSheet, renderer, 210, 10, & startClips[1]);
                }
            } else {
                renderTexture(startSheet, renderer, 210, 10, & startClips[2]);
            }
            if (xMouse > 410 && xMouse <= 610 && yMouse > 10 && yMouse <= 210) {
                if (SDL_GetMouseState( & xMouse, & yMouse) & SDL_BUTTON(1) == 1) {
                    renderTexture(stopSheet, renderer, 410, 10, & stopClips[0]);
                    //stop
                } else {
                    renderTexture(stopSheet, renderer, 410, 10, & stopClips[1]);
                }
            } else {
                renderTexture(stopSheet, renderer, 410, 10, & stopClips[2]);
            }
            if (xMouse > 610 && xMouse <= 810 && yMouse > 10 && yMouse <= 210) {
                if (SDL_GetMouseState( & xMouse, & yMouse) & SDL_BUTTON(1) == 1) {
                    renderTexture(stepSheet, renderer, 610, 10, & stepClips[0]);
                    //step
                } else {
                    renderTexture(stepSheet, renderer, 610, 10, & stepClips[1]);
                }
            } else {
                renderTexture(stepSheet, renderer, 610, 10, & stepClips[2]);
            }

            renderTexture(tnode0, renderer, 10, 220, NULL);
            renderTexture(tnode1, renderer, 277, 220, NULL);
            renderTexture(tnode2, renderer, 544, 220, NULL);
            renderTexture(tnode3, renderer, 811, 220, NULL);

            renderTexture(tnode4, renderer, 10, 430, NULL);
            renderTexture(tnode5, renderer, 277, 430, NULL);
            renderTexture(tnode6, renderer, 544, 430, NULL);
            renderTexture(tnode7, renderer, 811, 430, NULL);

            renderTexture(tnode8, renderer, 10, 640, NULL);
            renderTexture(tnode9, renderer, 277, 640, NULL);
            renderTexture(tnode10, renderer, 544, 640, NULL);
            renderTexture(tnode11, renderer, 811, 640, NULL);
            
            for(Z = 0; Z <= 11; Z++) {
                if (boxState[Z] == 1) {
                    if (buttonPress.type == SDL_TEXTINPUT || buttonPress.type == SDL_KEYDOWN) {
                        if (buttonPress.type == SDL_KEYDOWN && buttonPress.key.keysym.sym == SDLK_BACKSPACE && strlen(buffer[Z]) > 1) {
                            buffer[Z][strlen(buffer[Z]) - 1] = '\0';
                            renderTexture(textBox(buffer[Z], TTF_OpenFont(fontLocation, 14), color, renderer, 200), renderer, cooridnates[Z][0], cooridnates[Z][1], NULL);
                        } else if (buttonPress.type == SDL_TEXTINPUT) {

                            strcat(buffer[Z], buttonPress.text.text);
                            renderTexture(textBox(buffer[Z], TTF_OpenFont(fontLocation, 14), color, renderer, 200), renderer, cooridnates[Z][0], cooridnates[Z][1], NULL);

                        }
                        if (buttonPress.key.keysym.sym == SDLK_RETURN) {
                            strcat(buffer[Z], "\n ");
                            renderTexture(textBox(buffer[Z], TTF_OpenFont(fontLocation, 14), color, renderer, 200), renderer, cooridnates[Z][0], cooridnates[Z][1], NULL);
                        }
                    }
                }   
            }
        }
        //update frame
    }
    IMG_Quit();
    SDL_Quit();
}

I have already tried to destroy the textures using sdl_destroytexture, but it doesn't seem to help. Can anyone suggest a way to fix this?

the textBox function is as folows:

SDL_Texture *textBox(char tempBuffer[], TTF_Font* font, SDL_Color color, SDL_Renderer *renderer, int maxLength){
    int w,h;
    //TTF_SizeText(font, tempBuffer, &w, &h);
    SDL_Surface * Surface = TTF_RenderText_Blended_Wrapped(font, tempBuffer, color, maxLength);
    
    if(Surface == NULL) {
        logSDLError("TextboxCreate");
    } else {
    SDL_Texture * Texture = SDL_CreateTextureFromSurface(renderer, Surface);
    if(Texture == NULL) {
        logSDLError("TextboxCreate");
    } else {
        return Texture;
        }
    }
}
```
\$\endgroup\$
3
  • 2
    \$\begingroup\$ Are you calling TTF_CloseFont inside textbox? \$\endgroup\$
    – user35344
    Commented Dec 12, 2021 at 15:40
  • 2
    \$\begingroup\$ Stop calling TTF_OpenFont each frame. Load the font file(s) at application startup and destroy it/them at application shutdown. \$\endgroup\$ Commented Dec 12, 2021 at 19:02
  • \$\begingroup\$ @Daniel_1985 Im calling it each frame , because I'm utilizing multiple textboxes that depend on user input - what is a better way to carry this out? \$\endgroup\$ Commented Dec 12, 2021 at 19:43

1 Answer 1

3
\$\begingroup\$

The problem is that you're calling TTF_OpenFont every frame, and you only need to load a font once. TTF_OpenFont is a very intensive function that unsurprisingly uses a lot of memory, because it's only designed for one use. It would be a good idea to read the documentation for more on how it's intended to be used. (Documentation link.)

The best approach here is to save your fonts somewhere as a field and only open them once at the initialization stage of your program, and then pass that font to your rendering functions each time you want to draw a text box with that font on the screen.

Like this:

TTF_Font* Font = TTF_OpenFont("Arial.ttf", 18);

And then when you need to use it to create a text surface object for rendering (SDL_Surface), you can simply pass in the initialized font object.

SDL_Surface* Surface = TTF_RenderText_Blended_Wrapped(Font...);

You'll also need to call TTF_CloseFont in your destructor to free up the memory when the font is no longer needed, depending on how you've implemented all of this.

\$\endgroup\$
10
  • \$\begingroup\$ Should there be a matching call the TTF_CloseFont somewhere? Any guidance on where it would be appropriate to put that in this context? \$\endgroup\$
    – DMGregory
    Commented Dec 12, 2021 at 19:52
  • \$\begingroup\$ @DMGregory That would typically go in the destructor, but I don't know enough about their implementation to write more. \$\endgroup\$
    – Sciborg
    Commented Dec 12, 2021 at 19:55
  • \$\begingroup\$ @Sciborg , so could I call the font at the start of my program using TTF_Font *font = TTF_OpenFont(fontLocation, 14); and then render it using renderTexture(textBox(buffer[0], font, color, renderer, 200), renderer, 20, 240, NULL); or is this still calling openfont each time? \$\endgroup\$ Commented Dec 12, 2021 at 20:05
  • 1
    \$\begingroup\$ @BilalAhmed the example you show is referencing the already-loaded font data stored at the location indicated by the font pointer. It is not calling TTF_OpenFont() a second time. That's why Sciborg recommended this approach. \$\endgroup\$
    – DMGregory
    Commented Dec 13, 2021 at 13:45
  • \$\begingroup\$ @DMGregory Thanks for the advice , I switched to that - but the issue still persists for some reason \$\endgroup\$ Commented Dec 13, 2021 at 19:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .