SFML community forums

General => General discussions => Topic started by: Laurent on September 24, 2009, 10:09:31 am

Title: Automatic batching in sfml2 branch
Post by: Laurent on September 24, 2009, 10:09:31 am
Hi

After several weeks of work, I've finally commited the automatic batching code in the sfml2 branch.

Basically, it doesn't break anything in the public API so that most people won't notice anything except potentially improved performances.

The only modifications are on sf::Drawable and sf::RenderWindow:
- the Drawable::Render function now use a render queue instead of direct OpenGL calls
- drawables rendering nested drawables work better: the color is now combined in addition to transformations
- PreserveOpenGLStates doesn't exist anymore, states are now preserved by default (the rendering happens in one call, so doing it doesn't impact performances anymore)
- there's a new Flush function to manually trigger the actual rendering, for example if you need to draw 3D OpenGL on top of what SFML has drawn so far (otherwise rendering is automatically done in Display())
- RenderImage must now call Display() like RenderWindow
- windows are no longer active by default
- multithreading is potentially easier, as all the OpenGL calls happen in a single function (I haven't looked into that yet)
- rendering is now pixel perfect, all artifacts are gone (hopefully :D)

Regarding the performances, I don't really know if it will really be faster on every computer (the code now uses a little more CPU), so I'll need as much feedback as possible :)
This is the first version, so I'll probably have to adjust/fix a few things.

On my computer, the worst case (drawing sprites with different states) gave the same performances as before, and the best case (all sprites use the same image and states) gave a 300% improvement. Shapes gave a 500% improvement. Strings are slightly slower to render, but it's much more consistent than before; and they will soon be rewritten so it doesn't really matters ;)
The most important is that one can now draw intensive stuff like particle systems, tiles, GUI, etc. with SFML, there's no need to use OpenGL to get decent performances.

The next step will now be to rewrite PostFx to be compatible with the new system (and apply the modifications planned in the task list).

Please note that the quality of text rendering is slightly worse than before, it's a work in progress and it will be fixed soon.

CSFML and SFML.Net are also not up-to-date yet.
Title: Automatic batching in sfml2 branch
Post by: Tank on September 24, 2009, 01:39:28 pm
Sounds great, but doesn't work on my side. Minimal example:

Code: [Select]
#include <SFML/Graphics.hpp>

int main() {
sf::RenderWindow  window( sf::VideoMode( 1024, 768, 32 ), "TEST" );
sf::Event         event;
sf::Image         image;
sf::Sprite        sprite;

image.LoadFromFile( "listbox.png" );
sprite.SetImage( image );

while( window.IsOpened() ) {

while( window.GetEvent( event ) ) {
if( event.Type == sf::Event::Closed ) {
window.Close();
}
}

window.Clear();
window.Draw( sprite );
window.Display();
}
}


My window keeps black. Also all other SFML applications don't render anything at all, just the background.

Tested under Debian GNU/Linux (testing branch).
Title: Re: Automatic batching in sfml2 branch
Post by: Mindiell on September 24, 2009, 02:13:50 pm
Quote from: "Laurent"
- windows are no longer active by default

You probably have to SetActive your RenderWindow  :wink:
Title: Automatic batching in sfml2 branch
Post by: Laurent on September 24, 2009, 02:33:44 pm
Quote
You probably have to SetActive your RenderWindow

I was only talking about external OpenGL commands. SFML windows are smart enough to activate theirselves when they render their drawables.

Quote
Sounds great, but doesn't work on my side

Works on Windows for me. I'll try on Linux, although the new system doesn't involve anything which is OS-specific.
Title: Automatic batching in sfml2 branch
Post by: Tank on September 24, 2009, 02:49:53 pm
Maybe it's a difference in the OpenGL implementations or graphics-card dependant (a GTX 260 on my side).

