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

Author Topic: Optimizing sf::Text  (Read 2147 times)

0 Members and 1 Guest are viewing this topic.

jmcmorris

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Optimizing sf::Text
« on: December 31, 2016, 08:41:02 pm »
Hello! I have been poking around with sf::Text recently and discovered that rendering sf::Text objects can be a little slow. Rendering 150 sf::Text object is taking about 200IS from what I can see. The UI I'm planning will end up being a bit text intensive so I am looking at optimizing sf::Text some.

From what I can tell sf::Text uses a sf::VertexArray for rendering normal text and an extra sf::VertexArray for outlined text. What I am thinking of attempting is potentially condensing these sf::VertexArrays into a single one since the sf::Text will not be changing outlined thickness much.

Another possible change I was thinking is making some sort of overlord sf::VertexArray that could be shared between many sf::Text objects. If the geometry does not need to be recalculated much (static text) then this would obviously save a great deal on the drawing.

This is all theoretical right now and I haven't made a real plan yet. Before doing that I wanted to see if anyone here had any thoughts or suggestions on how to go about this. Thanks!

binary1248

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1405
  • I am awesome.
    • View Profile
    • The server that really shouldn't be running
Re: Optimizing sf::Text
« Reply #1 on: December 31, 2016, 11:26:37 pm »
Or... if as you say the text is more or less static, just bake it into an sf::RenderTexture and draw that instead. This is one of the reasons SFGUI can get away with ~3000 FPS running the demo (which has a relatively large amount of text) on my machine. It is a bit "cheaty" I guess, because this FPS value comes from the average frame time. In games development this doesn't mean anything however, and the harder constraint is the maximum frame time which is anywhere between 5ms and 14ms in the demo (16ms would be required for the magical 60 FPS, so we are pushing it). Players just hate stuttering as we all know. I assume that those are the frames when the FBO is refreshed as part of the demo GUI being all fancy and updating every now and then.

SFGUI's optimization is still a bit primitive in this regard, because it only caches the whole screen contents. If the GUI was broken down into subsections, the impact wouldn't be that dramatic when it has to be regenerated, but you lose out on that theoretical "single quad for the whole GUI" thing. As always, it's a trade-off that has to be considered. Big studios even spread out their updates over multiple frames based on time budgets that they have per frame. This also helps to reduce sudden stuttering.

Batching geometry was something that was already done early on in SFGUI, and even after reducing the number of draw calls (as you are attempting to do) there are still other bottlenecks. The OpenGL pipeline is fairly long, and you will have to be willing to look into every stage of it if you are seeking performance improvements. This is where the exact measurements of every step of the rendering process comes in, not only on the CPU but on the GPU as well (using the proper tools). Ultimately, you have to think more about reducing the amount of work that has to be done in total rather than transforming that work into another form. Work will always have to be done by someone/something, but the work itself will not always have to be done.

Maybe I should start writing a little utility library of my own that provides some of the more intricate optimizations... But considering that SFML 3 is supposed to come at some point, it's hard to pick what to invest my time into. ;D
SFGUI # SFNUL # GLS # Wyrm <- Why do I waste my time on such a useless project? Because I am awesome (first meaning).

binary1248

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1405
  • I am awesome.
    • View Profile
    • The server that really shouldn't be running
Re: Optimizing sf::Text
« Reply #2 on: January 01, 2017, 01:08:44 pm »
Here is a simple example that should provide a qualitative feeling for what FBO caching can do:
#include <SFML/Graphics.hpp>
#include <sstream>

int main()
{
    sf::RenderWindow window(sf::VideoMode(1800, 900), "Text Test");

    sf::Font font;
    if (!font.loadFromFile("resources/sansation.ttf"))
        return EXIT_FAILURE;

    std::vector<sf::Text> texts;
    texts.reserve(26*26*26);

    int position = 0;

    for (char a = 'a'; a <= 'z'; ++a)
    {
        for (char b = 'a'; b <= 'z'; ++b)
        {
            for (char c = 'a'; c <= 'z'; ++c, position += 20)
            {
                const char str[] = {a, b, c, '\0'};
                texts.push_back(sf::Text(str, font, 10));
                sf::Text& text = texts.back();
                text.setPosition(10 + position % 1780, 10 + position / 1780 * 10);
                text.setFillColor(sf::Color::Red);
            }
        }
    }

    sf::RenderTexture renderTexture;
    renderTexture.create(window.getSize().x, window.getSize().y);

    renderTexture.clear(sf::Color::Transparent);

    for (std::vector<sf::Text>::iterator iter = texts.begin(); iter != texts.end(); ++iter)
        renderTexture.draw(*iter, sf::BlendMode(sf::BlendMode::One, sf::BlendMode::One, sf::BlendMode::Add));

    renderTexture.display();

    sf::Sprite sprite;
    sprite.setTexture(renderTexture.getTexture());

    sf::Clock clock;
    bool useRenderTexture = false;
    std::ostringstream sstr;

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if ((event.type == sf::Event::Closed) ||
                ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape)))
            {
                window.close();
                break;
            }
            else if ((event.type == sf::Event::KeyPressed))
            {
                useRenderTexture = !useRenderTexture;
            }
        }

        clock.restart();

        window.clear(sf::Color::Green);

        if (useRenderTexture)
        {
            window.draw(sprite);
        }
        else
        {
            for (std::vector<sf::Text>::iterator iter = texts.begin(); iter != texts.end(); ++iter)
                window.draw(*iter);
        }

        window.display();

        sf::Int64 microseconds = clock.restart().asMicroseconds();
        sstr << microseconds << " microseconds";
        window.setTitle(sstr.str());
        sstr.str("");
    }

    return EXIT_SUCCESS;
}
SFGUI # SFNUL # GLS # Wyrm <- Why do I waste my time on such a useless project? Because I am awesome (first meaning).