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

Author Topic: Draw calls for text and performance  (Read 698 times)

0 Members and 1 Guest are viewing this topic.

Milichip

  • Newbie
  • *
  • Posts: 5
    • View Profile
Draw calls for text and performance
« on: January 19, 2025, 06:59:05 am »
Hello, I am currently making too many draw calls with sf::RectangleShape instances as well as sf::Text instances.

I know I can turn all the rectangle draw calls into 1 draw call by using vertex arrays, however, is there a way I can "combine" the text draw calls as well? If not, do you have any other suggestions on how to improve performance with text? If yes, how?

For context, I am making something similar to a text editor or fake operating system: lots of windows and lots of text.
A feature of the program is that I can zoom in and out, and zooming out can display a LOT of text on the screen all at once, obliterating performance due to being unable to cull most of the text.

And yes, this is a real problem and is not just premature optimization as I easily go down to 1-8fps.
« Last Edit: January 19, 2025, 07:04:42 am by Milichip »

kojack

  • Sr. Member
  • ****
  • Posts: 357
  • C++/C# game dev teacher.
    • View Profile
Re: Draw calls for text and performance
« Reply #1 on: January 21, 2025, 12:53:59 am »
If things aren't changing much, you could render rectangles and text into a render texture, then reuse that to render to the screen. This would let you replace any number of draw calls with just 1. But of course if things are moving independent of each other then you'd have to keep regenerating the render texture and lose the benefit.

Milichip

  • Newbie
  • *
  • Posts: 5
    • View Profile
Re: Draw calls for text and performance
« Reply #2 on: January 21, 2025, 06:42:05 am »
If things aren't changing much, you could render rectangles and text into a render texture, then reuse that to render to the screen. This would let you replace any number of draw calls with just 1. But of course if things are moving independent of each other then you'd have to keep regenerating the render texture and lose the benefit.

Oh yeah, that's a good idea!
Is there a limit / drawback to using many render textures? Aside from the obvious one where it doesn't reduce draw calls if there are too many.

If using render textures, I would also be more worried about interactions with it and zooming the camera in/out, are there good ways to deal with that besides just clearing and re-rendering render textures completely anytime a zoom in/out happens?
I assume otherwise things could look very pixelated (if zooming in), or be missing completely (if zooming out) if it is not handled properly, but maybe there is little point in creating exceptions for this as a big enough zoom would have to trigger this at some point anyway?
« Last Edit: January 21, 2025, 06:44:19 am by Milichip »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11110
    • View Profile
    • development blog
    • Email
Re: Draw calls for text and performance
« Reply #3 on: January 21, 2025, 09:50:54 am »
It's good to use as few RenderTexture (and textures in general) as possible, but as many as needed.
Switching textures is sort of expensive operation, if you have to do that a lot, it can start to be noticeable.

The advantage of RenderTextures is that you can render multiple unrelated things on it an then with texture coordinates (or texture rect on a sprite) pick the area you want to display.

As for any performance problem, do make use of CPU/GPU profilers to find bottlenecks, otherwise you might be optimizing areas that aren't even the source of your issues.

One additional issue with zooming and sf::Text is that text is just a rasterized texture, so when you zoom it will act the same way as some image. If you want to retain clarity with the text, you need to dynamically adjust the font size and render it un-zoomed with a separat view (using math to find the correct positioning).
Official FAQ: https://www.sfml-dev.org/faq/
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Milichip

  • Newbie
  • *
  • Posts: 5
    • View Profile
Re: Draw calls for text and performance
« Reply #4 on: January 22, 2025, 03:46:03 pm »
It's good to use as few RenderTexture (and textures in general) as possible, but as many as needed.
Switching textures is sort of expensive operation, if you have to do that a lot, it can start to be noticeable.

The advantage of RenderTextures is that you can render multiple unrelated things on it an then with texture coordinates (or texture rect on a sprite) pick the area you want to display.

As for any performance problem, do make use of CPU/GPU profilers to find bottlenecks, otherwise you might be optimizing areas that aren't even the source of your issues.

One additional issue with zooming and sf::Text is that text is just a rasterized texture, so when you zoom it will act the same way as some image. If you want to retain clarity with the text, you need to dynamically adjust the font size and render it un-zoomed with a separat view (using math to find the correct positioning).

Thanks, though I do have a question:
What do you mean "switching textures"? When does a texture switch, what's the criteria?