With more sprites (more than 100) I get this:
(http://www.abload.de/thumb/linessmaq.png) (http://www.abload.de/image.php?img=linessmaq.png)
Title: Automatic batching in sfml2 branch
Post by: Laurent on September 24, 2009, 03:29:16 pm
The makefiles have also been updated so that SFML is installed to another directory by default. Make sure it doesn't conflict with previous versions.

Does the Clear() function work (try a different color than black) ?
Title: Automatic batching in sfml2 branch
Post by: Tank on September 24, 2009, 03:31:37 pm
Quote from: "Laurent"
The makefiles have also been updated so that SFML is installed to another directory by default. Make sure it doesn't conflict with previous versions.

SFML files are only present in /usr/local/lib/. But that Makefiles changes were not introduced in the last commit.

Quote
Does the Clear() function works (try a different color than black) ?

Yes, the Clear() function works perfectly (see screenshot above).
Title: Automatic batching in sfml2 branch
Post by: Laurent on September 24, 2009, 03:46:59 pm
It should work now.
Title: Automatic batching in sfml2 branch
Post by: Tank on September 24, 2009, 03:57:26 pm
Thank you, it's working now. :)

Quote
Please note that the quality of text rendering is slightly worse than before, it's a work in progress and it will be fixed soon.

Indeed, the quality decreased a lot. ;)

I'll comment on performance soon when I'm ready to do some tests with a huge tilechat together with 3D models (regarding to old PreserveOpenGLStates()).

Keep it up!
Title: Automatic batching in sfml2 branch
Post by: K-Bal on September 25, 2009, 02:30:34 pm
I can't get pure OpenGL to work with SFML2. Minimal example:

Code: [Select]
#include <SFML/Graphics.hpp>
#include <GL/freeglut.h>

int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    sf::RenderWindow myWindow(sf::VideoMode::GetDesktopMode(), "Ogl with SFML2");
    myWindow.UseVerticalSync(true);

    while(myWindow.IsOpened())
    {
        sf::Event event;
        while(myWindow.GetEvent(event))
        {
            switch(event.Type)
            {
                case sf::Event::Closed:
                {
                    myWindow.Close();
                    break;
                }
                default:
                {
                    break;
                }
            }

            myWindow.Clear();

            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);

            glBegin(GL_POLYGON);
            {
                glColor3f(1.0,1.0,1.0);
                glVertex3f(0.25, 0.25, 0.0);
                glVertex3f(0.75, 0.25, 0.0);
                glVertex3f(0.75, 0.75, 0.0);
                glVertex3f(0.25, 0.75, 0.0);
            }
            glEnd();
            myWindow.Flush();

            myWindow.Display();
        }
    }


    return 0;
}
Title: Automatic batching in sfml2 branch
Post by: Laurent on September 25, 2009, 02:44:36 pm
Look at the OpenGL sample from the SDK to see the changes.

Here is your corrected code:

Code: [Select]
#include <SFML/Graphics.hpp>
#include <GL/freeglut.h>

int main(int argc, char** argv)
{
    glutInit(&argc, argv); // ????
    sf::RenderWindow myWindow(sf::VideoMode::GetDesktopMode(), "Ogl with SFML2");
    myWindow.UseVerticalSync(true);

    while(myWindow.IsOpened())
    {
        sf::Event event;
        while(myWindow.GetEvent(event))
        {
            switch(event.Type)
            {
                case sf::Event::Closed:
                {
                    myWindow.Close();
                    break;
                }
                default:
                {
                    break;
                }
            }

            myWindow.Clear();

            myWindow.SetActive(); // <--- activate your window before OpenGL rendering

            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);

            glBegin(GL_POLYGON);
            {
                glColor3f(1.0,1.0,1.0);
                glVertex3f(0.25, 0.25, 0.0);
                glVertex3f(0.75, 0.25, 0.0);
                glVertex3f(0.75, 0.75, 0.0);
                glVertex3f(0.25, 0.75, 0.0);
            }
            glEnd();
            // myWindow.Flush(); <--- not needed

            myWindow.Display();
        }
    }


    return 0;
Title: Automatic batching in sfml2 branch
Post by: K-Bal on September 25, 2009, 02:52:31 pm
Okay, thank you ;)
Title: Automatic batching in sfml2 branch
Post by: Avency on September 26, 2009, 10:22:28 pm
Now that I've had some time to test the new revision, I found some problems.
For me, performance has decreased a lot.
FPS have dropped from around ~6900 to ~900 (using the same OpenGL-code) or ~700 (the OpenGl code replaced with RenderQueue-code) when rendering a 2D tilemap + a few sprites.
For some odd reason the cpu usage stays at ~10-15% (instead of 25%), with drops to 1-2% which cause massive framedrops.
Older binaries linked aganst sfml 1.5 still work fine, as the previous revision of sfml2 did.
I've tried a few things like manually using the immediate mode renderer or getting rid of saving and restoring the GL-states.
I really don't know whats causing that slowdown...even if the batch renderer was slower, the GL code shouldn't be affected that much from the changes (I've looked through all files that changed in that revision and couldn't find any hint).
Even profiling with Valgrind gave no results.

