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

Author Topic: SFML 2.0 multi thread rendering demo  (Read 11630 times)

0 Members and 1 Guest are viewing this topic.

T.T.H.

  • Full Member
  • ***
  • Posts: 112
    • View Profile
SFML 2.0 multi thread rendering demo
« on: September 11, 2012, 01:53:26 pm »
I've written a short, fully functional and well documented SFML 2.0 multi thread rendering demo (see attachment) and have a couple of questions now:

Am I doing everything "right", especially regarding thread-safety and performance?

There still is some visible stretching of the viewport while resizing the window - is there any way to prevent that?

The documentation says never to use setVerticalSyncEnabled and setFramerateLimit at the same time but my demo actually has a lower CPU load (about 60% of one core) when using both setVerticalSyncEnabled(true) and setFramerateLimit(60) compared to a higher CPU load (about 100% of one core) when using only setVerticalSyncEnabled(true) - do I trust the documentation or my own tests now?

P.S.: the demo was made with Visual Studio 2010 on Windows 7 and the used font file and image file can simply be exchanged with others.

[attachment deleted by admin]

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: SFML 2.0 multi thread rendering demo
« Reply #1 on: September 11, 2012, 02:11:05 pm »
Here are my comments:

1. Is the sf::Context at the beginning of main() really necessary? What happens if you don't declare it?

2. There's no need to test if the window is open after creating it, it is always open.

3. The renderWindow.close() "just to be sure" is not necessary, the destructor takes care of that.

4. The event list optimization makes your code more complicated for really little gain, I don't think you need it (and you should generally not optimize anything before profiling it and making sure that it's worth it).

5. You probably don't need the beganToRun() test, a thread cannot be launched a second time if it's not finished first.

6. Is the sf::Context declared in the thread necessary? It should not.

To me there's a lot of unnecessary complexity in this code. If some tricks are indeed required, you'd better point to the corresponding issue in the tracker if it exists, or create a new one if not. A "perfect" code is supposed to be what the tutorials and documentation describe, without all these hacks. Anything else that you have to add must be reported as a bug.

Quote
The documentation says never to use setVerticalSyncEnabled and setFramerateLimit at the same time but my demo actually has a lower CPU load (about 60% of one core) when using both setVerticalSyncEnabled(true) and setFramerateLimit(60) compared to a higher CPU load (about 100% of one core) when using only setVerticalSyncEnabled(true) - do I trust the documentation or my own tests now?
You shouldn't get 100% with v-sync on. What is the framerate like when you activate it?
And what CPU load do you get with framerate limit alone?
Laurent Gomila - SFML developer

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10821
    • View Profile
    • development blog
    • Email
Re: SFML 2.0 multi thread rendering demo
« Reply #2 on: September 11, 2012, 02:18:39 pm »
The documentation says never to use setVerticalSyncEnabled and setFramerateLimit at the same time but my demo actually has a lower CPU load (about 60% of one core) when using both setVerticalSyncEnabled(true) and setFramerateLimit(60) compared to a higher CPU load (about 100% of one core) when using only setVerticalSyncEnabled(true) - do I trust the documentation or my own tests now?
I haven't had time to look through the code, so I don't know where you actually call the functions.
Vsync will free the resources on the render thread, where as the FramerateLimit will call sleep() on the thread the window is created in, thus if the render thread doesn't hold the window, calling both can reduce the load. But the bigger problem you can run into is that FramerateLimit tries to keep the FPS count down by calling sleep but since it's actually already limited by Vsync, the FPS will get slower overall and thus they keep on 'fighting' each other. If you want to get the load down on every thread it's better to implement your own framerate limiter (maybe similar to SFML's one) from where you call sleep for every thread. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: SFML 2.0 multi thread rendering demo
« Reply #3 on: September 11, 2012, 02:21:01 pm »
Quote
Vsync will free the resources on the render thread, where as the FramerateLimit will call sleep() on the thread the window is created in
Wrong. Both methods are applied in Window::display().
Laurent Gomila - SFML developer

T.T.H.

  • Full Member
  • ***
  • Posts: 112
    • View Profile
