Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: My SFML project uses large amounts of RAM and the font displays half of text  (Read 12401 times)

0 Members and 3 Guests are viewing this topic.

Gan

  • Jr. Member
  • **
  • Posts: 98
    • View Profile
Two problems, I'll start with the text not displaying correctly because that is the most obvious issue:



Only certain letters show. This was not always the case, I tweaked some graphics code in another part of the project and this started happening. In fact, I was trying to solve the second problem when the first problem started happening.

Secondly, the program uses massive amounts of ram. To replicate, fly the ship in one direction. The ram usage climbs upward and upward. After 7 hours it uses 700mb of ram.

I managed to comment code until I found the issue. It is located in source/Game/Misc/SpaceBackground.d at line 254.
Code: [Select]
target.draw(star);I'm drawing a circle shape to a RenderTexture. Comment that line of code and memory usage stabilizes at about 60mb consistently. Keep that line of code in and it climbs above 200mb+ over time.


I've attached the source and the executables.

Instructions to run the executables:
(1) Run the server executable.
(2) cd to the Game Executables directory.
(3) Run the Game executable.
(4) Type in letters for the username. Click Enter. Type in letters for the password. Click Enter. Type in letters for a name. Click Enter.
(5) Fly the space ship using the arrow keys. Watch the ram usage. Also notice the missing letters in the text.

Instructions for running from the source:
Use DUB.
Note: The source is using DSFML with the D Language.


I would appreciate ideas for why SFML is failing to render text and uses lots of memory for drawing circle shapes.


Source: http://cl.ly/2H3D3T3A0J2G
Executables: http://cl.ly/2L2v17471u3b

Jebbs

  • Sr. Member
  • ****
  • Posts: 358
  • DSFML Developer
    • View Profile
    • Email
I'll look into this more over the weekend, but the money issue could be caused by the GC. It doesn't return memory back to the OS when it does collections and can be a bit of a hog. Try importing core.memory and calling GC.minimize() to see if that does anything.
DSFML - SFML for the D Programming Language.

Gan

  • Jr. Member
  • **
  • Posts: 98
    • View Profile
I'll try that.


Regarding the text not fully displaying, on my desktop Mac text displays fine but on my laptop mac, it displays half of the text.
Desktop mac:

Gan

  • Jr. Member
  • **
  • Posts: 98
    • View Profile
Tried your ram idea, ram stabilized at 163mb.
I added this code on every loop:
Code: [Select]
GC.collect();
GC.minimize();

Though when I comment the draw star line:
Code: [Select]
//target.draw(star);It stabilizes at 50mb.

I'm trying to figure out why drawing many stars uses 110mb of ram.

Jebbs

  • Sr. Member
  • ****
  • Posts: 358
  • DSFML Developer
    • View Profile
    • Email
That's good to hear that you've had that much success already. I wonder if you could even omit the collection call.

I'll still be looking at your code this weekend so I'll let you know if anything jumps out at me.
DSFML - SFML for the D Programming Language.

Gan

  • Jr. Member
  • **
  • Posts: 98
    • View Profile
I optimized it so it only uses one CircleShape now but set it to redraw the maximum amount of stars per frame. Gonna see where it stops gaining memory.
It starts at 50mb still. Using 100% CPU cause I'm forcing it to redraw all tiles every frame so thousands of stars.

It is up to 209.3mb. Increasing incredibly slowly. Like takes a minute to go up another megabyte.


So drawing 1 star thousands of time uses 150+mb slowly over time. Now up to 213.5mb.

