SFML community forums
General => General discussions => Topic started 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.
-
Sounds great, but doesn't work on my side. Minimal example:
#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).
-
- windows are no longer active by default
You probably have to SetActive your RenderWindow :wink:
-
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.
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.
-
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)
-
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) ?
-
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.
Does the Clear() function works (try a different color than black) ?
Yes, the Clear() function works perfectly (see screenshot above).
-
It should work now.
-
Thank you, it's working now. :)
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!
-
I can't get pure OpenGL to work with SFML2. Minimal example:
#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;
}
-
Look at the OpenGL sample from the SDK to see the changes.
Here is your corrected code:
#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;
-
Okay, thank you ;)
-
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)
#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.
-
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.
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.
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).
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?
-
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).
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.
-
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"?
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 ;)
There should be a note in the docs
The documentation hasn't been written yet (I mean the tutorials) :)
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.
-
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).
-
Ok, I see.
Unfortunately this doesn't happen to me at all. What OS are you using?
-
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).
-
Ok, I'll try it on Linux and let you know if I get the same problem.
-
Any update on the font texture issue? Would be nice to have sharp strings, again. ;)
-
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.
-
Okay, no problem. Thanks.
-
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) ;)
-
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.
-
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.
-
Okay. Then it's nevertheless a nice placebo. ;)
-
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 :?
-
I see. I'm going to make a fresh Linux install.
I'll let you know if it makes any difference (new drivers etc.).
-
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 :)).
-
Ok, thanks for your feedback :)
-
- RenderImage must now call Display() like RenderWindow
How does RenderImage work in .NET? It's lacking a Display() method.
-
I probably forgot to add it in SFML.Net, thanks for reporting it :)
-
It's fixed.
-
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:
...
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();
}
...
-
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.
-
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.
-
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).
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?
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.
-
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!
-
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 :)
-
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.
-
There's no more automatic batching in SFML 2. The only optimization left is a cache that avoids setting redundant OpenGL states.
-
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?
-
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.
-
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?
-
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?
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.
-
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?
-
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?
-
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?
-
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.
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.
-
I see, thanks for the detailed explanation. I will try to group a bunch of particles depending on the effect.