Re: SFML 2.0 multi thread rendering demo
« Reply #4 on: September 12, 2012, 12:09:14 pm »
Regarding VerticalSync(true) and FramerateLimit(60) vs FramerateLimit(0): It seems my observation was wrong because after letting the demo run for some time the CPU load is approximately the same no matter the framerate limit.

Here are screenshots of the taskmanager with the CPU load. And another one.
« Last Edit: September 12, 2012, 12:46:29 pm by T.T.H. »

T.T.H.

  • Full Member
  • ***
  • Posts: 112
    • View Profile
Re: SFML 2.0 multi thread rendering demo
« Reply #5 on: September 12, 2012, 01:16:49 pm »
1. Is the sf::Context at the beginning of main() really necessary? What happens if you don't declare it?

I've taken this code from another application of mine where it is needed to run without errors because there is a special thread rendering to images without having a render window.

In this demo it seems not to be necessary so I removed it.

2. There's no need to test if the window is open after creating it, it is always open.

Ok. I removed it. But what happens if SFML cannot open the window?

3. The renderWindow.close() "just to be sure" is not necessary, the destructor takes care of that.

I want the render window to be closed before the message box eventually appears. I've changed the code comment accordingly to make that clear.

4. The event list optimization makes your code more complicated for really little gain, I don't think you need it (and you should generally not optimize anything before profiling it and making sure that it's worth it).

I admit I haven't profiled it but in my opinion the desire to use the mutex as rarely as possible and lock it as short is possible is worth the additional code complexity.

5. You probably don't need the beganToRun() test, a thread cannot be launched a second time if it's not finished first.

I want to make it impossible for the user to use the interface wrong (and create nasty multi-threading bugs). Mistakes like those:

// create and launch render thread
RenderThread renderThread(renderWindow);
sf::Thread thread(&RenderThread::run, &renderThread);
thread.launch();

// outch :(
sf::Thread thread2(&RenderThread::run, &renderThread);
thread2.launch();

// outch :(
renderThread.run();
 

To me there's a lot of unnecessary complexity in this code. If some tricks are indeed required, you'd better point to the corresponding issue in the tracker if it exists, or create a new one if not. A "perfect" code is supposed to be what the tutorials and documentation describe, without all these hacks. Anything else that you have to add must be reported as a bug.

I've written my demo because I haven't found a tutorial on multi-threaded rendering yet.

In addition everybody has a different definition of "perfect". I think one of the main reasons for multi-threaded rendering is not to block the rendering for of other tasks, e.x. loading resources. Nevertheless it needs to be locked once in a while - which my code does as rarely and as shortly as possible - yes, fully accepting the additional code complexity.

To be honest I hope somebody else considers my demo helpful. Based on my experience blocking threads (using mutexes) too often and too long is one of the typical beginner mistakes in multi-threaded programming.

You shouldn't get 100% with v-sync on. What is the framerate like when you activate it?
And what CPU load do you get with framerate limit alone?
Please see the screenshots in my post above for CPU loads with different settings.

Another strange observation: in my extended demo (code attached) one can toggle the vertical sync and the frame rate limit independently and on my PC I see different CPU loads based on which setting I enable before the other setting. Huh?!


Thanks for the comments to far! But what about the last of my three initial questions: there still is some visible stretching of the viewport while resizing the window - is there any way to prevent that?


[attachment deleted by admin]

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: SFML 2.0 multi thread rendering demo
« Reply #6 on: September 12, 2012, 05:17:42 pm »
Quote
But what happens if SFML cannot open the window?
This cannot happen.

Quote
I want the render window to be closed before the message box eventually appears. I've changed the code comment accordingly to make that clear.
Oh, ok.

Quote
I admit I haven't profiled it but in my opinion the desire to use the mutex as rarely as possible and lock it as short is possible is worth the additional code complexity.
If it makes no difference on performances, all you get is a more complex code ;)

Quote
I want to make it impossible for the user to use the interface wrong (and create nasty multi-threading bugs). Mistakes like those:
Ok.

And yes, your example is helpful :) I'll try to add more doc or examples to the 2.0 tutorials, if I can.

Quote
Another strange observation: in my extended demo (code attached) one can toggle the vertical sync and the frame rate limit independently and on my PC I see different CPU loads based on which setting I enable before the other setting. Huh?!
Huh?! too ;D

Quote
But what about the last of my three initial questions: there still is some visible stretching of the viewport while resizing the window - is there any way to prevent that?
I have no idea.
Laurent Gomila - SFML developer

T.T.H.

  • Full Member
  • ***
  • Posts: 112
    • View Profile
Re: SFML 2.0 multi thread rendering demo
« Reply #7 on: September 12, 2012, 06:47:23 pm »
Regarding my problem with resizing the window I already opened a thread 4,5 years ago. My current solution is to check the size of the window in every render loop and set the view again in case the size is different. In a single-threaded application I can prevent any visible stretching that way but in a multi-thread application the stretching is visible for a short amount of time and results in some nasty "wobbling".

The following code is a minimal example of a single-threaded application to demonstrate it.  I want to have a rectangle which is 100x100 pixel in size and absolutely never ever changes its visual size on the monitor.

#include <SFML/Graphics.hpp>

int main(int argc, char** argv)
{
  sf::RenderWindow renderWindow(sf::VideoMode(300, 300), "resize this window!");

  sf::RectangleShape rectangle;
  rectangle.setPosition(sf::Vector2f(100.0f, 100.0f));
  rectangle.setSize(sf::Vector2f(100.0f, 100.0f));
  rectangle.setFillColor(sf::Color(0, 0, 255));
  rectangle.setOutlineColor(sf::Color(255, 0, 0));
  rectangle.setOutlineThickness(1.0f);

  sf::Event event;
  while (renderWindow.isOpen())
  {
    while (renderWindow.pollEvent(event))
    {
      if (event.type == sf::Event::Closed) renderWindow.close();
    }
    // my current solution: renderWindow.setView(sf::View(sf::FloatRect(0.0f, 0.0f, (float) renderWindow.getSize().x, (float) renderWindow.getSize().y)));
    renderWindow.clear(sf::Color(0, 255, 0));
    renderWindow.draw(rectangle);
    renderWindow.display();
  }
  return 0;
}
 

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: SFML 2.0 multi thread rendering demo
« Reply #8 on: September 12, 2012, 09:00:07 pm »
In a single-threaded code, your application is frozen until the resize is finished. So it's impossible to see any artifact.

In a multi-threaded application, you can react to the resize in real-time (but with a tiny lag), that's why the results are so bad.

A solution would be to handle the resize event (instead of checking the size at every frame), so that you process it only when the resize is finished. Just like in single-threaded applications.

But since that's the reason why you're using threads... I guess it's not a solution :)
Laurent Gomila - SFML developer

T.T.H.

  • Full Member
  • ***
  • Posts: 112
    • View Profile
Re: SFML 2.0 multi thread rendering demo
« Reply #9 on: September 14, 2012, 03:17:29 pm »
In a multi-threaded application, you can react to the resize in real-time (but with a tiny lag), that's why the results are so bad.
Yes, I now think it's that "tiny lag", because the the call of sf::RenderWindow::getSize() within the rendering thread does not return the precisely current size of the window. The "wobbling" seems to be a result of that.

A solution would be to handle the resize event (instead of checking the size at every frame), so that you process it only when the resize is finished. Just like in single-threaded applications.
Doing so looks like this: rotating line still rotates (which means it's still rendering), no "wobbling" but instead those borders.  Hmpf.

Is there any way not to block the main thread while resizing a window?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: SFML 2.0 multi thread rendering demo
« Reply #10 on: September 14, 2012, 03:26:08 pm »
Quote
no "wobbling" but instead those borders.  Hmpf.
I know, it's frustrating. But it's not a critical issue, people are used to visual artifacts when resizing a window.

Quote
Is there any way not to block the main thread while resizing a window?
Not with the way SFML works.
The only way would be to use callbacks, so that SFML could call user code directly from inside the blocking resize loop. But SFML doesn't use callbacks.
« Last Edit: September 14, 2012, 03:28:26 pm by Laurent »
Laurent Gomila - SFML developer