SFML community forums

Help => System => Topic started by: Beta_Ravener on April 19, 2012, 12:05:40 am

Title: A correct way to multithread OGL context?
Post by: Beta_Ravener on April 19, 2012, 12:05:40 am
I've found plenty of stuff out there about multithreading, OGL and SFML, yet I had only partial success in inmplementing it. I'm currently working in test project to comprehend things and I actually managed to get it done..

Wait I didn't tell what.. I setup my raw OGL view, then in main loop I draw green point through thread, I switch back render a red point, display window.. Before switching to thread I deactivate window context in main thread, activate it in second thread, do my drawing, deactivate, get back to main thread and reactivate. Everything is synchronized with glFinish in second thread and sf::Thread::Wait in main thread so I'm sure nothing gets corrupted.. And it works, I get green point, a red point, but it is sloooooow as.. well I can't really expres that.

Basically it displays a frame then get stucked for about 5 seconds (everything.. even whole event system is frozen so I can't move a window). I noticed this behaviour in the dark age of mine when I recreated whole context on every launch of second thread. Also it had serious memory leaks. And the same behaviour (both leaks and long jumps to/from thread) I get now with only activating or deactivating.

This is my test code:

#include <GL\glew.h>
#include <GL\GLU.h>
#include <GL\GL.h>
#include <SFML\Window.hpp>
#include <SFML\System.hpp>
#include <SFML\Graphics.hpp>

void fun(sf::RenderWindow* win){
        win->setActive(true);

        glBegin(GL_POINTS);
                glVertex2f(50,50);
        glEnd();
        glFinish();

        win->setActive(false);
}

int main(){
        sf::RenderWindow win(sf::VideoMode(200,200), "Context sharing", sf::Style::Close);

        //setup view
        gluOrtho2D(0,200,200,0);
        glPointSize(10);

        sf::Thread tr(fun,&win);       
        sf::Event ev;

        while(win.isOpen()){
                while(win.pollEvent(ev)){
                        switch(ev.type){
                        case sf::Event::Closed:
                                win.close();
                                break;
                        default:
                                break;
                        }
                }

                //clear buffer
                glClearColor(1,1,1,1);
                glClear(GL_COLOR_BUFFER_BIT);

                //render green point from thread
                glColor3f(0,1,0);
                win.setActive(false);
                tr.launch();
                tr.wait();
                win.setActive(true);

                //render red point to check if working
                glColor3f(1,0,0);
                glBegin(GL_POINTS);
                        glVertex2f(75,75);
                glEnd();

                //display it
                win.display();
        }

        return 0;
}

If I get it right I might give that code to wiki if you like as only resource on this "Loading images in a thread (and displaying progress)" is now greyed out and can't be accessed.
Title: Re: A correct way to multithread OGL context?
Post by: Laurent on April 19, 2012, 12:17:07 am
This code is good for learning and testing, but what do you expect from it? It doesn't reflect how things would be done in a real app, so don't focus on its performances. Now write your real code instead.
Title: Re: A correct way to multithread OGL context?
Post by: Beta_Ravener on April 19, 2012, 12:30:40 am
The point is that I would except it to work flawlessly.. no huge lags and memory leaks.. I got over 100 mb of RAM occupied by this small app after running it for 5 minutes. If I can't get it right here, the real code would be catastrophe!

And in fact I'm creating benchmarking program for something of mine that will display progress in RenderWindow and will use another context for running my pure openGL things. So this is very simplified and I'm missing one context here, but still it would answer my questions.

I thought you might see a problem in this code (not meant in way that you are my personal debugger) but if not.. Well and maybe it is concept conflict between my and SFML thinking, so if there really isn't a catch (but those leaks ?!), how would be things done in real code?

Edit: So to be at least usefull for something, I debuged into sfml code. The lag is caused by single function
Code: [Select]
win.setActive(true) which is reactivating context after returning from thread. After debugging it gets to single function that causes trouble:

bool WglContext::makeCurrent()
{
    return m_deviceContext && m_context && wglMakeCurrent(m_deviceContext, m_context);
}

The same function causes memory leaks but this time it's called from second thread while activating. After every activation in thread the consumed RAM gets ~800 kb bigger.
Title: Re: A correct way to multithread OGL context?
Post by: Laurent on April 19, 2012, 08:08:03 am
There's nothing wrong with your code, but you must keep in mind that there are two things that take a big amount of time:
- creating/launching/destroying a thread
- activating an OpenGL context

Fortunately, these things should never happen 60 times per second in a sane program.

As for memory leaks, I'm 99.9999% sure that there are none in this part of SFML, it's more likely your graphics driver that leaks. Have you tried tools like Valgrind?
Title: Re: A correct way to multithread OGL context?
Post by: Ceylo on April 19, 2012, 09:07:31 am
As for memory leaks, I'm 99.9999% sure that there are none in this part of SFML, it's more likely your graphics driver that leaks. Have you tried tools like Valgrind?
The sample took 4 GB of RAM in a few seconds on my laptop (OS X 10.7).

When tracking leaks I noticed it comes from retainPool() in src/SFML/Window/OSX/AutoreleasePoolWrapper.mm.
Title: Re: A correct way to multithread OGL context?
Post by: Laurent on April 19, 2012, 09:14:13 am
I can't say anything about the OS X implementation ;D
Title: Re: A correct way to multithread OGL context?
Post by: Ceylo on April 19, 2012, 09:18:27 am
Yup I know :P
One more interesting screenshot (allocation graph) :
(https://legacy.sfmluploads.org/cache/pics/237_allocations.png)

This is after 11 mn running, at 1 display/sec (to slow down the leak). Considering that the sample was taking 414MB in the end, here is 90% of the leak.
Title: Re: A correct way to multithread OGL context?
Post by: Laurent on April 19, 2012, 09:34:11 am
Would be cool to see the corresponding source code :P

Looking at this report, it seems like SFML is creating a new context every time setActive is called (!!!)
Title: Re: A correct way to multithread OGL context?
Post by: Ceylo on April 19, 2012, 09:47:22 am
It's almost the same source code as the one in the first post of this topic :
#include <SFML/OpenGL.hpp>
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>

void fun(sf::RenderWindow* win){
        win->setActive(true);

        glBegin(GL_POINTS);
                glVertex2f(50,50);
        glEnd();
        glFinish();

        win->setActive(false);
}

int main(){
        sf::RenderWindow win(sf::VideoMode(200,200), "Context sharing", sf::Style::Close);

        //setup view
        gluOrtho2D(0,200,200,0);
        glPointSize(10);

        sf::Thread tr(fun,&win);        
        sf::Event ev;

        while(win.isOpen()){
                while(win.pollEvent(ev)){
                        switch(ev.type){
                        case sf::Event::Closed:
                                win.close();
                                break;
                        default:
                                break;
                        }
                }

                //clear buffer
                glClearColor(1,1,1,1);
                glClear(GL_COLOR_BUFFER_BIT);

                //render green point from thread
                glColor3f(0,1,0);
                win.setActive(false);
                tr.launch();
                tr.wait();
                win.setActive(true);

                //render red point to check if working
                glColor3f(1,0,0);
                glBegin(GL_POINTS);
                        glVertex2f(75,75);
                glEnd();

                //display it
                win.display();
               
                sf::sleep(sf::seconds(1));
        }

        return 0;
}
Title: Re: A correct way to multithread OGL context?
Post by: Laurent on April 19, 2012, 10:03:02 am
Oh, I get i now. Since a new thread is created every time, a new OpenGL context is internally created (when the window's one is deactivated -- SFML never leaves a thread without an active OpenGL context).

Use a single secondary thread with a rendering loop, and everything should be ok.
Title: Re: A correct way to multithread OGL context?
Post by: Ceylo on April 19, 2012, 10:30:04 am
Thus relaunching threads while disabling the main's thread context isn't allowed ? That's a little bit restrictive...
Title: Re: A correct way to multithread OGL context?
Post by: Beta_Ravener on April 19, 2012, 10:40:31 am
Ok I get it now, I didn't expect thread to be such big resource. I'll have to redesign my code a bit but it should be ok.

Btw, as far as I know, thread created variables are dangerous and so creating static context for thread would have many pitfalls. I was thinking about creating Context and RenderWindow in main thread, deactivating Context immediately (that would leave main thread still with RenderWindow context active, right?) and passing it to thread on launch (Indeed, my real code launches it just once for button press, so it isn't 60 FPS for sure).

Only problem I have is your statement:
Quote
SFML never leaves a thread without an active OpenGL context
. It is connected with a question - does every new thread have its own internal OpenGL context created? If so, and SFML is using shared contexts ( = every context same data, what happens to one modifies the others ), it would mean that I wouldn't have to pass any context and render as in my example code and things would work, but green point isn't drawn in that case. Or is it the way that ONCE there was context presented explicitly, upon deactivation implicit context is created? I think later is the case, because if one never uses SFML in his main thread, OpenGL context shouldn't be created for it.

Anyway if I would run that sample not for every frame and would use extra context for second thread, there would be still problem in second thread with deactivating it, because SFML would step in and create internal.. but I don't want that as I'm already leaving thread and nothing really is done afterwards. Can be this behaviour altered?

And thanks for time and effort!
Title: Re: A correct way to multithread OGL context?
Post by: Laurent on April 19, 2012, 12:58:38 pm
Quote
Thus relaunching threads while disabling the main's thread context isn't allowed ? That's a little bit restrictive...
Hmm... I don't get it. Maybe you could show some pseudo-code, to make sure that we're talking about the same thing?

Quote
Ok I get it now, I didn't expect thread to be such big resource. I'll have to redesign my code a bit but it should be ok.
A thread is not a big resource, but you should never have to launch/stop them at a high rate. A good strategy is to allocate a pool of worker threads (size depending on the underlying hardware capabilities), and dispatch work to them on demand.

Quote
Btw, as far as I know, thread created variables are dangerous and so creating static context for thread would have many pitfalls.
What kind of pitfalls?

Quote
I was thinking about creating Context and RenderWindow in main thread, deactivating Context immediately (that would leave main thread still with RenderWindow context active, right?) and passing it to thread on launch (Indeed, my real code launches it just once for button press, so it isn't 60 FPS for sure).
This is a solution but what is your problem first? You should start by describing what you're trying to achieve with threads and contexts ;)

Quote
does every new thread have its own internal OpenGL context created?
No, only when required: for example, when you try to load a texture and there's no context currently active. In this case, SFML will create one and use it. For later calls, the same context will be reused for the same thread, so there's at most one extra context created for each thread.

Quote
it would mean that I wouldn't have to pass any context and render as in my example code and things would work
Yes, that's how SFML is supposed to work: you shouldn't have to do tricky things with context management.

Quote
but green point isn't drawn in that case
Yeah of course, for your green dot to be drawn to a target, it must be the target's context which is active when the dot is rendered.

Quote
Anyway if I would run that sample not for every frame and would use extra context for second thread, there would be still problem in second thread with deactivating it, because SFML would step in and create internal.. but I don't want that as I'm already leaving thread and nothing really is done afterwards. Can be this behaviour altered?
I don't think so, but why is it a problem if your thread is not destroyed and recreated thousands times? Like I said, it should ultimately be done only once.
Title: Re: A correct way to multithread OGL context?
Post by: Ceylo on April 19, 2012, 01:54:09 pm
Quote
Thus relaunching threads while disabling the main's thread context isn't allowed ? That's a little bit restrictive...
Hmm... I don't get it. Maybe you could show some pseudo-code, to make sure that we're talking about the same thing?
I was especially thinking of the example from this topic. I admit threads shouldn't be relaunched every frame but you're saying it should be launched just once, not because launching the thread is too slow, but because otherwise there're leaking OpenGL contexts. And at the same time the given code isn't something very fancy and should work fine and should be correctly handled by SFML (even if it's no very efficient).
Title: Re: A correct way to multithread OGL context?
Post by: Laurent on April 19, 2012, 02:41:10 pm
Quote
And at the same time the given code isn't something very fancy and should work fine and should be correctly handled by SFML (even if it's no very efficient).
This is a known problem indeed, but since the code is not realistic, is it worth providing a (potentially ugly) solution to it?
Title: Re: A correct way to multithread OGL context?
Post by: Ceylo on April 19, 2012, 03:12:31 pm
This is a known problem indeed, but since the code is not realistic, is it worth providing a (potentially ugly) solution to it?
I don't know what to say :D . It's indeed probably not a high priority.
It's just that it reminds me of this issue : https://github.com/SFML/SFML/issues/120
Which is a real and realistic use case as I launch a new thread each time I start playing with sfeMovie, and let the thread die each time I stop playing. But these are different issues.
Title: Re: A correct way to multithread OGL context?
Post by: Beta_Ravener on April 19, 2012, 09:14:39 pm
Quote
What kind of pitfalls?

Imagine a situation where you run same thread from 2 places in program. Voila - as the variable is static, it is common for all the launches and thus it can be corrupted easily. One way to solve this is using mutexes but I try to avoid static variables in threads in first place ;)

Quote
A good strategy is to allocate a pool of worker threads (size depending on the underlying hardware capabilities), and dispatch work to them on demand.

I don't think so, but why is it a problem if your thread is not destroyed and recreated thousands times? Like I said, it should ultimately be done only once.

Ahh I see what you are trying to tell me. I should've created and launch my threads at start of program and just signalize them to work when needed, right? This way everything's created and destroyed on start/end of program. Not a bad approach.  It's still better to have a thread sitting in my memory whole time than to get my memory occupied by OGL contexts. Only problem I see would be that when there are more and more threads added to program, things can get messy (Delphi's make everything global approach).

Background you asked for and I tried to avoid due details:
Take a look at my library www.unishader.g6.cz (http://www.unishader.g6.cz). In short, it makes working with shaders in OGL much easier. I'm now benchmarking CPU vs GPU in parallel computing contest. For that I've built GUI with SFGUI that is quite neat and I would like to keep that GUI somehow responsive (to break computations, etc.) although due benchmarking it will be slowed down ofc.

The threads are used for both CPU and GPU computing. I need to stress my CPU, so I compute in parallel on as many threads as user inputs (this way multicore CPUs can be fully benchmarked). For GPU only one thread with it's own context is created where my library gets initialized and does its job but as I said, this has to be different thread to keep GUI responsive. And as we already noticed in this test case, the lag and memory leaks are so bad that my real code is currently unusable.. I believe that was the same approach as Ceylo here mentioned (substitute playing movie with running computations).

In the past I've already used combination my library + SFML and I have to say they're working nicely together but those threads got me to dead end. I'm now thinking about reading some pages on traditional ways of multithreading so I get more familiar with the whole concept.

PS: you may notice on my web page that I'm talking about small SFML problem connected with creating pure OGL context on Linux, but I didn't try the new release so I won't start a topic for it now ..
Title: Re: A correct way to multithread OGL context?
Post by: Laurent on April 19, 2012, 09:30:51 pm
Quote
Imagine a situation where you run same thread from 2 places in program. Voila - as the variable is static, it is common for all the launches and thus it can be corrupted easily. One way to solve this is using mutexes but I try to avoid static variables in threads in first place
Ah, I thought you were talking about thread-local variables.
So why are we talking about static variables?

Quote
Only problem I see would be that when there are more and more threads added to program, things can get messy
Why would you have so many threads? The strategy that I describe allocates the optimal amount of threads and then only uses them -- if there are more tasks to be performed then they are queued, since the hardware couldn't make them run in parallel efficiently anyway.
(remember, this is only (a very generic) one of the possible approches to multi-threading, I'm not telling you do it this way)
Title: Re: A correct way to multithread OGL context?
Post by: Beta_Ravener on April 19, 2012, 10:42:18 pm
Quote
Ah, I thought you were talking about thread-local variables.
Actually they're thread-local.. Let me clarify:

fun(){
   static int i;
   for(int j = 0; j < 10; j++)
      i++;
}

Now, it is very unlikely that when this thread would be run from 2 places, the i++ would begin to be incremented while it is already incremented in other launch but you can't deny the fact that it can happen. This is simplified and you can imagine how many bad things could've happened if I used whole context instead of simple int. As I said one approach is to lock mutex before incrementing but in the end, it's better to pass by parameter (that static won't be going anywhere even if you'll destroy all the threads, so if that was a big resource you get a lot of memory lost). But that is for another discussion and varies with what you want to actually implement, and actually I realized that also passed variable would have need guards so it's irrelevant for this topic.

Quote
Why would you have so many threads?
Well later in development I could have a lot of functions with different purpose - loading, processing, ... So I would run all these threads on program start and most of them would be just sitting there because loading scene is infrequent, big space battles are infrequent,.. I hope you get my point. Anyway I would be left with many threads, most of them unused most of the time and signalizer (variable responsible for "awaking" the thread) would need to be passed around. I don't know if I would ever do such thing as there is always more ways to do the same, some of them much less complicated.. But I got a feeling from your message that you would make e.g. 5 threads (optimal) and make universal function for all the threads with big switch for loading, processing, .. ?
Title: Re: A correct way to multithread OGL context?
Post by: Laurent on April 20, 2012, 08:10:18 am
Quote
Actually they're thread-local.. Let me clarify
No, I'm really talking about thread-local storage (http://en.wikipedia.org/wiki/Thread-local_storage).
Title: Re: A correct way to multithread OGL context?
Post by: Beta_Ravener on April 20, 2012, 03:58:30 pm
Thanks for link, I didn't know about such variable type before. One learns something new every day.. ;)

I'm already rewriting my app to use thread poll as you proposed, however there will be still a vector of threads for CPU parallel computing created (because of dynamic number of threads that can be input) and those threads will be launched and terminated on each benchmark start. It is not a big deal though as I don't need OGL context in those threads and therefore rerunning them shouldn't be so expensive.