Sharing VBOs across a JOGL context

I’m trying to get VBOs shared across multiple QGLWidgets. I’m using JOGL and jruby and noticed when I switched to VBOs from immediate mode, I no longer have any meshes drawing in the secondary windows. I’m sharing the VBOs in a global variable. I understood that I just had to create a pbuffer and share the contexts between multiple GLWidgets. That doesn’t seem to be the case, however. Maybe I’m not setting up my VBO correctly or there’s another step to setup the context? Or an explicit sharing function?

class PanelGL < QGLWidget
  @@pBuffer = nil
  def initialize(parent = nil)
    super
    setMouseTracking(true)
    profile = GLProfile.get(GLProfile::GL3)
    glCaps = GLCapabilities.new(profile)
    glCaps.setPBuffer true
    if not @@pBuffer
      @@pBuffer = GLDrawableFactory.getFactory(profile).createGLPbuffer(glCaps,
        DefaultGLCapabilitiesChooser.new(),
        1, 1, nil)
    end
  end

You can see the code in it’s entirety here with the important stuff starting around line 750.

http://code.google.com/p/sunshine/source/browse/src/main/resources/com/googlecode/sunshine/clear_scene.rb?r=9a2872d17b51

Looking at your code around line 750, I don’t think you’re sharing the contexts correctly. You need to share contexts when you create a pbuffer or QGLWidget. When creating a GLCanvas or pbuffer, the last argument to the constructor or factory method is a GLContext. This is the context that is shared. Every created window or pbuffer that is sharing with that context shares with each other.

It seems like QGLWidget does things a little differently. They have a constructor that takes a QGLWidget to share with. If you want sharing to work with Qt, you should create one QGLWidget as your primary widget and make sure that doesn’t change. Then any other window you need will be created with the primary widget as the shareWith argument. It doesn’t look like you can use JOGL pbuffers to specify a context because they don’t expose enough.

I’ve seen the sharewith property before, but I’m not sure how to properly use it. When I create the first one, do I still need to create pbuffer object or do I need to do something else? I’ve spent hours looking for context-sharing in QGLWidgets, but the only posts I’ve found are just similar ones I’ve created earlier. I might remember some site long ago saying I had to create my first widget and update it because it wouldn’t have a context to share later until I drew it. Is that really required, because that seems like a massive headache, and documentation is very old for JOGL.

I just want a simple example to create multiple QGLWidgets that share the same GL3 context.

Thanks.

Unless I missed something, I can’t see how to share an actual GLContext with QGLWidgets when you create them. The only constructor they give you has the QGLWidget as the sharewith.

Although this will suck, you will have to do it this way:

  1. Create your first QGLWidget without any sharewith parameter.
  2. Draw once with the widget to make sure it is displayed (this is an unfortunate consequence of how JOGL works I think)
  3. Create your other QGLWidgets and always pass in the QGLWidget from #1 as the sharewith argument.

I changed the code around so it’s only making one QGLWidget at startup, which seems to work fine. However, when creating a new QGLWidget after launching, I still don’t see anything drawn in that widget. I guess I could call “isSharing” on the secondary widgets to see if they really are sharing. I’m using jruby so I’m wondering if when I call the super constructor, it’s really sending @@firstPanel to the correct function or sending it to the wrong constructor and just not displaying any errors and not sharing. I might do this part in java or scala just to make sure.


class PanelGL < QGLWidget
  @@firstPanel = nil
  @@pBuffer = nil
  def initialize(parent = nil)
    if @@firstPanel
      super(parent, @@firstPanel)
    else
      super

      profile = GLProfile.get(GLProfile::GL3)
      glCaps = GLCapabilities.new(profile)
      glCaps.setPBuffer true
      if not @@pBuffer
        @@pBuffer = GLDrawableFactory.getFactory(profile).createGLPbuffer(glCaps,
          DefaultGLCapabilitiesChooser.new(),
          1, 1, nil)
      end
      @@firstPanel = this
    end
  end

  def initializeGL
    @ctx = @@pBuffer.getContext()
  end
  attr_reader :gl

  def resizeGL(width,height)
    @ctx.makeCurrent()
    gl = @ctx.getGL.getGL3
    gl.glViewport(0,0,width,height)
  end

  def paintGL
    @ctx.makeCurrent()
    @gl = @@pBuffer.getContext().getGL.getGL3
    ...

I still don’t know why you are creating a pbuffer, though. Why can’t you use the context from the QGLWidget, i.e. the context() method? Maybe you need to use GLDrawableFactory.getFactory().createExternalContext() but I’ve never done that (I’m a little out of my element with this Qt stuff, sorry).

I’m a little out of my element, too. I’ve been using a pBuffer because that’s the only way I’ve ever been able to create a GL3-profiled context. And from what I understand the QGLContext and GLContext are two different things, where GLContext is from JOGL and can provide a valid “GL” object for running commands. Maybe I can try going without GL3 for a bit and doing everything through Qt. Then I can try GLContext.getCurrent() to see if JOGL can figure out the context can give me a JOGL context to get the GL object from.

Reading the docs for GLDrawableFactory.createExternalContext(), I think you can still use a GL3 context. It will be a little bit of a headache because you aren’t supposed to use the makeCurrent() and release() methods on the GLContext, but would need to use the similarly named methods on QGLWidget.context().

In your initialize() method (I think), try doing something like:


widget.context.makeCurrent() // This is the QGLWidget here
gl_context = GLDrawableFactory.getFactory(GLProfile.get(GLProfile::GL3)).createExternalContext() // The qgl context must be current for this to work
gl = gl_context.getGL.getGL3

