OpenGLContext::executeOnGLThread - why assertion?


#1

Sorry guys for asking so much OpenGL stuff lately :roll_eyes:

For the context: I’m trying to build an OpenGL-based oscilloscope component as a first project to learn OpenGL.

I designed my Component a bit like OpenGLAppComponent so it has an OpenGLContext as member variable (but unlike OpenGLAppComponent mine is private)

In the constructor I set

OpenGLOscilloscope::OpenGLOscilloscope() {
    setOpaque (true);
    openGLContext.setRenderer (this);
    openGLContext.attachTo (*this);
    openGLContext.setContinuousRepainting (true);
};

Now in my design, each audio channel to be displayed has its own vertex attribute buffer. So when adding a channel, from whichever thread (most likely the message thread) I do this, to let the OpenGLThread take care of the buffer allocation:

int OpenGLOscilloscope::addChannel (const String &channelName, Colour channelColour) {

    // a lambda to execute on the gl thread
    openGLContext.executeOnGLThread ([this] (OpenGLContext &openGLContext)
        {
            GLuint glBufferLocation;
            openGLContext.extensions.glGenBuffers (1, &glBufferLocation);
            openGLContext.extensions.glBindBuffer (GL_ARRAY_BUFFER, glBufferLocation);
            openGLContext.extensions.glBufferData (GL_ARRAY_BUFFER,
                                                   static_cast<GLsizeiptr> (maxNumSamples * sizeof (Point<float>)),
                                                   NULL,
                                                   GL_STREAM_DRAW);
            channelGLBuffers.add (glBufferLocation);
        }, false);

    // ... some more work that may run on whichever thread it wants to

This compiles, but an assertion in juce_OpenGLContext.cpp line 1056 fires when calling addChannel, telling me jassertfalse; // You must have attached the context to a component. But wait, didn’t I attach my context to the component right up in the constructor?

I’m not quite sure what I did wrong - one thing I still don’t feel 100% safe at are indeed lambdas, so might this be some stupid lambda-syntax error?


#2

Okay, I could narrow it down a little bit. The cachedImage pointer of the Component is a nullpointer at the moment the assert fires, this is why it says that there is no attachment.

I’m a bit lost down in the depths of the framework - I don’t quite get when which call should have assigned a CachedImage to that member so some feedback on that would be greatly appreciated to find out where to look for the source of this unexpected behavior.

Edit: Just took the debugger and stepped through the code of a working OpenGL Application built on the Projucer’s OpenGL Application template and using OpenGLAppComponent. I found out that the cachedImage in this working app gets created as soon as the Component is set visible. Exactly in line 534 of juce_Component.cpp the working app steps into OpenGLContext::Attachment::componentVisibilityChanged() and then OpenGLContext::Attachment::attach() where the cachedImage is created. However in my Scope component nothing happens in this line, no call to componentVisibilityChanged and so in the end no cachedImage. However all this seems to narrow down the scope a bit for me, but doesn’t make a lot clearer :neutral_face:


#3

Your component must already be on the screen and visible - otherwise the OS has no way to know which GPU to use (in case you have several). Instead of using executeOnGlThread, you could queue the request in a queue and then pull from that queue in your render callback which executes the above commands.


#4

Thank you Fabian, this totally makes sense! I‘ll try doing it that way - could have saved me hours to think about it that way :sweat_smile: