GL extension handling (on Linux)

Hi,

I have problems building my OpenGL code on Linux. Although most GL calls are available, it fails with glGenVertexArrays not being defined. Normally graphics engines or drawing applications use some kind of GL loading library in order to manage the different extensions and their availability on different platforms, graphics driver implementations and OpenGL versions. It seems JUCE is doing this somehow by itself? The code compiles fine on MacOS X 10.11. I want to try to make this work by using glbinding for this, a rather new library which has some great properties such as clean namespace scoping and typesafe interfaces for all the GL functions: https://github.com/cginternals/glbinding

It does not have to be glbindings of course. Any loading library, like the aging GLEW or glxw, glLoadGen, flextGL, etc. would be fine for me. For some background see https://www.opengl.org/wiki/OpenGL_Loading_Library.

So there are frameworks which handle OpenGL extensions by themselves to some extent, but offer a #define which can be set before including their headers, in order to flag that they shall not include the GL headers by themselves. For example look at GLFW:

http://www.glfw.org/faq.html#can-i-use-extension-loaders-with-glfw

http://www.glfw.org/docs/latest/context.html#context_glext

Is some mechanism like this available in JUCE? If not, how do you think I should go about this? Looking at the headers I don't immediately see that's possible. Maybe you can just fix it in JUCE, which would be ok, but using a proper loading library would be preferrable.

I want to mention that I've read other forum posts where people have been asking about this:

http://www.juce.com/forum/topic/glew-juce

http://www.juce.com/forum/topic/opengl-and-glew

http://www.juce.com/forum/topic/opengl-extensions

where it seems you are for now sticking with your own solution. But maybe things have changed since Jan 2014?

 

Thanks for your support!

I tried implementing GLFW in JUCE on Windows; it was a total PITA to get it to compile due to it redefining some macros, and I wanted to see if I could embed the header and c file into library so as to avoid linking it.

Would be nice to have something like it in, though, or if JUCE could be extended to load more than the bloody old OpenGL 2 functions...

Hmm looking further into this, I somehow get the feeling "modern" OpenGL is not supported inside Juce at the time being? Having worked out some different problems now in order to do a run-time test on Mac OS 10.11, I get 

ERROR: 0:1: '' :  version '330' is not supported
ERROR: 0:3: 'layout' : syntax error: syntax error

from the shader compilation stage, which would be a showstopper for us.

Getting rid of the shader code and trying to work with glGenVertexArrays again (on Mac) I get

***** GL_INVALID_OPERATION  at /Users/schmitz/mnt/development/ircam/ARP/JuceLibraryCode/modules/juce_opengl/opengl/juce_OpenGLContext.cpp : 183
JUCE Assertion failure in juce_opengl.cpp:141

glGenVertexArrays is present in core since version 3.0, so I'm really getting worried here.

Does Juce only support the legacy OpenGL API? What is the JUCE_OPENGL3 define (which is set on my platforms) for? 

Thanks!

From my brief analysis of juce_opengl.h; JUCE supports OpenGL ES 2.0 out of the box, and 3.(?) also so long as it's available as part of the OS (only OSX 10.7+, afaict).

First of all, congrats to the JUCE4 release!

Using the JUCE4 codebase I am currently trying to make everything work with the glbindings library. I introduced a JUCE_NO_GLINCLUDES define, took care of the header inclusion inside Juce, made all the juce-internal gl API calls consistent with the strict typing glbinding enforces. Everything compiles fine already, however I get a failed assertion from inside glbinding.

Having contacted the glbinding developers, they told me that it's likely a threading issue. The stacktrace


