Optimizing rendering with many OpenGL contexts


Recently made the plunge from 1.53 to 3.0.0, woop WHOOP. Thanks for the release Jules. 

On the down side, OpenGL performance has taken a noticeable hit. In the case of my app there are several (5) opengl contexts that update in unison, which may be a rare case.

Strangely enough, CPU time is down for most relevant functions, see the attached (r082=v1.53, r083=v3). 

But some of the panels update at perhaps 15fps. Using the OpenGL low-level renderer across the application didn't improve performance. setComponentPaintingEnabled is set false. 

My model is essentially

// on events (Message thread)
   ScopedLock sl(dataLock)
   ... update data  


       ScopedLock sl(dataLock);
      ... copy tables, vertices, etc ... 


I wonder if there's a way to unify the rendering into a single OpenGL thread? Any other ideas? I've put a lot of effort into getting performance where it was and would be a shame to see it degraded. 



A concurrency analysis, really a horror show - only one context seems to be allowed to run at a time. This is probably my fault by flaw of design, but I the sync objects that are causing this aren't from my app; it's the MessageManagerLock NativeContext::Locker(?) in juce::OpenGLContext::CachedImage::renderFrame. Is there any point in having a thread per context when only one may run?


Yeah, trying to use multiple threads would make things very complicated - I can't remember why the NativeContext::Locker is needed, but I'm sure there was a good reason! Also, a lot of the refactoring was done this way because on Android, it's impossible to supply your own thread, you can only render in the callbacks the OS provides, so I tried to make all platforms work in a way that conformed to that model.

If you *really* need multiple threads, probably the best way is to use the render callback as your master thread, and somehow sync your other threads as workers that do things when the render callback is invoked.



I have no need for multiple threads, but I do need multiple OpenGL panels. 

I was mistaken about the lock causing all the waiting, it's not NativeContext::Locker (I temporarily removed that without negative effect), but context.makeActive() -> wglMakeCurrent(). 

According to msdn, 

"A rendering context can be current to only one thread at a time. You cannot make a rendering context current to multiple threads." 

While that doesn't say "you cannot make multiple rendering contexts current to multiple threads" thats what I think I'm seeing, by nature of driver implementation or whatnot.

It'd seem natural to me to decouple the CachedImage from the thread, make the OpenGLRenderer a thread, and allow multiple CachedImages/contexts per renderer, am I speaking rubbish?


It'd seem natural to me to decouple the CachedImage from the thread, make the OpenGLRenderer a thread, and allow multiple CachedImages/contexts per renderer, am I speaking rubbish?

Not sure.. Something I do intend to do is to figure out a way to make it simple for the user to share a single GL context amongst multiple renderer callbacks. Is that what you mean?


That would potentially reduce video memory via sharing, I assume, but wouldn't reduce the thread-count. 

What I'm thinking is on one thread you could do the following:

void run()
    while(! threadShouldExit())
        for(int i = 0; i < contexts.size(); ++i)
        wait(continuousRendering ? 6 : -1);



I don't see how that'd help.. There's already a thread like that per context, and I can't see much point in trying to use a single thread to service multiple contexts. If you want multiple functions to render on one context + one thread, you can simply call them yourself in your own renderOpenGL callback.


One thread, 5 contexts - 100+ fps

5 threads, 5 contexts - 10-15 fps


​edit - figured it out.