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

Author Topic: A correct way to multithread OGL context?  (Read 16099 times)

0 Members and 1 Guest are viewing this topic.

Beta_Ravener

  • Jr. Member
  • **
  • Posts: 51
    • ICQ Messenger - 271426715
    • View Profile
A correct way to multithread OGL context?
« 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.
« Last Edit: April 19, 2012, 12:17:36 am by Laurent »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: A correct way to multithread OGL context?
« Reply #1 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.
Laurent Gomila - SFML developer

Beta_Ravener

  • Jr. Member
  • **
  • Posts: 51
    • ICQ Messenger - 271426715
    • View Profile
Re: A correct way to multithread OGL context?
« Reply #2 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.
« Last Edit: April 19, 2012, 12:58:05 am by Beta_Ravener »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: A correct way to multithread OGL context?
« Reply #3 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?
Laurent Gomila - SFML developer

Ceylo

  • Hero Member
  • *****
  • Posts: 2325
    • View Profile
    • http://sfemovie.yalir.org/
    • Email
Re: A correct way to multithread OGL context?
« Reply #4 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.
Want to play movies in your SFML application? Check out sfeMovie!

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: A correct way to multithread OGL context?
« Reply #5 on: April 19, 2012, 09:14:13 am »
I can't say anything about the OS X implementation ;D
Laurent Gomila - SFML developer

Ceylo

  • Hero Member
  • *****
  • Posts: 2325
    • View Profile
    • http://sfemovie.yalir.org/
    • Email
Re: A correct way to multithread OGL context?
« Reply #6 on: April 19, 2012, 09:18:27 am »
Yup I know :P
One more interesting screenshot (allocation graph) :


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.
Want to play movies in your SFML application? Check out sfeMovie!

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: A correct way to multithread OGL context?
« Reply #7 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 (!!!)
Laurent Gomila - SFML developer

Ceylo

  • Hero Member
  • *****
  • Posts: 2325
    • View Profile
    • http://sfemovie.yalir.org/
    • Email
Re: A correct way to multithread OGL context?
« Reply #8 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;
}
« Last Edit: April 19, 2012, 09:58:06 am by Laurent »
Want to play movies in your SFML application? Check out sfeMovie!

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: A correct way to multithread OGL context?
« Reply #9 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.
Laurent Gomila - SFML developer

Ceylo

  • Hero Member
  • *****
  • Posts: 2325
    • View Profile
    • http://sfemovie.yalir.org/
    • Email
Re: A correct way to multithread OGL context?
« Reply #10 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...
« Last Edit: April 19, 2012, 10:46:33 am by Ceylo »
Want to play movies in your SFML application? Check out sfeMovie!

Beta_Ravener

  • Jr. Member
  • **
  • Posts: 51
    • ICQ Messenger - 271426715
    • View Profile
Re: A correct way to multithread OGL context?
« Reply #11 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!

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: A correct way to multithread OGL context?
« Reply #12 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.
Laurent Gomila - SFML developer

Ceylo

  • Hero Member
  • *****
  • Posts: 2325
    • View Profile
    • http://sfemovie.yalir.org/
    • Email
Re: A correct way to multithread OGL context?
« Reply #13 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).
Want to play movies in your SFML application? Check out sfeMovie!

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: A correct way to multithread OGL context?
« Reply #14 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?
Laurent Gomila - SFML developer