You would still use the shareWith as I described above with the QGLWidgets. Every time you need to use the GLContext, make the widget.context current instead and then everything should get connected behind the scenes correctly. You might want to ask for a new GL every time you need to render in case the instance changes.

HTH

I have the following, which seems to almost work. I just create a vanilla QGLWidget with no parameters to the constructor and the following code. The problem is getting the GL3 object. The strange thing is in resizeGL I can call getGL3 without problems. But in paintGL calling it returns a javax.media.opengl.GLException: Not a GL3 implementation (NativeException) error. Not sure why. I’ve tried calling makeCurrent on the Qt context, but then resizeGL gives the same error. I’ve confirmed that GLProfile.get(…) returns a GL3 profile, but the createExternalGLContext function returns a GL2 implementation.


 def initializeGL
    @glContext = GLDrawableFactory.getFactory(GLProfile.get(GLProfile::GL3)).createExternalGLContext()
  end

 def resizeGL(width,height)
    @glContext.makeCurrent()
    gl = @glContext.getGL.getGL3
    
    gl.glViewport(0,0,width,height)
  end

  def paintGL
    @glContext.makeCurrent()

    @gl = @glContext.getGL.getGL3
   ...

[info] com.sun.opengl.impl.x11.glx.X11ExternalGLXContext [com.sun.opengl.impl.gl2.GL2Impl@1f01b29,
[info] 	Drawable: com.sun.opengl.impl.x11.glx.X11ExternalGLXContext$Drawable[realized true,
[info] 	factory   com.sun.opengl.impl.x11.glx.X11GLXDrawableFactory@7dedad,
[info] 	window    NullWindow[config X11GLXGraphicsConfiguration[class javax.media.nativewindow.x11.X11GraphicsScreen[class javax.media.nativewindow.x11.X11GraphicsDevice[type X11, handle 0x780804f0], idx 0], visualID 0x21, fbConfigID 0x75,
[info] 	requested GLCapabilities[Capabilities[Onscreen: true, Red: 8, Green: 8, Blue: 8, Alpha: 0, Opaque: true], GL profile: GLProfile[GL2/GL2], PBuffer: true, DoubleBuffered: true, Stereo: false, HardwareAccelerated: true, DepthBits: 24, StencilBits: 8, Red Accum: 16, Green Accum: 16, Blue Accum: 16, Alpha Accum: 16, Multisample: false, Num samples: 0, PBuffer-FloatingPointBuffers: false, PBuffer-RenderToTexture: false, PBuffer-RenderToTextureRectangle: false],
[info] 	chosen    GLCapabilities[Capabilities[Onscreen: true, Red: 8, Green: 8, Blue: 8, Alpha: 0, Opaque: true], GL profile: GLProfile[GL2/GL2], PBuffer: true, DoubleBuffered: true, Stereo: false, HardwareAccelerated: true, DepthBits: 24, StencilBits: 8, Red Accum: 16, Green Accum: 16, Blue Accum: 16, Alpha Accum: 16, Multisample: false, Num samples: 0, PBuffer-FloatingPointBuffers: false, PBuffer-RenderToTexture: false, PBuffer-RenderToTextureRectangle: false]], displayHandle 0x780804f0, surfaceHandle 0x4c00018, size 0x0],
[info] 	requested GLCapabilities[Capabilities[Onscreen: true, Red: 8, Green: 8, Blue: 8, Alpha: 0, Opaque: true], GL profile: GLProfile[GL2/GL2], PBuffer: true, DoubleBuffered: true, Stereo: false, HardwareAccelerated: true, DepthBits: 24, StencilBits: 8, Red Accum: 16, Green Accum: 16, Blue Accum: 16, Alpha Accum: 16, Multisample: false, Num samples: 0, PBuffer-FloatingPointBuffers: false, PBuffer-RenderToTexture: false, PBuffer-RenderToTextureRectangle: false],
[info] 	chosen    GLCapabilities[Capabilities[Onscreen: true, Red: 8, Green: 8, Blue: 8, Alpha: 0, Opaque: true], GL profile: GLProfile[GL2/GL2], PBuffer: true, DoubleBuffered: true, Stereo: false, HardwareAccelerated: true, DepthBits: 24, StencilBits: 8, Red Accum: 16, Green Accum: 16, Blue Accum: 16, Alpha Accum: 16, Multisample: false, Num samples: 0, PBuffer-FloatingPointBuffers: false, PBuffer-RenderToTexture: false, PBuffer-RenderToTextureRectangle: false]]] 

If the QGLContext is creating GL2-only contexts, you might be out of luck as far as rendering directly into the widget. They don’t give you enough options to configure, so they probably don’t have support for OpenGL version profiles.

An option you could try is rendering directly to a pbuffer. Instead of creating a 1x1 and trying to use its context for the QGLWidget, you would just use the GLEventListener interface and render with JOGL directly. Then you’d have to figure out a way of copying the pbuffer data to a texture in a QGLContext so that you could render the pbuffer to the screen. Depending on how fast you need things to be, you can just read it into memory and then push it to the widget (might not even need a QGLWidget then).

Alternatively, you could try setting up a shared context again, but render using the pbuffer as above, then use glCopyTexImage to a texture object that has been shared with an external context wrapping a QGLContext (as you got working in the last post), then both contexts should see the rendered-to texture. I don’t know if contexts from different opengl versions can share properly, though. All of this is getting pretty complicated, so I would recommend doing the pbuffer-to-memory copy if its fast enough or living with OpenGL 2.0