Something else:
Why is the stack of render states (myStatesStack) of sf::RenderQueue fixed 16 elements?
It will cause the following code to segfault (I know that it is an artificial test case, but still)
Code: [Select]
#include <SFML/Graphics.hpp>
#include <iostream>

class TestDrawable : public sf::Drawable
{
public:
TestDrawable(int id) : mId(id)
{
}

void Render(sf::RenderTarget& target, sf::RenderQueue& queue) const
{
std::cout << mId << std::endl;

if (mId < 15)
{
TestDrawable nestedDrawable(mId + 1);

target.Draw(nestedDrawable);
}
}

private:
int mId;
};

int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600, 32), "SFML Window");

TestDrawable test(0);

window.Draw(test);
window.Display();
}

I hope that those initial problems can be sorted out, since I really like the new batch rendering interface.
Title: Automatic batching in sfml2 branch
Post by: Laurent on September 27, 2009, 11:17:30 am
Quote
FPS have dropped from around ~6900 to ~900 (using the same OpenGL-code) or ~700

This doesn't mean anything... Here you're talking about less than 1 ms, which is the same difference as between 50 and 52 FPS.
Benchmarks done at such high framerates are not relevant at all, as the rendering part only represents a small percentage of the whole frame. The contribution of the event handling, window management, memory transfers, etc. is not negligible.

Quote
Why is the stack of render states (myStatesStack) of sf::RenderQueue fixed 16 elements?

This part of the code is very critical, and even a slight modification has a huge impact on performances. And in this case, using a fixed-size array is faster than using a dynamic one.

Quote
It will cause the following code to segfault (I know that it is an artificial test case, but still)

Absolutely :)
But nobody will ever have 16 levels of nested drawables; and if it happens, I can easily increase the limit (it just takes a little more memory).

Quote
I hope that those initial problems can be sorted out

Honestly, I don't think that having 900 FPS instead of 6900 can be called a "problem". It doesn't mean anything, and it might even give different results when you'll have more data to render.
How many drawables do you render in this program?
Title: Automatic batching in sfml2 branch
Post by: Avency on September 27, 2009, 12:43:42 pm
The FPS are not the problem (I usually cap them and use VSync if possible), but the framedrops definitely are. They occur at random times.
I wonder why it doesn't take full cpu. I know that this could be scheduler related, but it didn't happen witholder revisions.
I noticed that same happens with the example applications (the cpu stays at about 10% on a quad). Although the framedrops are not noticable as they are when scrolling a map, they also occur (this can be noticed best in the opengl sample).

Quote
This part of the code is very critical, and even a slight modification has a huge impact on performances. And in this case, using a fixed-size array is faster than using a dynamic one.

I thought so. There should be a note in the docs or even range checking if the performance penalty isn't too high.
Title: Automatic batching in sfml2 branch
Post by: Laurent on September 27, 2009, 04:01:10 pm
Quote
The FPS are not the problem (I usually cap them and use VSync if possible), but the framedrops definitely are. They occur at random times.

Sorry I don't get it. What do you mean with "framedrops"?

Quote
I wonder why it doesn't take full cpu

Probably because now the GPU usage is optimized, and the CPU can do something else than bothering the graphics driver while the geometry is rendered. So, I'd say that this is rather a good thing ;)

Quote
There should be a note in the docs

The documentation hasn't been written yet (I mean the tutorials) :)

Quote
or even range checking if the performance penalty isn't too high

