Shared context between two threads issue - LWJGL 2

Started by Thiird, April 25, 2018, 21:13:03

Previous topic - Next topic

Thiird

I needed to be able to render from a thread and load images from another thread. so I googled a bit and found this: https://www.khronos.org/opengl/wiki/OpenGL_and_multithreading
So I implemented the first patch of code that u can see in that page, that is sharing a single context between two or more threads by letting one release it and acquire it in another thread, and it works perfectly.
The problem here is that I need to move the GL context from a thread to another several times per seconds, and as you may imagine this causes significant pauses in the rendering, given also the fact the context stays away from the rendering thread for a little bit less that a second (a long pause in the rendering). So I decided that I had to create two contexts so that the rendering never stops and I can load new stuff in the second thread, which is the second patch of code in the linked page,but I encountered several issues that I'm not familiar with:
-you use the wglShareList(long context1, long context2) method to share the context, but as far as I know there's no such object as a Context Obj in LWJGL, I just use Display.create() and thats it, so what argument should I use in there? Is it referring to Drawable object and/or SharedDrawable? (Which I have yet to understand what they are tbh);
-from where do I call the wglShareList method? I cant find it in GL11... GL41...neither of those;

I know it's and "old" topic but I cant find a solution.

Thanks

CoDi

My personal opinion: it's not worth the trouble. Too much possibly going wrong with different GPU driver & OS combinations.

I'm on LWJGL3/GLFW which makes context creation a little easier. I'm using shared contexts for rendering to multiple windows in the same thread with our editing tools, and it's been troublesome enough to get this right - which means to always remember what types of GL resources are shared, and which are not.

We load assets in a background thread too. I'm just not touching any GL functions there. So for textures, for example, the background thread does all the I/O and image decoding work. The render thread then grabs the resulting raw RGBA buffer, then does the create/bind/subimage work on the texture object.

spasi

See the background loader sample. You create either a Pbuffer (deprecated) or a SharedDrawable and share resources with the Display context.

In any case, it is highly recommended to migrate your code to LWJGL 3. Context sharing is easily achieved by sharing contexts of different GLFW windows. Windows can stay hidden, which is useful for offscreen rendering and background loading. LWJGL 3 also includes bindings to WGL/GLX/CGL, if you need to use those directly.

Thiird

Thanks for the answers.

Unfortunately I'm not in the position to be able to port all the rendering engine code to LWJGL 3: I've been working on this project since october and I have to deliver it in about two months, no time to rewrite the whole engine.

QuoteSee the background loader sample. You create either a Pbuffer (deprecated) or a SharedDrawable and share resources with the Display context.

I think this is the way to go. I had already tried to implement a sharedDrawable by watching at that file but I wasn't able to, so tell me if I'm doing something wrong:



public class main
{
public static void main(String[] args)
{
    Application app = new Application();
    Loader loader = new Loader(app.getDrawable());

    new Thread(app).start();
    new Thread(loader).start();
}

}

//Main app class
public Application 
{ 

//Empty constructor

@Override
public void run()
{
    //Display creation

    //Rendering
}

public Drawable getDrawable()
{
    return Display.getDrawable();
}





//Loader class
public loader
{
    private SharedDrawable sharedD;
    
public loader(Drawable drawable)
{
    this.sharedD = new SharedDrawable(drawable)
    this.sharedD.makeCurrent();
}

@Override
public void run()
{
    //GL calls and general work
}

}

spasi

You're making the loader context current in the constructor, which makes it current in the main thread. Move the makeCurrent() call to the run() method.

Btw, porting OpenGL code from LWJGL 2 to LWJGL 3 should be extremely simple. Most, if not all, functions are source compatible. The main issue is porting Display and input to GLFW, which may require a bit of work, depending on the nature of your application.

Thiird

QuoteYou're making the loader context current in the constructor, which makes it current in the main thread. Move the makeCurrent() call to the run() method.

I did: the program doesn't crash, and to test this I loaded a big 3d model in the secondary thread, and I can clearly see that there is a pause when it loads it, but the model (which is so big I cant miss it) doesn't show up in the viewport.
This makes me think that the new data isn't being picked up by the main rendering thread...?

EDIT: Actually, as far as I can tell, its a problem with my rendering engine, I'm trying to solve and I'll post the solution if I ever get out of this.

Thiird

QuoteThis makes me think that the new data isn't being picked up by the main rendering thread...?

I think this is true.

http://prntscr.com/jazsvf

Every time I load a model I print out its vao ID and other informations, the first three are loaded from the main thread, and the last three from the secondary thread: as you can see the first three have vao ID's from 1 to 3 and those loaded from the secondary thread have the same values. This makes me think that the resources of the two context are not shared at all (therefore rendering error), something is wrong about how I have set thing up.

KaiHH

Quote
Each context has its own set of OpenGL Objects, which are independent of those from other contexts. A context's objects can be shared with other contexts. Any OpenGL object types which are not containers are sharable, as well as Sync Objects and GLSL Objects (excluding program pipeline objects). All container objects are not shared between contexts.
https://www.khronos.org/opengl/wiki/OpenGL_Context

Quote
Not all object types can be shared across contexts. Objects that contain references to other objects cannot be shared. All other object types can be shared. This includes GLSL Objects and Sync Objects, which do not follow the OpenGL Object model.
https://www.khronos.org/opengl/wiki/OpenGL_Object#Object_Sharing

Do some research on your own by simply googling "OpenGL sharing" or "OpenGL object sharing". VAOs are considered container objects, so are not shared between contexts. As a rule of thumb, only OpenGL objects that are actually resources allocated on the graphics card (textures, buffers, shader programs) are shared. Objects that simply contain pointers/handles to other objects, only held in the driver to juggle around objects, are not shared.

Thiird

QuoteVAOs are considered container objects, so are not shared between contexts

Ok I didn't know that. I have also read your links, very usefull.

What I'm going to do is to use the secondary thread to load the obj data from file and notify the primary thread which will, apart from render the scene, load an entity based on the previously loaded data when required.

I'll post more details if needed.


Thanks to all.