On a side note, any idea why text rendering is so finicky? (I'm going to go look at the CircleShape code)

Gan

  • Jr. Member
  • **
  • Posts: 98
    • View Profile
I fixed the text rendering issue.

Turns out you don't want to create any objects before the window is created. Pretty easy fix. Strange though.

As for the lots of ram usage. I made a new example to test that.

I made two tests. Both tests drawing 4000 stars each frame.

Differences:
  • Test without render textures stabilizes at 35.1mb.
  • Test with 30 render textures doesn't stabilize, goes 137mb and continues to slowly rise.
  • Test without render textures uses anti-aliasing.
  • Test with 30 render textures does not use anti-aliasing despite having smooth set to true.

It seems render textures are flawed in that they use lots of ram and don't anti-alias.
Though they are essential to lower the CPU required to draw a very large, moving, generated parallax background.


Test program:
module main;

import dsfml.graphics;
import std.stdio;
import core.memory;
import std.file;
import std.conv;
import std.string;
import std.random;

void main(string[] args)
{
        //GC.disable();
        GC.minimize();

        ContextSettings settings;
        settings.antialiasingLevel = 8
                ;
        settings.majorVersion = 3;
        settings.minorVersion = 2;

        auto window = new RenderWindow(VideoMode(1152, 720),"SpaceCraft", RenderWindow.Style.DefaultStyle, settings);
        window.setFramerateLimit(60);
       
        Font GameFont = new Font();
        string path = thisExePath();
        int index = to!int(path.lastIndexOf("/"));
        if (!GameFont.loadFromFile(path[0 .. index]~"/vertiup2.ttf")) {
                writeln("Font failed to load");
        }
       
        Text text = new Text("Hello DSFML", GameFont, 50);
       
        text.setStyle(Text.Style.Bold);
        text.setColor(Color(255, 0, 0));
        text.origin = Vector2f(0, 0);
        text.position = Vector2f(100, 100);
       
        CircleShape shape = new CircleShape(30);

        RenderTexture[] textures = new RenderTexture[](0);
        Sprite[] textureSprites = new Sprite[](0);

        for (int i = 0; i < 30; i++) {
                RenderTexture canvas = new RenderTexture();
                canvas.create(512, 512);
                canvas.clear(Color(0, 0, 0, 0));
                canvas.smooth = true;
                textures ~= canvas;
                Sprite tileSprite = new Sprite(canvas.getTexture());
                tileSprite.origin = Vector2f(512/2,512/2);
                textureSprites ~= tileSprite;
        }

    while (window.isOpen())
    {
        Event event;

        while(window.pollEvent(event))
        {
            if(event.type == event.EventType.Closed)
            {
                window.close();
            }
        }

                window.clear();

                drawStarsWithRenderTextures(textureSprites, textures, shape, window);
                //drawStars(shape, window, 4000);

                window.draw(text);

                window.display();

                //GC.collect();
                //GC.minimize();
    }

}
/***************   Hits 137.5mb+, not stabilizing after 5 minutes  **************/
void drawStarsWithRenderTextures(Sprite[] tileSprites, RenderTexture[] textures, CircleShape shape, RenderTarget window) {
        int starCount = to!int(4000/textures.length);
        for (int i = 0; i < textures.length; i++) {
                RenderTexture canvas = textures[i];
                canvas.clear(Color(0, 0, 0, 0));
                drawStars(shape, canvas, starCount);
                canvas.display();
                Sprite tileSprite = tileSprites[i];
                Random gen;
                gen.seed(unpredictableSeed);
                int x = uniform(0, window.getSize().x, gen);
                int y = uniform(0, window.getSize().y, gen);
                tileSprite.position = Vector2f(x, y);
                window.draw(tileSprite);
        }
}
/***************   Stabilizes at 35.1mb   **************/
void drawStars(CircleShape shape, RenderTarget window, int starCount) {
        //Draw stars
        for (int i = 0; i < starCount; i++) {
                Random gen;
                gen.seed(unpredictableSeed);
                int r = uniform(0, 255, gen);
                int g = uniform(0, 255, gen);
                int b = uniform(0, 255, gen);
                int x = uniform(0, window.getSize().x, gen);
                int y = uniform(0, window.getSize().y, gen);
                shape.fillColor = Color(to!ubyte(r), to!ubyte(b), to!ubyte(g));
                shape.position = Vector2f(x, y);
                shape.origin = Vector2f(0.5, 0.5);
                shape.scale = Vector2f(15 / 30.0 , 15 / 30.0);
                window.draw(shape);
        }
}

Jebbs

  • Sr. Member
  • ****
  • Posts: 358
  • DSFML Developer
    • View Profile
    • Email
Holy crap. Are you creating multiple RenderTextures and then drawing to all of them each frame?

That would be your memory issue. RenderTextures are not light objects and should be used sparingly. I'm surprised it isn't causing performance issues.

As for the text thing, I bet that has something to do with needing an openGL context before the font texture is created. It makes sense.
DSFML - SFML for the D Programming Language.

Gan

  • Jr. Member
  • **
  • Posts: 98
    • View Profile
Holy crap. Are you creating multiple RenderTextures and then drawing to all of them each frame?

That would be your memory issue. RenderTextures are not light objects and should be used sparingly. I'm surprised it isn't causing performance issues.
Yes. I have a grid of render textures across the screen. I redraw the render textures when they get off screen. It allows me to make a very heavily starred procedurally generated background at 6% CPU perfromance. Producing the same without render textures, just drawing circle shapes uses 100%+ CPU usage.

Would you have a better solution than RenderTextures?

Edit: Despite render textures using inherently more memory, why would the memory continue to rise? Seems like an issue in the RenderTexture itself.
« Last Edit: February 21, 2015, 06:10:01 am by Gan »

Mario

  • SFML Team
  • Hero Member
  • *****
  • Posts: 879
    • View Profile
Using render textures that way to "cache" output is perfectly fine as long as you don't redraw them every frame.

However, I'd just use one big render texture to cover the whole screen. Then just draw it on a quad that is essentially four times the window size (so the texture repeats itself). You can then just move that one quad around (repositioning it), to have a continually filled screen without any redrawing.

Of course this won't work if you'd like to draw some specific kind of map.

Gan

  • Jr. Member
  • **
  • Posts: 98
    • View Profile
That's a pretty good idea for a more performant space background.


In my actual game, I don't redraw the rendertextures every frame. Just as soon as they get off screen. Though the same issue happens as if drawing every frame- just slower.

2 issues:
- Anti aliasing doesn't happen on RenderTextures
- Memory usage creeps upward

Gan

  • Jr. Member
  • **
  • Posts: 98
    • View Profile
Are these issues only inherit to DSFML or flaws in SFML itself?

Gan

  • Jr. Member
  • **
  • Posts: 98
    • View Profile
(bump)

Jebbs

  • Sr. Member
  • ****
  • Posts: 358
  • DSFML Developer
    • View Profile
    • Email
Hey, sorry. I'm actually in the process of moving so things are nuts for me right now.

Anyways, the anti aliasing thing could be in the binding. A good way to check it though would be to create a minimal example that shows the issue and then port it back to C++ to see if you experience the same issue. Having the minimal example for that would also be good for me for checking.

As for the memory issue, that is most likely something that is going on in the binding. I have been focusing on completeness over optimization, but with the next release I should be in pretty good shape to work out any kinks.

This should be the last week that things are wonky for me, so I will get to it as soon as I can.
DSFML - SFML for the D Programming Language.