Search code examples
c++opengltexturesmesaqopenglfunctions

Texture sharing between QOpenGLContext and native OpenGL context does not work with Mesa drivers


I'm trying to add an UI as a plugin to an existing application using OpenGL. For this I'm rendering the UI into a texture and drawing that texture on top of the 3D scene after the scene was drawn.

The texture is generated as follows:

if (context_ == nullptr)
{
  QSurfaceFormat format;

  format.setDepthBufferSize( 16 );
  format.setStencilBufferSize( 8 );
  format.setMajorVersion(3);
  format.setMinorVersion(3);

  native_context_ = new QOpenGLContext;
  native_context_->setNativeHandle( QVariant::fromValue(
    QGLXNativeContext( native_context_information_->context, native_context_information_->display )));

  if ( !native_context_->create())
  {
    ROS_ERROR( "OverlayManager: Fatal! Failed to create context!" );
  }

  context_ = new QOpenGLContext;
  context_->setFormat( format );
  context_->setShareContext( native_context_ );

  if ( !context_->create())
  {
    ROS_ERROR( "OverlayManager: Fatal! Failed to create context!" );
  }

  surface_ = new QOffscreenSurface;
  surface_->setFormat( format );
  surface_->create();
  if ( !surface_->isValid()) ROS_ERROR( "Surface invalid!" );

  context_->makeCurrent( surface_ );

  paint_device_ = new QOpenGLPaintDevice( 1920, 1080 );

  {
    QOpenGLFramebufferObjectFormat format;
    format.setSamples(16);
    format.setAttachment( QOpenGLFramebufferObject::CombinedDepthStencil );
    fbo_ = new QOpenGLFramebufferObject( 1920, 1080, format );
    texture_fbo_ = new QOpenGLFramebufferObject( 1920, 1080 );
  }
  fbo_->bind();
}
else
{
  context_->makeCurrent( surface_ );
  fbo_->bind();
}
context_->functions()->glClear(GL_COLOR_BUFFER_BIT);

QPainter painter(paint_device_);
painter.setRenderHint(QPainter::RenderHint::Antialiasing);
painter.setBrush(QBrush(Qt::green));
painter.drawRect(0, 0, 400, 300);
//  painter.setPen(Qt::red);
painter.setPen(QPen(QBrush(Qt::red), 4));
painter.setFont(QFont("Arial", 20));
painter.drawText(100, 120, "Hello");
painter.drawText( 10, 80, QString( "Rendertime (ms): %1" ).arg( timer_average_ / 15.0 ));
painter.end();
fbo_->release();
QOpenGLFramebufferObject::blitFramebuffer(texture_fbo_, fbo_);
context_->functions()->glFinish();
// Texture looks fine
context_->doneCurrent();
glXMakeCurrent( native_context_information_->display, native_context_information_->drawable, native_context_information_->context );
// Now it is messed up

The probably more interesting part is the drawing of the texture:

glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_TEXTURE_2D);
glDisable(GL_LIGHTING);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glMatrixMode(GL_PROJECTION);
glPushMatrix();
glOrtho(0, 1920, 0, 1080, -1, 1);

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture_fbo_->texture());

glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex2f(0, 0);
glTexCoord2f(0, 1); glVertex2f(0, 1080);
glTexCoord2f(1, 1); glVertex2f(1920, 1080);
glTexCoord2f(1, 0); glVertex2f(1920, 0);
// glClear(GL_DEPTH_BUFFER_BIT); // Wrong but not the issue, see second edit
glEnd();

glPopMatrix();
glDisable(GL_TEXTURE_2D);
glEnable(GL_LIGHTING);
glMatrixMode(GL_MODELVIEW);

I've mainly found this method of drawing textures together with some comments saying this is deprecated but couldn't really find much on full view overlaid textures using more recent methods. If you have any short resource / howto on that, I'd appreciate it but since this is far from my field of expertise I don't really want to invest more than a couple of hours into this just to avoid using deprecated code.

I hope you've got a somewhat clear picture of what I'm trying to achieve.
This actually works well on my desktop using an NVidia Geforce GTX 1080 using Ubuntu 16.04, Qt 5.5.1, OGRE 1.9.0 and OpenGL 4.6 (GLSL 4.6) and a VM on my desktop using Ubuntu 18.04, Qt 5.9.5, OGRE 1.9.0 and OpenGL 2.1 (GLSL 1.2). However, on my notebook using Ubuntu 16.04, Qt 5.5.1, OGRE 1.9.0 and OpenGL 3 (GLSL 1.3) it doesn't work at all.
Now, the obvious question is: Why is that and how can I fix it?

That's how it looks like on my desktop and VM: Working example

That's how it looks like on my notebook: Broken example

The entire source code can be found here.

Edit: In case it's important, if I move the camera in the bottom example, the white areas also change. Because of that I think they might be leftovers from the scene rendering.

Second Edit: I've done more debugging and it's not the drawing that's wrong as I initially thought but the context switch. I've saved the texture to a file before the switch and after it, and the texture looks as intended before and is messed up after the switch. Now, I just need to figure out why.


Solution

  • I've solved it with a workaround for now.
    When initializing, I check whether context sharing works by creating a texture in one context, switching the context, reading the content back and comparing them.
    If it doesn't, I grab the texture content after the rendering using glGetTexImage( GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel_data_ );, switch the context and upload it again using glTexImage2D.
    Not pretty but it works.