multithreadingmacosopenglopenglcontext

Am I allowed to simultaneously render from the same buffer object on multiple shared contexts in OpenGL 2.1?


In Apple's documentation, I read this:

So I expected to be able to create my buffer objects once, then use them to render simultaneously on multiple contexts. However if I do that, I get crashes on my NVIDIA GeForce GT 650M with backtraces like this:

Crashed Thread:        10  Dispatch queue: com.apple.root.default-qos
Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       EXC_I386_GPFLT
…
Thread 10 Crashed:: Dispatch queue: com.apple.root.default-qos
0  GLEngine  0x00007fff924111d7 gleLookupHashObject + 51
1  GLEngine  0x00007fff925019a9 gleBindBufferObject + 52
2  GLEngine  0x00007fff9243c035 glBindBuffer_Exec + 127

I've posted my complete code at https://gist.github.com/jlstrecker/9df10ef177c2a49bae3e. At the top, there's #define SHARE_BUFFERS — when commented out it works just fine, but uncommented it crashes.

I'm not looking to debate whether I should be using OpenGL 2.1 — it's a requirement of other software I'm interfacing with. Nor am I looking to debate whether I should use GLUT — my example code just uses that since it's included on Mac and doesn't have any external dependencies. Nor am I looking for feedback on performance/optimization.

I'd just like to know if I can expect to be able to simultaneously render from a single shared buffer object on multiple contexts — and if so, why my code is crashing.


Solution

  • We also ran into the 'gleLookupHashObject' crash and made a small repro-case (very similar to yours) which was posted in an 'incident' to Apple support. After investigation, an Apple DTS engineer came back with the following info, quoting:

    "It came to my attention that glFlush() is being called on both the main thread and also a secondary thread that binds position data. This would indeed introduce issues and, while subtle, actually does indicate that the constraints we place on threads and GL contexts aren’t being fully respected. At this point it behoves you to either further investigate your implementation to ensure that such situations are avoided or, better yet, extend your implementation with explicit synchronization mechanisms (such as what we offer with GCD). "

    So if you run into this crash you will need to do explicit synchronization on the application side (pending a fix on the driver-side).

    Summary of relevant snippets related to "OpenGL, Contexts and Threading" from the official Apple Documentation:

    [0] Section: "Use Multiple OpenGL Contexts"
    If your application has multiple scenes that can be rendered in parallel, you can use a context for each scene you need to render. Create one context for each scene and assign each context to an operation or task. Because each task has its own context, all can submit rendering commands in parallel.
    https://developer.apple.com/library/mac/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_threading/opengl_threading.html#//apple_ref/doc/uid/TP40001987-CH409-SW6
    
    [1] Section: Guidelines for Threading OpenGL Applications
    (a) Use only one thread per context. OpenGL commands for a specific context are not thread safe. You should never have more than one thread accessing a single context simultaneously.
    (b) Contexts that are on different threads can share object resources. For example, it is acceptable for one context in one thread to modify a texture, and a second context in a second thread to modify the same texture. The shared object handling provided by the Apple APIs automatically protects against thread errors. And, your application is following the "one thread per context" guideline.
    https://developer.apple.com/library/mac/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_threading/opengl_threading.html
    
    [2] OpenGL Restricts Each Context to a Single Thread
    Each thread in an OS X process has a single current OpenGL rendering context. Every time your application calls an OpenGL function, OpenGL implicitly looks up the context associated with the current thread and modifies the state or objects associated with that context.
    OpenGL is not reentrant. If you modify the same context from multiple threads simultaneously, the results are unpredictable. Your application might crash or it might render improperly. If for some reason you decide to set more than one thread to target the same context, then you must synchronize threads by placing a mutex around all OpenGL calls to the context, such as gl* and CGL*. OpenGL commands that blockâsuch as fence commandsâdo not synchronize threads.
    https://developer.apple.com/library/mac/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_threading/opengl_threading.html