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

Author Topic: Best way to render, performance-wise  (Read 11933 times)

0 Members and 1 Guest are viewing this topic.

Vot1_Bear

  • Newbie
  • *
  • Posts: 31
  • Noob in-training
    • View Profile
    • Game development blog
Best way to render, performance-wise
« on: September 25, 2013, 10:28:01 am »
Hello again SFML community!

I am currently developing a game project, and it has come to my attention that the performance is quite lacking, at least for me and some of my testers.

One of my biggest concerns is the rendering method I use. Performance-wise, what is the most efficient way of rendering sprites to the window?

Currently I am using a RenderWindow and its draw() function to draw sprites on it, then use display() at the end of every loop. Some post on the internet suggested using a RenderTexture first, however, and only extracting it to a Texture and drawing it to the Window in the end of the loop.

1. So which one of these are better, and if neither of them are, is there a faster way to render sprites?

2. Will maintaining a "background" RenderTexture (a texture that won't change and can be reused over and over again) give significant improvement than rendering ~100 sprites to the RenderWindow per loop?

3. Which process is actually the most expensive one anyway? The draw(), or the display() function?

4. On an unrelated (window-related) note, what does setVerticalSyncEnabled() do? Can it be used together with setFrameRateLimit()?

For reference, the target FPS is about 30. There are occasionally some lag during gameplay on my laptop, but considering how it can run skyrim pretty fine (at low specs though), I kinda doubt the code logic is the source of the problem. Or can you actually 'request' more performance priority from the machine?

Thanks!
« Last Edit: September 25, 2013, 10:29:33 am by Vot1_Bear »

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: Best way to render, performance-wise
« Reply #1 on: September 25, 2013, 10:43:07 am »
I'm no expert on this, but the number one thing I ALWAYS hear the experts saying on this subject is that you want to minimize your draw calls.  As far as I know there are two main ways of doing this:

1) Draw several things with a single draw() call by using a VertexArray instead of a ton of sprites (ideal for things like tiled levels)

2) Simply not drawing things that won't be visible on screen (this is called "culling"), either because they're outside the window entirely or they're covered up by other stuff.

My impression is that things like RenderTexture don't matter all that much, except in the sense that drawing something to a RenderTexture and then drawing that texture to a RenderWindow is two separate draw() calls where you might have gotten away with just one.

Of course, if you're doing something silly like loading new textures or creating new rendertextures or capturing the screen every single frame (yes, I've done this myself >_>) then that's probably a big problem you can easily fix.

Some specific responses:

1) If all you're doing is rendering everything to a RenderTexture and then rendering the result to the window, all you've done is added an extra draw() call for no benefit.  Of course, if you ever want to do post-processing effects, you'll have to do this anyway, but don't bother with it if you don't plan to do any of that.

2) Not really.  Again, moving the texture to the window afterward is just an extra draw() call that doesn't gain you anything.  I've never heard of rendertexture being somehow faster or slower than renderwindow.

3) clear() and display() should always be called exactly once per frame no matter what, so I'm not sure why this question matters.  However, my gut feeling is that display() merely swaps buffers (look up Double Buffering) so draw() would be doing all the real work.

4) The official tutorials explicitly state you should not use both.  In my personal experiments setting vsync doesn't do much but framerate limiting works flawlessly.  What Vsync does is tell the graphics card not to bother rendering frames faster than the monitor can display them, which typically means 60fps.  There are a LOT of details about this process I'm not going to try explaining here (eg, why Triple Buffering becomes relevant).  Check out http://www.tweakguides.com/Graphics_9.html for a far better explanation than I can provide.

This is just what I've gotten from other sources.  Since I'm not even close to an expert, I won't try to analyze your code myself.  I'd probably get it wrong.
« Last Edit: September 25, 2013, 10:52:44 am by Ixrec »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Best way to render, performance-wise
« Reply #2 on: September 25, 2013, 10:48:24 am »
Ixrec gave you the best advice. If you want more precise answers, you'll have to describe what kind of stuff you're drawing (what kind of entites, how many, are they static or dynamic, ...) ; there's no universal "best way of drawing", it is potentially different for every program.

sf::RenderTexture can be an alternative to sf::VertexArray in some cases, as it also allows to pre-render many entities at init time and draw them later in a single call. But vertex arrays are preferred if you don't have a good reason to avoid them.

For 4., please read the documentation and tutorials. It is very well explained.
Laurent Gomila - SFML developer

Vot1_Bear

  • Newbie
  • *
  • Posts: 31
  • Noob in-training
    • View Profile
    • Game development blog
Re: Best way to render, performance-wise
« Reply #3 on: September 25, 2013, 11:58:20 am »
Thank you for the quick replies!


