0

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.

Stefan Fabian
  • 498
  • 4
  • 21
  • Perhaps it could have something to do with the graphics drivers since my Notebook has an integrated graphics card (i7 8550U)? – Stefan Fabian Mar 13 '19 at 13:58
  • I have reproduced the issue on another Notebook with an integrated graphics card. I think it may have something to do with the mesa drivers. – Stefan Fabian Mar 13 '19 at 21:01

2 Answers2

2

Calling glClear inside an glBeginglEnd block is invalid. Also I'm not entirely sure, what your intention is with that. If you want to prevent it writing to the depth buffer, you'd use glDepthMask for that. Also disabling depth testing disables depth writes.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • Thanks for the input! I've removed the line. There wasn't really an intention, it was just an artifact from some code samples that I forgot to remove. Unfortunately, that doesn't solve the problem nor changes what's happening. Judging from what it looks like it seems like the texture memory is not what it's supposed to be. Maybe the context sharing isn't working and the texture handle is invalid? – Stefan Fabian Mar 13 '19 at 09:19
  • I've done some more debugging by saving the texture to a file before I did the context switch and before I tried drawing the texture and it looks like the sharing isn't working. The texture looks fine before the switch and is messed up after it. – Stefan Fabian Mar 13 '19 at 21:46
0

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.

Stefan Fabian
  • 498
  • 4
  • 21