The performance penalty is high. In this part of the code, every instruction I add has an impact on performances.
Title: Automatic batching in sfml2 branch
Post by: Avency on September 27, 2009, 04:16:04 pm
Lets take the opengl sample: most of the time the cube rotates smoothly, but sometimes it stops and for a short time, as if it was rendered at a very low framerate (like if you're playing a game that your pc cannot handle, you can actually see the single frames).
Title: Automatic batching in sfml2 branch
Post by: Laurent on September 27, 2009, 04:37:39 pm
Ok, I see.

Unfortunately this doesn't happen to me at all. What OS are you using?
Title: Automatic batching in sfml2 branch
Post by: Avency on September 27, 2009, 07:15:19 pm
It only seems to happen on Linux.
I made a Windows build, but I'm unable to reproduce the problem (the hardware is the same).
Title: Automatic batching in sfml2 branch
Post by: Laurent on September 27, 2009, 10:12:09 pm
Ok, I'll try it on Linux and let you know if I get the same problem.
Title: Automatic batching in sfml2 branch
Post by: Tank on September 28, 2009, 07:18:55 pm
Any update on the font texture issue? Would be nice to have sharp strings, again. ;)
Title: Automatic batching in sfml2 branch
Post by: Laurent on September 28, 2009, 07:28:32 pm
Sorry, I didn't have time to work on it.

You should use the previous revision, this one was just released to test the new batching feature.
Title: Automatic batching in sfml2 branch
Post by: Tank on September 28, 2009, 08:06:14 pm
Okay, no problem. Thanks.
Title: Automatic batching in sfml2 branch
Post by: Laurent on October 01, 2009, 09:04:21 am
Quote from: "Tank"
Any update on the font texture issue? Would be nice to have sharp strings, again. ;)

It's fixed, and the quality is even slightly better than before (but you probably won't be able to notice it) ;)
Title: Automatic batching in sfml2 branch
Post by: Tank on October 01, 2009, 05:48:45 pm
Thanks a lot. And you're right, the quality IS better. Is it possible that the edges got a bit sharper? But that's maybe happening because of your rounding code.
Title: Automatic batching in sfml2 branch
Post by: Laurent on October 01, 2009, 06:13:29 pm
Quote
Is it possible that the edges got a bit sharper?

No, unless I implement my own anti-aliasing filter.
I tried all the combinations of Freetype's antialiasing on/off and OpenGL's texture filtering on/off, they were all worse than the current solution.
Title: Automatic batching in sfml2 branch
Post by: Tank on October 01, 2009, 06:34:50 pm
Okay. Then it's nevertheless a nice placebo. ;)
Title: Automatic batching in sfml2 branch
Post by: Laurent on October 03, 2009, 07:52:07 pm
Quote from: "Avency"
Lets take the opengl sample: most of the time the cube rotates smoothly, but sometimes it stops and for a short time, as if it was rendered at a very low framerate (like if you're playing a game that your pc cannot handle, you can actually see the single frames).

I couldn't reproduce this bug on my own Linux. All the samples are running smoothly all the time.
Let's wait and see if someone else gets the same issue :?
Title: Automatic batching in sfml2 branch
Post by: Avency on October 03, 2009, 10:00:36 pm
I see. I'm going to make a fresh Linux install.
I'll let you know if it makes any difference (new drivers etc.).
Title: Automatic batching in sfml2 branch
Post by: Avency on October 04, 2009, 01:50:44 pm
Well, here's the result: it still happens, although it's a lot less noticable than it used to be.
I'm now runnig the latest Ubuntu beta, the graphics card is a nvidia 8800gtx. The driver version is 185.18.36.
Everytime it happens, the cpu usage goes down in the process viewer.
The application now shows up with 64% cpu (I think they changed to relative cpu usage to core usage) usage which drops to ~40% every time it happens (used to show around 15% with drops to 1-2%, this probably explains why its less noticable now). The opengl sample now uses 44% and drops to 30% cpu.
But lets wait if someone else notices the same (and who knows, the problem may disappear before a final 2.0 release :)).
Title: Automatic batching in sfml2 branch
Post by: Laurent on October 04, 2009, 01:54:49 pm
Ok, thanks for your feedback :)
Title: Re: Automatic batching in sfml2 branch
Post by: Dominator on October 17, 2009, 09:08:51 pm
Quote from: "Laurent"

- RenderImage must now call Display() like RenderWindow