1) Draw several things with a single draw() call by using a VertexArray instead of a ton of sprites (ideal for things like tiled levels)   
This is probably the most promising thing I can do to improve the performance, so thanks for the info
The tutorial is quite confusing however. Based on what I read before, a VertexArray is used to create geometrical shapes, not to list a bunch of texture-sprites and draw them on one go (which, I believe, is what you're talking about above).

Then again, maybe I should re-read the tutorial.

2) Not really.  Again, moving the texture to the window afterward is just an extra draw() call that doesn't gain you anything.  I've never heard of rendertexture being somehow faster or slower than renderwindow.
I was actually talking about a rendertexture that don't have to be changed anymore over the course of the game. Say, there are 10*10 background tiles, but these tiles are going to be reused over and over again per loop. I think rendering all 100 to a rendertexture would be faster, since later on you'll only have to render the single rendertexture per loop. (Basically similar as how you said VertexArrays is used, and is probably what Laurent is said regarding rendertexture)

Again, @Ixrec and Laurent, thanks for the fast reply! 

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Best way to render, performance-wise
« Reply #4 on: September 25, 2013, 12:23:04 pm »
Quote
The tutorial is quite confusing however. Based on what I read before, a VertexArray is used to create geometrical shapes, not to list a bunch of texture-sprites and draw them on one go (which, I believe, is what you're talking about above).
A vertex arrays is indeed a list of geometrical primitives, with an optional texture applied on them. It is not a sprite batch, it's more low-level. So if you want to translate a bunch of static sprites into a single vertex array, you have to compute vertex positions and texture coordinates yourself, as shown in the tutorial (look at the tilemap example).

But once in the end the result will be the same, except with much better performances ;)
Laurent Gomila - SFML developer

Vot1_Bear

  • Newbie
  • *
  • Posts: 31
  • Noob in-training
    • View Profile
    • Game development blog
Re: Best way to render, performance-wise
« Reply #5 on: October 02, 2013, 10:38:26 am »
One more question regarding vertexarrays... Is it not possible for us to modify the size as we add more vertices to it?

This is the code I use to add a rectangular sprite (Basically I use this function to replace any draw(Sprite) function in the previous version). Every time I need to add the 'sprite', I increase the size of the vertexarray by 4 and inputted the details (position, texture coordinates). This, however, results in a heap corruption... So, are we supposed to immediately declare how many vertices we need (just like in the tutorial example)? Or is there a way to modify the vertices amount as you draw?

(Vertices is the name of the VertexArray)
(xstr, ystr, xlen and ylen are vectors of float denoting the sprite [id]'s texture rect.)
(Please ignore the whitespace errors)
        ////Adds a quad at position xpos, ypos with texturerect id
void VertexManager::AddQuad(float xpos, float ypos, int id){
        sf::Int8 now = Vertices.getVertexCount();
       
        Vertices.resize( now + 4 );
        sf::Vertex* Quad = &Vertices[now];
       
        Quad[0].position  = sf::Vector2f ( xpos                 , ypos  );
        Quad[1].position  = sf::Vector2f ( xpos + xlen[id] , ypos       );
        Quad[2].position  = sf::Vector2f ( xpos + xlen[id] , ypos       + ylen[id]      );
        Quad[3].position  = sf::Vector2f ( xpos                 , ypos  + ylen[id]      );
       
        Quad[0].texCoords = sf::Vector2f ( xstr[id]               ,ystr[id]                  );
        Quad[1].texCoords = sf::Vector2f ( xstr[id]+xlen[id] ,ystr[id]                  );
        Quad[2].texCoords = sf::Vector2f ( xstr[id]+xlen[id] ,ystr[id] + ylen[id]       );
        Quad[3].texCoords = sf::Vector2f ( xstr[id]               ,ystr[id] + ylen[id]  );
}

Thanks in advance!
« Last Edit: October 02, 2013, 10:46:00 am by Vot1_Bear »

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: Best way to render, performance-wise
« Reply #6 on: October 02, 2013, 10:48:16 am »
Just check the documentation:

http://sfml-dev.org/documentation/2.1/classsf_1_1VertexArray.php

The page for VertexArray lists all the functions associated with that class.  The existence of resize() and append() should make it clear that you can indeed add more elements after construction.

It also has a brief description of the class which explicitly states it's a "dynamic array", which means the implementation can and will change size on its own if one of its public methods ever requires it to (just like std::vector does).
« Last Edit: October 02, 2013, 10:51:56 am by Ixrec »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Best way to render, performance-wise
« Reply #7 on: October 02, 2013, 11:16:49 am »
I'd rather use the append function, but your code looks ok. You should use your debugger to find out more details about the error.
Laurent Gomila - SFML developer

Vot1_Bear

  • Newbie
  • *
  • Posts: 31
  • Noob in-training
    • View Profile
    • Game development blog
Re: Best way to render, performance-wise
« Reply #8 on: October 03, 2013, 03:50:31 pm »
My bad, I was trying out the sf::Int(number) variables which I'm going to use for networking later... and forgot that Int8 is equal to a 8-bit signed integer.

The error was merely overflow, and is fixed now. VertexArray worked perfectly, so thanks again for the help!