Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-47185

Hidden QOpenGLWidget does not render()

    XMLWordPrintable

Details

    • Suggestion
    • Resolution: Done
    • P2: Important
    • 5.10.0 Alpha
    • 5.4.2, 5.9
    • GUI: OpenGL
    • None
    • Windows 7 x64, Qt 5.4.2 x64, Nvidia Quadro K3100M, recent drivers

    • 2ea90c56f2924acc5c620ed7c29a48c72a42efd3

    Description

      I have a hidden QOpenGLWidget I want to capture. This is only possible by doing something along those lines:

      void GLWidget::drawOffscreen()
      {
          //the context should be valid. make sure it is current for painting
          makeCurrent();
          if (!m_isInitialized)
          {
              initializeGL();
              resizeGL(width(), height());
          }
          if (!m_fbo || m_fbo->width() != width() || m_fbo->height() != height())
          {
              //allocate additional? FBO for rendering or resize it if widget size changed
              delete m_fbo;
              QOpenGLFramebufferObjectFormat format;
              format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
              m_fbo = new QOpenGLFramebufferObject(width(), height(), format);
              resizeGL(width(), height());
          }
      
          //#1 DOES NOT WORK: bind FBO and render() widget
          m_fbo->bind();
          QOpenGLPaintDevice fboPaintDev(width(), height());
          QPainter painter(&fboPaintDev);
          painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
          render(&painter);
          painter.end();
          //You could now grab the content of the framebuffer we've rendered to
          QImage image1 = m_fbo->toImage();
          image1.save(QString("fb1.png"));
          m_fbo->release();
          //#1 --------------------------------------------------------------
      
          //#2 WORKS: bind FBO and render stuff with paintGL() call
          m_fbo->bind();
          paintGL();
          //You could now grab the content of the framebuffer we've rendered to
          QImage image2 = m_fbo->toImage();
          image2.save(QString("fb2.png"));
          m_fbo->release();
          //#2 --------------------------------------------------------------
      
          //bind default framebuffer again. not sure if this necessary and isn't supposed to use defaultFramebuffer()...
          m_fbo->bindDefault();
          doneCurrent();
      }
      

      I had expected due to QOpenGLWidget being backed by a FBO the widget would render() just fine, even without an extra FBO.
      Now, Additionally inside paintGL, when using QPainter to paint, this only works when the widget is visible. If the widget is hidden you have to provide a QOpenGLPaintDevice in paintGL():

      void GLWidget::paintGL()
      {
          //When doing mixed QPainter/OpenGL rendering make sure to use a QOpenGLPaintDevice, otherwise only OpenGL content is visible!
          //I'm not sure why, because according to the docs (http://doc.qt.io/qt-5/topics-graphics.html) this is supposed to be the same...
          QOpenGLPaintDevice fboPaintDev(width(), height());
          QPainter painter(&fboPaintDev);
          painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
          //This is what you'd use (and what would work) if the widget was visible
          //QPainter painter;
          //painter.begin(this);
      
          //now start OpenGL painting
          painter.beginNativePainting();
          glClearColor(0.5f, 0.0f, 0.0f, 1.0f);
          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
          ...
          painter.endNativePainting();
          //draw non-OpenGL stuff with QPainter
          painter.drawText(20, 40, "Foo");
          ...
          painter.end();
      }
      

      What is strange about this, is that when using a QGraphicsView with a QOpenGLWidget set as its viewport, the following code works, even with mixed OpenGL + non-OpenGL QGraphicsItems:

      MainWindow::MainWindow()
      {
          scene = new QGraphicsScene;
          hiddenView = new QGraphicsView(scene);
          hiddenGLWidget = new QOpenGLWidget;
          hiddenView->setViewport(hiddenGLWidget);
          //hiddenView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
          //hiddenView->show();
      }
      
      void MainWindow::screenshot()
      {
          //DOES NOT WORK: try regular grab functions
          QPixmap pixmap1 = hiddenView->grab(); //image with scrollbars, no OpenGL content
          pixmap1.save("bla1.png");
          QPixmap pixmap2 = hiddenGLWidget->grab(); //produces an empty image
          pixmap2.save("bla2.png");
          //try grabbing only the QOpenGLWidget framebuffer
          QImage image1 = hiddenGLWidget->grabFramebuffer(); //null image
          image1.save("bla3.png");
      
          //WORKS: render via FBO
          hiddenGLWidget->makeCurrent();
          QOpenGLFramebufferObjectFormat format;
          format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
          QOpenGLFramebufferObject * fbo = new QOpenGLFramebufferObject(hiddenView->width(), hiddenView->height(), format);
          fbo->bind();
          QOpenGLPaintDevice fboPaintDev(hiddenView->width(), hiddenView->height());
          QPainter painter(&fboPaintDev);
          painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
          hiddenView->render(&painter); //WORKS and captures mixed OpenGL and non-OpenGL QGraphicsitems
          //hiddenView->repaint(); //does not work
          //hiddenView->scene()->render(&painter); //does not work
          //hiddenGLWidget->paintGL(); //might work. can not call, protected
          //hiddenGLWidget->render(&painter); //does not work
          //hiddenGLWidget->repaint(); //does not work
          painter.end();
          QImage image2 = fbo->toImage();
          image2.save("bla4.png");
          fbo->release();
          delete fbo;
      }
      

      UPDATE with "MISSING INFO": I have roled my own class now, base on QWindow and createWindowContainer(), because QOpenGLWidget just wasn't working in my case. You can see how I did it at the end of this StackOverflow question. The new widget works fine when rendering while it's hidden.

      Attachments

        Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

              lagocs Laszlo Agocs
              kiro Kim
              Votes:
              2 Vote for this issue
              Watchers:
              6 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Gerrit Reviews

                  There are no open Gerrit changes