OpenGL context lifetime


#1

I spent some time this evening playing around with OpenGL framebuffer objects. It took me a while to figure out why my state wasn’t being maintained, but I finally tracked it down to the fact that OpenGL is not initialized during the constructor of the OpenGLComponent. I find this behavior highly unintuitive, as it means I can’t follow typical class initialization behavior (RAII) for objects which depend on OpenGL being usable.

Basically, I’d like to request to see this fixed in a future version of JUCE. I’ve hacked my local code base for now, but it really seems like the component should work this way by design. Of course, there may be a good reason for this design, but I can’t think of it myself.

One other item for the wish-list (while I’m at it 8) ) would be for the OpenGL components to automatically set up the available extensions (which is a platform-specific task). It’s easy enough to use another library to do this, or to even do it manually when only a few functions are needed, but it seems like this is something that would fit in well with the goals of JUCE. Anyway, this one probably doesn’t need to be implemented in any hurry.


#2

I can’t speak for Jules but my philosophy is to have a near trivial constructor and an initialise function that returns error/status information. my classes also have an initilaised_ bool member, that disallows use of non-setup functions.


#3

Nice thought, but it can’t possibly initialise the context in the constructor - to create a context you need to know the window that it goes in, and a Component doesn’t get associated with a window until it’s been created and added to a parent component, made visible, added to the desktop etc…, which could happen at any time later on.

(In fact it could happen multiple times, if the component gets removed and re-added to another window).

I guess it’d be handy to have a callback in the OpenGLComponent class that tells a subclass when it’s been initialised, but if you know the point in your code when you actually make it visible, that’s the time to do your stuff.


#4

Well, I understand where you’re coming from on this one, but it’s a bit ‘C with classes’ rather than C++, imho. That is to say, it’s somewhat contrary to the Resource Acquisition Is Initialization idiom, which has many advantages in modern object-oriented code. Anyway, I’m not intending to preach (not like RAII is required by the C++ standard or anything, heh), but you might want to check it out… it can really change the way you think about some things! :lol:

Well, this may be true, but I don’t believe it to be as much of a showstopper as you claim. The pixel format request for the OpenGL component seem to be hardcoded to common values, rather than depending on the available formats. At least on Windows and Linux/X11, windows can be reparented. This means a simple solution can be to create an offscreen window which holds the context, and then reparent it when it’s made visible.

In fact, the changes to the code base to get this to happen on Windows border on trivial: the SetParent call can be moved from the juce_createOpenGLContext function to the juce_updateOpenGLWindowPos function, remove the early exit in juce_createOpenGLContext, add an initialise() call to the InternalGLContextHolder constructor, and remove the release/initialise block from InternalGLContextHolder::componentParentHierarchyChanged. Maybe there’s something unsafe about doing it this way, but it seems to work for me, and I’m fairly sure if there’s something wrong, it’s just me overlooking minutia.

Anyway, personally I think this would be the best way, because its very unnatural to not be able to use OpenGL inside an OpenGLComponent subclass’s constructor. For example, in the juce demo, an Image is loaded in the constructor, and then copied to OpenGL each time the canvas is resized, which is highly inefficient compared to using a texture object. Ideally, a texture object would be created in the constructor and deleted in the destructor of the DemoOpenGLCanvas, and the Image could just live on the stack in the constructor to copy the data to the OpenGL texture object.

Anyway, I hope this demonstrates my point. If the constructor and destructor are truly impossible places for creating and destroying the context, then adding an initialised callback event, as you said, would be the next best thing. I could keep patching my code manually, but this gets to be a pain given JUCE’s lack of a version control repository, so it may be a bit selfish of me, but I’d like to see something like this get into the main code base. :slight_smile:

Thanks for all the hard work, jules. I truly appreciate everything you’ve done for us![/url]


#5

Ok - I guess you could work around it on windows. I’ll have to have a look at the mac/linux stuff and see if they have any problems with doing it that way…


#6