#0  0x00007ffff45b35f8 in raise () from /usr/lib/libc.so.6
#1  0x00007ffff45b4a7a in abort () from /usr/lib/libc.so.6
#2  0x00007ffff45ac417 in __assert_fail_base () from /usr/lib/libc.so.6
#3  0x00007ffff45ac4c2 in __assert_fail () from /usr/lib/libc.so.6
#4  0x00007ffff64ed87d in glbinding::AbstractFunction::state (this=0x7ffff6c1ec40 <glbinding::Binding::GetString>, pos=-1) at /home/bzk/development/graphics/glbinding/source/glbinding/source/AbstractFunction.cpp:51
#5  0x00007ffff64ed81a in glbinding::AbstractFunction::state (this=0x7ffff6c1ec40 <glbinding::Binding::GetString>) at /home/bzk/development/graphics/glbinding/source/glbinding/source/AbstractFunction.cpp:45
#6  0x00007ffff64edcd4 in glbinding::AbstractFunction::address (this=0x7ffff6c1ec40 <glbinding::Binding::GetString>) at /home/bzk/development/graphics/glbinding/source/glbinding/source/AbstractFunction.cpp:142
#7  0x00007ffff66890d8 in glbinding::Function<unsigned char const*, gl::GLenum>::operator() (this=0x7ffff6c1ec40 <glbinding::Binding::GetString>, arguments#0=@0x7fffdf62ccfc: <incomplete type>) at /home/bzk/development/graphics/glbinding/source/glbinding/include/glbinding/Function.hpp:126
#8  0x00007ffff6650ca7 in gl::glGetString (name=<incomplete type>) at /home/bzk/development/graphics/glbinding/source/glbinding/source/gl/functions_g.cpp:1635
#9  0x00000000007bc96a in juce::OpenGLShaderProgram::getLanguageVersion () at ../../../juce-grapefruit-linux/modules/juce_opengl/opengl/juce_OpenGLShaderProgram.cpp:57
#10 0x00000000007c70b5 in juce::OpenGLContext::CachedImage::initialiseOnThread (this=0x1158ef0) at ../../../juce-grapefruit-linux/modules/juce_opengl/opengl/juce_OpenGLContext.cpp:458
#11 0x00000000007c6f2f in juce::OpenGLContext::CachedImage::runJob (this=0x1158ef0) at ../../../juce-grapefruit-linux/modules/juce_opengl/opengl/juce_OpenGLContext.cpp:411
#12 0x000000000051ff97 in juce::ThreadPool::runNextJob (this=0x115a0c0, thread=...) at ../../../juce-grapefruit-linux/modules/juce_core/threads/juce_ThreadPool.cpp:341
#13 0x00000000005593ed in juce::ThreadPool::ThreadPoolThread::run (this=0x115a190) at ../../../juce-grapefruit-linux/modules/juce_core/threads/juce_ThreadPool.cpp:40
#14 0x000000000051ea14 in juce::Thread::threadEntryPoint (this=0x115a190) at ../../../juce-grapefruit-linux/modules/juce_core/threads/juce_Thread.cpp:101
#15 0x000000000051ea7f in juce::juce_threadEntryPoint (userData=0x115a190) at ../../../juce-grapefruit-linux/modules/juce_core/threads/juce_Thread.cpp:113
#16 0x000000000053a686 in juce::threadEntryProc (userData=0x115a190) at ../../../juce-grapefruit-linux/modules/juce_core/native/juce_posix_SharedCode.h:852
#17 0x00007ffff6c614a4 in start_thread () from /usr/lib/libpthread.so.0
#18 0x00007ffff466913d in clone () from /usr/lib/libc.so.6

shows the problematic calls are made from inside a ThreadPoolThread::run function. This time it crashed with glGetString, last time it was with a glViewport call. Do you take provisions inside of Juce to assure that all ThreadPoolThreads making GL calls are always executed in the same physical thread which created the respective GL context? Actually I think it does not make that much sense to have GL calls from a ThreadPool at all, since it's purpose is to schedule job executions dynamically on different physical threads, but probably I'm missing something here.

Regarding GL3 availability inside Juce, I looked closer at the juce_opengl.h header. it seems it is only set automatically on MacOS (10.7+) after all. Setting it myself in the Projucer file does not change things, since it looks like you assume the function to be readily available from the system header, which is an assumption which does not hold across platforms (and what the binding libraries are for).

Thanks for your support!

Hmm.. We use a ThreadPool because it makes it easier for synchronisation when we tear down the job on completion, but as far as we can tell there shouldn't be a case where it swaps to a physically different thread..

Ah so each component has a dedicated thread then for sure? I had the notion of a ThreadPool that it was a finite collection of actual physical / operating system threads, onto which any number of actual computation jobs can be scheduled dynamically, in order to get rid of the thread setup / teardown overhead. This is just what pool implied for me, I'm a bit new to the inner workings of Juce, so please bear with me :) I hope we'll be able to work this out together. Thanks! 

Yeah, it was a valid question to ask, and I also had to double-check the code, but I think it's safe the way it works!

Looking at addJob this kinda seems to be the idea after all 

        Once a job has been added, then the next time a thread is free, it will run
        the job's ThreadPoolJob::runJob() method. Depending on the return value of the
        runJob() method, the pool will either remove the job from the pool or add it to
        the back of the queue to be run again.
 

BUT

every OpenGLContext::runJob does a 

 while (! shouldExit())...
Thus it never terminates actually, presuming that shouldExit returns true only when the context gets destroyed?

So it's more of a thread list, not really making use of the pooling feature. But fine of course...

Well gotta see what's actually going wrong here.

Hey there, good news!

Got it all worked out, Juce4 is compiling, running, and using modern GL calls on Linux using the glbinding library. I wasn't aware of the fact the glbinding, apart from per context initialization, needs to have its reference to the currently active context updated after each switch. All GL calls in Juce4 now make use of the GL API in a type safe fashion (well there were two or three "hard" casts from GLenum to int which were not easy to resolve otherwise) but everything seems fine now. No problems on Juce's threading side after all. Again, thanks for your kind support! Now let's do some advanced GL rendering shall we? *yay*

:)

And share your results with us - we always enjoy seeing people's whizzy GL apps!

Will do! I might note that glbinding is MIT licensed and cross-platform, so while you are redesigning your OpenGL context handling in Juce anyways, you might consider integrating this upstream such that you have all the GL calls and extensions readily available on all platforms, and rather spend your time on actual Juce issues. I would share the code if you are interested in that. A colleague told me you'll will be visiting IRCAM end of the month anyways, so maybe we can take the opportunity to quickly talk about this and your changing GL implementation as well, since that's very relevant to our project.

So have a nice day, happy hacking!

flavi0 > I have just sent you a PM, tell me if it worked ;)

Hey Wolfen, read your PM, sorry for not answering yet it was a quite busy weekend, will answer soon.

No worries, I wasn't sure if I have been able to send the message ;)