How does RenderImage work in .NET? It's lacking a Display() method.
Title: Automatic batching in sfml2 branch
Post by: Laurent on October 17, 2009, 10:59:14 pm
I probably forgot to add it in SFML.Net, thanks for reporting it :)
Title: Automatic batching in sfml2 branch
Post by: Laurent on October 18, 2009, 10:04:05 am
It's fixed.
Title: Automatic batching in sfml2 branch
Post by: Dominator on October 18, 2009, 01:36:07 pm
Thanks! But I still can't seem to get it to work properly.

The RenderImage won't show on the screen after calling Display().
Are there any usage examples?

I've worked around this by loading RenderImage.Image in a Sprite and displaying that instead, but I have to redraw all the contents of the RenderImage every frame or else it turns black.
Is this behavior intended?

Example main loop:
Code: [Select]

...
RenderImage ri = new RenderImage(200, 100);
Sprite spr = new Sprite();
Shape rect = Shape.Rectangle(new Vector2(1, 1), new Vector2(199, 99), Color.White, 1, Color.Black);

while(app.IsOpened())
{
  app.Clear(Color.Blue);

  //has to be done every frame or else it turns black
  ri.Draw(rect);
  ri.Flush();

  //Display() won't work - wrong place to call it?
  //ri.Display();

  //workaround with a sprite
  spr.Image = ri.Image;
  app.Draw(spr);

  app.Display();
 
  app.DispatchEvents();
}
...
Title: Automatic batching in sfml2 branch
Post by: Laurent on October 18, 2009, 01:49:28 pm
Quote
Is this behavior intended?

Yes :)
Render-to-image really means rendering to an image, not on screen. So you then have to use a sprite to display it, like any other image.
Title: Automatic batching in sfml2 branch
Post by: Dominator on October 18, 2009, 01:56:40 pm
Oh, I see. So what's the .Display() method needed for and why turn RenderImages black each frame? It would be nice to be able to keep some pre-rendered images in memory. I tried to copy the RenderImage.Image to a new Image, but then it's just black.

Also the Sprite resulting from an RenderImage is upside down and I have to call Sprite.FlipY() before drawing the Sprite on the screen.
Title: Automatic batching in sfml2 branch
Post by: Laurent on October 18, 2009, 02:08:59 pm
Quote
So what's the .Display() method needed for

Do you know about double-buffering? This techniques uses two buffers: the front buffer (what you see) and the back buffer (where the data gets rendered). When you call Draw(), everything gets rendered into the back-buffer, so that the front-buffer can be displayed on screen without being modified. Then, when you call Display(), these two buffers are switched so that the back-buffer becomes the front-buffer and everything that you rendered during the current frame gets displayed on screen. Then the back-buffer, which contains data from 2 frames ago, can be overwritten with new data and so on...

Well, this is the same for RenderImage: everything gets rendered to a temporary buffer, and calling Display() updates the target image with this temporary buffer (well, this isn't always exactly true, but this is an internal detail).

Quote
why turn RenderImages black each frame?

The contents of a render image are not supposed to change if you don't call Clear() or Draw(). Can you show a complete and minimal example that reproduces this problem?

Quote
Also the Sprite resulting from an RenderImage is upside down and I have to call Sprite.FlipY() before drawing the Sprite on the screen

Same as above ;)
This is not supposed to happen, and I'd like to see an example.
Title: Automatic batching in sfml2 branch
Post by: Dominator on October 18, 2009, 02:38:30 pm
Nevermind, I found the problem.  :wink:

First I used Flush() because of the lack of the Display() method.
Then I added Display() right after Flush(), but it still didn't seem to work correctly.
Now by replacing Flush() with Display() everything works - no more black images and sprites aren't flipped.  :D

Thanks for your effort and keep up the good work!
Title: Automatic batching in sfml2 branch
Post by: Laurent on October 18, 2009, 03:31:38 pm
That's right, Flush() is needed only if you want to insert custom OpenGL rendering between SFML drawables. But... it shouldn't make any difference, that's still weird. I'll have to look into this.

