QOpenGlWidget's ::paintEngine being called from QCoreApplication::processEvents



  • I'm converting an OSX application from Qt 4/Carbon to Qt5.11 with the QOpenGLWidget.

    I've moved the drawing "calls" to my overridden QOpenGlWidget::paintGL().

    The problem is I'm still getting these messages on the console:

    QWidget::paintEngine: Should no longer be called

    Getting a stack trace, I've discovered that this is being called eventually from QCoreApplication::processEvents, which I'm calling from my own internal event loop.

    Here's a stack trace (edited for readability)

    thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: libQt5Widgets_debug.5.dylibQWidget::paintEngine()
    frame #1: libQt5Widgets_debug.5.dylibQOpenGLWidget::paintEngine(0) frame #2: libQt5Gui_debug.5.dylibQPainter::begin()
    frame #3: libQt5Gui_debug.5.dylibQPainter::QPainter() frame #4: libQt5Gui_debug.5.dylibQPainter::QPainter()
    frame #5: libQt5Widgets_debug.5.dylibQWidgetPrivate::drawWidget() frame #6: libQt5Widgets_debug.5.dylibQWidgetPrivate::repaint_sys()
    frame #7: libQt5Widgets_debug.5.dylibQWidgetPrivate::syncBackingStore() frame #8: libQt5Widgets_debug.5.dylibQWidgetWindow::handleExposeEvent()
    frame #9: libQt5Widgets_debug.5.dylibQWidgetWindow::event() frame #10: libQt5Widgets_debug.5.dylibQApplicationPrivate::notify_helper()
    frame #11: libQt5Widgets_debug.5.dylibQApplication::notify() frame #12: libQt5Core_debug.5.dylibQCoreApplication::notifyInternal2()
    frame #13: libQt5Gui_debug.5.dylibQCoreApplication::sendSpontaneousEvent() frame #14: libQt5Gui_debug.5.dylibQGuiApplicationPrivate::processExposeEvent()
    frame #15: libQt5Gui_debug.5.dylibQGuiApplicationPrivate::processWindowSystemEvent() frame #16: libQt5Gui_debug.5.dylibbool QWindowSystemInterfacePrivate::handleWindowSystemEventQWindowSystemInterface::SynchronousDelivery()
    frame #17: libQt5Gui_debug.5.dylibvoid QWindowSystemInterface::handleExposeEvent() frame #18: libqcocoa_debug.dylibQCocoaWindow::handleExposeEvent()
    frame #19: libqcocoa_debug.dylib::-[QNSView updateRegion:](self=0x000061200039fc40, _cmd="updateRegion:", dirtyRegion=QRegion @ 0x00007ffeefbf9b18) frame #20: libqcocoa_debug.dylib::-[QNSView updateLayer](self=0x000061200039fc40, _cmd="updateLayer")
    frame #21: AppKit_NSViewUpdateLayer + 45 frame #22: AppKit-[_NSViewBackingLayer display] + 495
    frame #23: QuartzCoreCA::Layer::display_if_needed(CA::Transaction*) + 634 frame #24: QuartzCoreCA::Context::commit_transaction(CA::Transaction*) + 319
    frame #25: QuartzCoreCA::Transaction::commit() + 576 frame #26: QuartzCoreCA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 66
    frame #27: CoreFoundationCFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION + 23 frame #28: CoreFoundation__CFRunLoopDoObservers + 452
    frame #29: CoreFoundationCFRunLoopRunSpecific + 523 frame #30: HIToolboxRunCurrentEventLoopInMode + 293
    frame #31: HIToolboxReceiveNextEventCommon + 618 frame #32: HIToolbox_BlockUntilNextEventMatchingListInModeWithFilter + 64
    frame #33: AppKit_DPSNextEvent + 997 frame #34: AppKit-[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1362
    frame #35: libqcocoa_debug.dylibQCocoaEventDispatcher::processEvents(this=0x00006040000dbdf0, flags=(i = 0)) at qcocoaeventdispatcher.mm:482 frame #36: libQt5Core_debug.5.dylib`QCoreApplication::processEvents(flags=(i = 0)) at qcoreapplication.cpp:1252
    The problem is that ::processEvents is eventually calling ::paintEngine for the QOpenGLWidget, OUTSIDE of ::paintGL, but it's totally out of my control.

    FWIW, the Event driving this is a QEvent::UpdateRequest.

    I tried overriding ::event in my QOpenGLWidget-inheriting class to call QOpenGlWidget::update when it receives a QEvent::UpdateRequest, but that just ended up making the app non-responsive.

    How should I handle ::processEvents attempting to draw QOpenGlWidgets?

    Thanks!

    P.S. Here's a link to the same question on Stack Overflow: https://stackoverflow.com/questions/51561062/



  • Using the minimal example above, I fixed this by removing this call in our QOpenGlWidget subclass's constructor:

    setAttribute( Qt::WA_PaintOnScreen, true );

    Removing this got rid of the paintengine calls (and numerous other problems).

    Thanks!!!!


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    Can you share your widget's code ?



  • Sure...here are the relevant QOpenGlWidget methods. I purposely have over-simplified them for now.

    Unfortunately, none of these methods are involved in code path being called. :-(.

    void
    UiEventSourceWidget::initializeGL()
    {
    // Set up the rendering context, load shaders and other resources, etc.:
    initializeOpenGLFunctions();
    bool result;
    uint error;

    error = glGetError();
    Log4(L"UiEventSourceWidget::initializeGL:%d glGetError %d", LINE, error);
    Assert(error == GL_NO_ERROR);

    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);

    error = glGetError();
    Log4(L"UiEventSourceWidget::initializeGL:%d glGetError %d", LINE, error);
    Assert(error == GL_NO_ERROR);

    Log4(L"UiEventSourceWidget::initializeGL:%d context 0x%x, defaultFramebufferObject 0x%x",
    LINE, context(), defaultFramebufferObject());
    }

    void
    UiEventSourceWidget::resizeGL(int w, int h)
    {
    Log4(L"UiEventSourceWidget::resizeGL");
    update();
    }

    void
    UiEventSourceWidget::paintGL()
    {
    Log4(L"UiEventSourceWidget::paintGL QOpenGLContext::currentContext() 0x%x, "
    "defaultFramebufferObject() %d, isValid %s, glGetError() %d",
    QOpenGLContext::currentContext(),
    defaultFramebufferObject(),
    isValid() ? L"True" : L"False",
    glGetError());

    uint error;
    error = glGetError();
    Assert(error == GL_NO_ERROR);
    
    glClear(GL_COLOR_BUFFER_BIT);
    error = glGetError();
    Assert(error == GL_NO_ERROR);
    
    glClear(GL_COLOR_BUFFER_BIT);
    error = glGetError();
    Log4(L"UiEventSourceWidget::paintGL:%d glGetError %d", __LINE__, error);
    Assert(error == GL_NO_ERROR);
    
    windowPainter_->updateEntireWindow();
    
    error = glGetError();
    Log4(L"UiEventSourceWidget::paintGL:%d glGetError %d", __LINE__, error);
    Assert(error == GL_NO_ERROR);
    
    Log4(L"UiEventSourceWidget::paintGL ...exit");
    

    }



  • FWIW, in QOpenGLWidget::paintEngine(), d->inBackingStorePaint is True.


  • Lifetime Qt Champion

    By the way, what version of macOS are you running this on ?



  • 10.14 (Mojave) but it also failed under High Sierra.


  • Lifetime Qt Champion

    What is windowPainter_ ?



  • It's the code that does our Rendering.


  • Lifetime Qt Champion

    Would it be possible to also see that ?



  • Well, considering that it's rendering an entire Virtual World (There.com) I'm pretty sure it wouldn't fit in one post :-).

    But, I can replace it with this and get the same problem.

    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    
    AssertNoGlError();
    
    GLint drawFboId = 0, readFboId = 0, drawBoId = 0;
    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &drawFboId);
    glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &readFboId);
    glGetIntegerv(GL_DRAW_BUFFER, &drawBoId);
    
    Log4(L"PoWglPlatform::openPlatform:%d, drawFboId %d, readFboId %d, drawBoId %d",
         __LINE__, drawFboId, readFboId, drawBoId);
    
    AssertNoGlError();
    
    glColor3d(0.0,1.0,0.0);
    // Draw something static to the back buffer.
    glBegin(GL_QUADS);
            glVertex2d(0.0,0.0);glVertex2d(0.10,0.0);glVertex2d(0.10,0.10);glVertex2d(0.0,0.10);
    glEnd();
    glFlush();
    
    // If this line is uncommented, it proves that
    // glDrawBuffer(GL_FRONT) is not working!!!!!!!!!!!!
    //glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
    
    //glDrawBuffer(GL_FRONT);
    
    float angle=0;
    for( angle=0;angle<360.0;angle+=2.0)
    {
        // This should restore our static drawing.
        glFlush();
    
        float x=sin(angle*M_PI/180.0);
        float y=cos(angle*M_PI/180.0);
        glColor3d(1.0,1.0,1.0);
        glBegin(GL_LINES);
            glVertex2d(0.0,0.0);
            glVertex2d(x,y);
        glEnd();
    
        glFinish();
    }
    
    AssertNoGlError();
    

    Also, here's ::initializeGL

    // Set up the rendering context, load shaders and other resources, etc.:
    initializeOpenGLFunctions();

    AssertNoGlError();

    glClearColor(0.0f, 0.0f, 1.0f, 1.0f);

    FWIW, the screen stays blue ( glClearColor(0.0f, 0.0f, 1.0f, 1.0f); ), and never turns red or draws anything we see in ::paintGL.



  • Using the minimal example above, I fixed this by removing this call in our QOpenGlWidget subclass's constructor:

    setAttribute( Qt::WA_PaintOnScreen, true );

    Removing this got rid of the paintengine calls (and numerous other problems).

    Thanks!!!!


Log in to reply