OpenGL: secondary thread for loading resources?
Asked Answered
S

3

11

Working with C and Win32, I would like to know how to implement a secondary OpenGL thread for loading resources(textures and VBOs).

From what I found, this should be done with wglShareLists(), but I am unsure about how to set up the secondary thread:

Do I need a new device context or only a new rendering context?

What wgl functions do I need to call?

Schach answered 14/11, 2011 at 1:21 Comment(0)
M
9

You don't need a new context because you can reuse the same device context of the first one. Btw, you can specify another device context, but depending on your platform you should take care about it (on Windows, device contextes must have the same pixel format), otherwise you could fail to share objects between two contextes

Create both context in the main thread, the second one sharing with the first one. Then, make the first one current on the main thread, while making the other one current on the secondary thread. Note that you can share with any render context: all sharing contextes "see" the same object by its name, indeed they share an object name space. Two different object name spaces (i.e. two non-sharing contextes) can have defined the same object (i.e. texture object name is 1), but the same name actually points to different object depending on the current context.

Objects created by the secondary thread are visible concurrently and consistently. However, not all objects can be shared across contextes. Keep in mind that sometimes it happens that the driver supports unexpected objects, and other times it happens that driver doesn't support correctly an expected object.

OpenGL is becoming an object oriented language. You can see a certain pattern for creating objects:

  • Generate name (GenTextures, GenBuffers)
  • Define object (BindTexture, BindBuffer)
  • Object existence (IsTexture, IsShader, IsFramebuffer)
  • Delete name (and object)

(Note that an object created with Gen routines exists only when they are bound)

Object classes could be

  • Display lists
  • Texture objects
  • Buffer objects
  • Shader objects and program objects
  • Renderbuffer objects
  • Framebuffer objects
  • Query objects
  • Sync objects
  • Transform feedback objects

I would suggest to use a "runtime" test, like the following:

private bool TestSharingObject(RenderContext rContextShare)
{
    uint texture = 0;

    // rContextShader is a context sharing with this RenderCOntext

    this.MakeCurrent(true);

    if (Caps.TextureObject.Supported == true) {
        // Generate texture name
        Gl.GenTextures(1, out texture);
        // Ensure existing texture object
        Gl.BindTexture(Gl.TEXTURE_2D, texture);
        Gl.BindTexture(Gl.TEXTURE_2D, 0);
        // Self test
        if (Gl.IsTexture(texture) == false)
            throw new NotSupportedException();
    }

    // Make current sharing context
    rContextShare.MakeCurrent(true);

    return ((texture != 0) && (Gl.IsTexture(texture) == true));
}

Another suggestion would be to run on secondary thread operations that are CPU intensive, and not directly affecting drawing system windows buffers. A good example would be a shader compilation, since the compilation runs on CPU side; keep also in mind that the driver could async your operations, and that OpenGL implementations may pipeline different operations..

Mccrea answered 14/11, 2011 at 18:23 Comment(0)
B
1

Load resources however you need to in your secondary thread and then pass ownership of them back to your primary for the GL calls.

Sharing "lists" between GL contexts should work. Doing all your GL calls on your primary thread does work :)

Bribe answered 14/11, 2011 at 5:24 Comment(4)
+1 In a previous project, we really wanted to find a way to subload resources in another thread and despite spending a few weeks trying out different approaches, it never works reliably. Well... it "works", but the drivers take global locks when you upload stuff in a second thread that block the first thread's operation so it defeats the whole purpose.Chestnut
Thanks, now I wont waste time trying to make a secondary OpenGL thread (I had considered doing what you said but I was thinking it would be more efficient if the secondary thread could upload to OpenGL too).Schach
@Chestnut If these operations are so frequent, why run on different threads? Instead, long-running tasks are reasonable to run on a differet thread (shader compilation, resource data loading, vertex processing).Mccrea
@Mccrea Piccioni we had near real-time constraints (i.e. never drop below 60hz) and huge environments (hundreds of GB) that were paged progressively as the viewpoint moved. The textures were 4kx4k texels so uploading a whole texture took around 2ms. 2ms out of 16ms is way too much. That is where a 2nd OpenGL upload thread would have simplified matters, but since it actually caused more glitches, we upload the textures in small patches each frame. The long-running tasks you mention would also be good candidates if it weren't for the glitches you get that way.Chestnut
B
0

It works!, only if wglShareLists is done by the main thread for all the worker thread. Using message map, pass the reference of rendering context and make the main thread create the rendering context, use wglShareLists by main thread alone. Then call wglMakeCurrent on the worker thread with the rendering context created by main thread. It is imperative that wglShareLists is called before any gl operation is done on the rendering context. This is due to the requirement on parameter hglrc2(the OpenGL rendering context to share display lists with hglrc1), that it should not contain any existing display lists when wglShareLists is called.

Bikales answered 5/12, 2012 at 20:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.