Thanks for your feedback :)
Title: Automatic batching in sfml2 branch
Post by: Svenstaro on November 02, 2010, 08:55:03 pm
What's the status on this? Did you go back or has this actually been implemented? Sorry, I don't feel like reading through the svn log right now.
Title: Automatic batching in sfml2 branch
Post by: Laurent on November 02, 2010, 09:01:12 pm
There's no more automatic batching in SFML 2. The only optimization left is a cache that avoids setting redundant OpenGL states.
Title: Automatic batching in sfml2 branch
Post by: Svenstaro on November 02, 2010, 09:09:23 pm
So basically to "simulate" batching manually, groups of lots of sprites (like particles) should be drawn to a RenderImage and then to RenderWindow to save on draw calls?

Also, any chance for automatic batching to make a return or is excluding them a rather final decision?
Title: Automatic batching in sfml2 branch
Post by: Laurent on November 02, 2010, 09:20:57 pm
No, I'm currently working on a totally different approach. I'll try to provide a lower level API for drawables. Basically, you will be able to define your own geometry, you won't be limited to shapes, sprites or text anymore.
Title: Automatic batching in sfml2 branch
Post by: Svenstaro on November 02, 2010, 10:11:51 pm
Sounds good, however, as it currently stands, does my approach above sound good for particles?

Also, will your new idea be contained in the first release of sfml2 or is that for some time beyond that?
Title: Automatic batching in sfml2 branch
Post by: Laurent on November 02, 2010, 10:15:59 pm
Quote
Sounds good, however, as it currently stands, does my approach above sound good for particles?

Why would it be better to first render to a RenderImage? What can you reuse?

Quote
Also, will your new idea be contained in the first release of sfml2 or is that for some time beyond that?

It will be included in SFML 2.0.
Title: Automatic batching in sfml2 branch
Post by: Svenstaro on November 02, 2010, 10:24:37 pm
Currently I'm rendering 15000 particles draw-by-draw. Of course, this is rather stupid. I'd like to combine these into a single single draw call per frame, if possible. I thought the RenderImage approach would be suited here, is it not?
Title: Automatic batching in sfml2 branch
Post by: Laurent on November 02, 2010, 11:56:20 pm
Quote
I thought the RenderImage approach would be suited here, is it not?

But you still need to render your 15000 particles to the render image, don't you?
Title: Automatic batching in sfml2 branch
Post by: Svenstaro on November 03, 2010, 12:07:34 am
To be honest, I haven't looked at the details of RenderImage at all. How would you efficiently draw lots of particles while trying to be light on draw calls? I SDL I used to blit all my particles onto a surface and then drew the surface to the screen. Any way of replicating this behavior?
Title: Automatic batching in sfml2 branch
Post by: Spodi on November 03, 2010, 01:32:42 am
You would only want to draw to an off-screen surface (RenderImage) if your particles do not change each frame. That'd be a pretty boring particle effect, though.

Drawing to another surface every frame will only reduce the frame rate since the render target has to be changed every so often, plus you are using more resources.

I have particle effects in my engine, and just drawing like any other sprite works just fine. Granted, it is far from ideal since that is a lot of calls, but from what I have seen, draw calls do not have as much overhead in OpenGL as they do in DirectX.

The only places you can really use an off-screen surface for performance gains by drawing less is if you:
1. Have relatively static images (you don't need to update the RenderImage much... exactly how much depends on other variables)
2. The RenderImage you are creating is relatively complex (a bunch of overlaid sprites, like a GUI system)
3. The cost of managing the updates and tracking the state is relatively low

For many games, the number of places you can utilize an off-screen cache is relatively low, and it can add an incredible amount of complexity to your render logic. So my opinion - don't bother trying to cache parts of the screen.

If you want to display more than your system can handle, you're going to want to try to find some other ways to "cheat" it. Draw larger particles instead of smaller ones. Cluster multiple particles together in a single sprite to make it appear like there are really more. Make use of fragment shaders where you can. Etc.

Quote
I SDL I used to blit all my particles onto a surface and then drew the surface to the screen. Any way of replicating this behavior?


That is exactly the same as you are describing for SFML, and its very easy  with SFML 2.0. Its just that it doesn't bring any benefit unless the particles in your particle system are completely static.
Title: Automatic batching in sfml2 branch
Post by: Svenstaro on November 03, 2010, 08:25:09 pm
I see, thanks for the detailed explanation. I will try to group a bunch of particles depending on the effect.