Deadlock when using multiple OpenGLRenderer components as children of a non openGL component

It sounds about right, here’s the general steps for the approach I took:

  • We already had a “TopLevelWindow” class for our plugins that was used to handle certain settings like plugin size and such… this is the class I attached the OpenGLContext to

  • I updated our TopLevelWindow to add OpenGLRenderer to the classes it’s deriving from. I didn’t explicitly use setComponentPaintingEnabled(false) because we allow component painting still. HOWEVER, make sure that any components that “fill” the window (including this TopLevelWindow) use setOpaque(true). This is what allows the OpenGL rendering to show through when using the other components described below.

  • Made a RenderView class that inherits both OpenGLRenderer and Component. These are able to be positioned anywhere in the UI, essentially. This are also marked setOpaque(true). When these are opaque and the TopLevelWindow is opaque, you can skip drawing the RenderView background and the OpenGL rendering you’re doing will show through the component painting!

  • Added a Array<RenderView*> renderers member to the TopLevelWindow. When the OpenGLRenderer methods are called like newOpenGLContextCreated(), renderOpenGL(), openGLContextClosing() the TopLevelWindow really just iterates that array and calls those methods for each item

  • Before actually calling renderOpenGL(), we had to ensure that transparency was enabled (glEnable(GL_BLEND) and friends) and ensure that the viewport was set correctly. The viewport is based on supplying getLocalArea() with RenderView*, RenderView.getLocalBounds() and multiplying the result by OpenGLContext::getRenderingScale(). Our plugins allow for resizable windows, so we also had to take our own scale into account as well as that context scale.

  • OPTIONAL: Our plugins have features that require adding, removing, and re-ordering the renderers at run-time. Because these operations would come from the main thread, I had to essentially make two OpenGLRenderer* queues and add methods that atomically let you add/remove renderers. The methods use std::atomic_flag to ensure that only one thing is manipulating the queues at a time, letting us properly set up or tear down the target renderers. For example, if you were to add a renderer while the OpenGL loop is running, you couldn’t call its renderOpenGL() method until calling its newOpenGLContextCreated() method (assuming the renderer needs to allocate buffers etc., which is usually the case). Similarly, renderers in the removal queue have their renderOpenGL() callback called for the last time followed by openGLContextClosing() and are then subsequently removed from the renderers array.

  • OPTIONAL: Finally, RenderView used the same approach as above and held its own array/queues of OpenGLRenderer*. This is mostly because of the previous point: we’ll draw a grid, and RTA display, and an EQ curve all in the same view. If you don’t need that (i.e. each RenderView really just draws one thing) then you can just make a custom subclass of RenderView each time and write your OpenGL drawing code in the subclass

Sorry if that’s long winded :slight_smile: but let me know if you have any other questions and I’ll try to answer them as best as I can! The whole process was very annoying for me so I’d like to help people out in this regard.

6 Likes