Context Sharing Patch


Contents


Why would I want to share GLX contexts?

AGL, GLX, and wGL all support GL context sharing. When two GL contexts are shared, what this means is that they share the same call list and (for GL >= 1.1) texturing namespaces. So, if you have a form with 4 GL canvases on it, the way it is now, and you want to display the same, say, 60K+ polygon surface model on all 4 canvases, you need to create 4 separate call lists. So you need to store the same call list in memory 4 times, and you need to take the time to generate 4 identical call lists. And if you have the same texture that you want to display on all 4 canvases, you have to load the same texture 4 times. Same deal.

For example (as far as texturing goes), if you wanted to display captured video on a surface, you may choose to copy the captured image to texture mapped memory to update that texture and display the video. Your frame rate will suffer if you have to load the same image into memory 4 times simply because you have 4 separate GL canvases. Also, in applications with huge amounts of textures, memory does become an issue when you have to load the same textures into memory 2, 3, or more times.

Context sharing, as far as XForms goes, allows you to use the same call lists and textures for multiple GL canvases. When you think about the impact this has on the amount of memory your application uses and the amount of time you have to spend processing call lists and textures, the benefits are clear. As a matter of fact, GLX makes it simple to share contexts: the glXCreateContext function can actually take, as a parameter, another context which you want to share data with. Problem is, of course, the XForms GL canvas doesn't provide a way for you to specify a context to share data with.

Also note that you can't begin sharing contexts after both contexts are created. You have to specify the context to share data with at the time you create a new context, so it's not something that you can do with GLX calls after you have already created and displayed the GL canvases. glXCopyContext will create a copy of the context but the new context will not share the same call list and texture namespace as the one it was copied from, and you cannot specify a context to share data with in the glXCopyContext call. It -must- be done in the glXCreateContext call. Even if glXCopyContext did work, there currently exists no clean way to change the GLXContext of a GL canvas, as far as I know, although you can "change" it by directly modifying the data in the GL canvases CSPEC.

Top


Why use shared canvas pools?

Consider the alternatives:

Specify GLXContext to share with on glcanvas creation:
This doesn't work because fl_create_glcanvas() just allocates memory for the FL_OBJECT and initializes it's data. It is called when the form is created prior to the canvas actually being shown. The GLXContext for a glcanvas is created when it is shown and destroyed when it is hidden. The only way to make this work, then, would be to make sure that fl_create_shared_glcanvas() is called -after- the other canvases on the form are made visible. There are plenty of other reasons why this doesn't work, as well. Plus it does not lend itself well to fdesign.

Specify FL_OBJECT to share with on glcanvas creation:
This also does not work. If an FL_OBJECT * (call it 'sharewith') is stored in the glcanvas's CSPEC, then initialization of sharewith can be forced in glx_init for the newer glcanvas, guaranteeing that it has a valid GLXContext to share with. This has a number of major problems, among others:

Share pools: A shared canvas pool is simply a list of succesfully initialized glcanvases that should all be sharing contexts with eachother. A pointer to the pool is stored in the glcanvas's CSPEC when it is created. When it's context is initialized (glx_init), it is added to the pool. When it's context is destroyed (glx_cleanup), it is removed from the pool. When it's context is created, the context it should share data with is determined by searching for an initialized canvas in it's pool and using the context from that canvas. If there are no other initialized canvases in the pool then the canvas is deemed the 'root' canvas and no shared context is specified to glXCreateContext. This gives the following benefits:

Top


Why can't canvas pools be destroyed?

Reason:
It is, for all intents and purposes, impossible to be able to tell when a canvas pool is no longer needed. Even if all the canvases in a specific pool are hidden (and therefore that pool is empty), some of those canvases may be made visible again, or new canvases may be created to use that pool.

Justification:
The exact amount of memory used by an empty canvas pool is 8 bytes (for the structure) + the length of the name + 1 byte (for terminating 0 on name). A typical application probably won't have more than 4 or 5 pools any way, but regardless, it would be extremely difficult for an application to use more than 1KB of memory for storing pool structures and names. Additonally, it's not a continuous memory leak -- it's memory that is allocated once when each pool is created, and never explicitly free'd. The one-time allocation and negligible amounts of memory used justify not having a more advanced "garbage collection" for shared pools.

Solution (if necessary):
The best solution to this, if it ever becomes necessary, which it really shouldn't, is to store the name of the pool in the glcanvas's CSPEC rather than a pointer to the actual pool structure. On initialization of the glcanvas, if the pool doesn't exist, it is created. On cleanup, if the glcanvas was the last in the pool, the pool could be destroyed. Main problem: now pool structures have to be looked up by string name each time the canvas is created; more importantly, it is no longer possible to optimize pool context switches by looking up the pool structure by name once then referring to the pool by that structure for the rest of the program. When it comes to GL, efficiency is everything, especially considering the fact that most apps call fl_check_forms() between every frame, which is sluggish enough as it is. Anything to keep things fast is worth it.

Top


Why include "GL/glx.h" in glcanvas.h?

Reasons and justification:

Top


How do I do it?

There are two options for creating a shared canvas. Either use fl_add_shared_glcanvas (or a similar function), or use fl_add_glcanvas and then call fl_set_glcanvas_pool_name() immediately afterwards (the way fdesign does it). You may set up shared canvases in fdesign by creating a glcanvas then setting it's share pool name in the 'Spec' tab of the glcanvas attributes.

After that, to create a call list/texture in that share pool, simply activate the GLXContext of any one of the glcanvases in the pool and do your GL stuff. This can be achieved in one of two ways: either activate one of the canvases with fl_activate_glcanvas(), or let XForms pick a suitable canvas and just activate a pool by name using fl_activate_named_sglpool().

To optimize look ups into the pool name list, you may retrieve an FL_SGLPOOL * representing the actual pool, by name, using fl_get_named_sglpool(). Then just pass that pointer (which will remain valid throughout the entire application) to fl_activate_sglpool().

Initially, a maximum of 32 named pools may exist. You may change that by calling fl_set_max_sglpools() before creating any shared canvases. Typically, you would want to call fl_set_max_sglpools() immediately after fl_initialize().

Note that context sharing shares call lists and, for GL versions >= 1.1, textures. The matrix stack is not shared between contexts, and drawing only occurs on the active context.

Top


Changes

Features:

Specific:

Top


Jason Cipriani (jac4 at mindless dot com), 16-May-2004