And yes, I did add some timers to see, the current bottlenecks are updating the positions of rects and text + filling the vertex array with the rect vertices (~40ms in one test) followed by drawing text (~4ms in the same test).

Admittedly there is a ton of stuff I update that doesn't really need updating, since right now moving the camera requires everything to update as there is no transform on individual windows yet.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11110
    • View Profile
    • development blog
    • Email
Re: Draw calls for text and performance
« Reply #5 on: January 22, 2025, 04:03:14 pm »
If you render something with one texture and then render something with a different texture.
The texture will be bound and unbound in the background with OpenGL, which does cost a bit of time.

That's why it's recommended to build an texture atlas, so you can run everything off of one texture.
Official FAQ: https://www.sfml-dev.org/faq/
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Milichip

  • Newbie
  • *
  • Posts: 5
    • View Profile
Re: Draw calls for text and performance
« Reply #6 on: January 22, 2025, 10:59:20 pm »
If you render something with one texture and then render something with a different texture.
The texture will be bound and unbound in the background with OpenGL, which does cost a bit of time.

That's why it's recommended to build an texture atlas, so you can run everything off of one texture.

Sure, but what do you mean by "render" exactly? As soon as you do a .clear(), .draw() or .display() call? Is it when any of those are used, or multiple, or only a specific one?

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11110
    • View Profile
    • development blog
    • Email
Re: Draw calls for text and performance
« Reply #7 on: January 22, 2025, 11:10:23 pm »
For normal sf::Texture you "use" them by calling draw of a sprite, shape or passing it via render state.

For sf::RenderTexture the situation is slightly different, because there's a render time for the contents of the RT and then there's the usage of the texture itself that was rendered.

In the end there isn't an exact limit and sometimes paying the price is the right move.
All I'm trying to say is, that you shouldn't design a system with hundreds of textures and/or render textures.
Use a few as possible (e.g. via texture atlas) and as many as you need.
And use GPU and CPU profilers to find true bottlenecks if you do run into performance issues.
Official FAQ: https://www.sfml-dev.org/faq/
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Milichip

  • Newbie
  • *
  • Posts: 5
    • View Profile
Re: Draw calls for text and performance
« Reply #8 on: January 23, 2025, 01:02:23 am »
For normal sf::Texture you "use" them by calling draw of a sprite, shape or passing it via render state.

For sf::RenderTexture the situation is slightly different, because there's a render time for the contents of the RT and then there's the usage of the texture itself that was rendered.

In the end there isn't an exact limit and sometimes paying the price is the right move.
All I'm trying to say is, that you shouldn't design a system with hundreds of textures and/or render textures.
Use a few as possible (e.g. via texture atlas) and as many as you need.
And use GPU and CPU profilers to find true bottlenecks if you do run into performance issues.

I don't think that really answers the original question of what causes a texture to switch, but I suppose it's not the shortest explanation.
And yeah of course, I wasn't going to try making a rendertexture per object or anything, and did take into consideration the atlas thing.

Thanks for all the answers, both from you and kojack

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11110
    • View Profile
    • development blog
    • Email
Re: Draw calls for text and performance
« Reply #9 on: January 23, 2025, 09:49:18 am »
For normal sf::Texture you "use" them by calling draw of a sprite, shape or passing it via render state.
This is the answer, not sure what else you expect. :D

When the texture is bound, you get a switch, see the source code.
The OpenGL specs apparently allow for up to 16 active textures per stage, so don't think about this too much, if you don't plan to use excessively many textures. ;D
Official FAQ: https://www.sfml-dev.org/faq/
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Hapax

  • Hero Member
  • *****
  • Posts: 3388
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Draw calls for text and performance
« Reply #10 on: January 24, 2025, 01:30:47 am »
You must be drawing a lot of texts as I've recently been doing some tests and failed to gain any substantial speed from batching many texts into a single vertex array.

With that said, however, the test was with identical character sizes.
With different character sizes, a different texture needs to be used.
So, using multiple character sizes means multiple textures and each time you draw a different size, you are switching that texture (the expensive thing!)
One thing you can try is to draw all texts with the same character sizes at the same time; this should reduce texture switching and allows sf::Text to work some magic.
Note that switching fonts is also texture switching so to 'not switch', it must be the same font and the same character size.

You can still scale a text without texture switching but this is often less than desirable.



As an aside, I'm unaware if my tests were unsuccessful just because of my hardware (or some other optimization or maybe some other reason) so I may continue to develop a text batcher ;)
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*