GC & OpenGL (work in progress)

As discussed in the previous chapter, GC finalization occurs on the finalizer thread. This poses some problems on OpenGL resource deallocation, since the context used to create the resources is not available in the finalizer thread!

Since OpenGL functions cannot be called in finalizers, a different methodology must be followed. By implementing the disposable pattern, we can use the Dispose() method to deterministaclly destroy OpenGL resources in the main thread. By modifying the finalizer logic we can provide a way to flag resources as 'dead', and destroy them from the main thread. Last, by extending the concept of the OpenGL context, we can be notified of context destruction, to release all related resources.

The following code describes the implementation of the "OpenGL disposable pattern" in OpenTK, but it is easy to adapt this code to any managed OpenGL project:

// This code is out-of-date. Please do not use it!
 
// The OpenGL disposable pattern
class GraphicsResource: IDisposable
{
    int resource_handle;    // The OpenGL handle to the resource
    GraphicsContext context;      // The context which owns this resource
 
    public GraphicsResource()
    {
        // Obtain the current OpenGL context, and allocate the resource
        context = GraphicsContext.CurrentContext;
        if (context == null)
            throw new InvalidOperationException(String.Format(
                "No OpenGL context available in thread {0}.",
                System.Threading.Thread.CurrentThread.ManagedThreadId));
 
        resource_handle = [...];
 
        context.Destroy += ContextDisposed;
    }
 
    #region --- Disposable Pattern ---
 
    private void ContextDisposed(IGraphicsContext sender, EventArgs e)
    {
        context.Destroy -= ContextDisposed;
        // TODO: Shared resources shouldn't be destroyed here.
        Dispose();
    }
 
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
 
    // If the owning context is current then destroy the resource,
    // otherwise flag it (so it will be destroyed from the correct thread)..
    // TODO: Is the "manual" flag necessary? Simply checking for the
    // owning context should be enough.
    private void Dispose(bool manual)
    {
        if (!disposed)
        {
            if (!context.IsCurrent || !manual)
            {
                GC.KeepAlive(this);
                context.RegisterForDisposal(this);
            }
            else
            {
                // Destroy resource_handle through OpenGL
                disposed = true;
            }
        }
    }
 
    ~GraphicsResource()
    {
        Dispose(false);
    }
 
    #endregion
}

In OpenTK, each GraphicsContext class maintains a queue of OpenGL resources that need to be destroyed. Resources are added to this queue through the RegisterForDisposal() call, and they are destroyed through the DisposeResources() method. The whole process is deterministic: it is your responsibility to call DisposeResources at appropriate time intervals (or setup up a timer event to do this for you).

Resource creation takes a small performance hit due to the call to GraphicsContext.CurrentContext, while garbage collect-able OpenGL resources consume slightly more memory (due to the reference to the GraphicsContext). Prefer calling the Dispose() method to destroy resources instead of relying on the GC, as finalizable resources are only collected on a generation 1 or 2 GC sweep.

The current implementation in OpenTK does not take shared contexts into account - this will be taken care of in the near future.


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
nythrix's picture

Changes:
1. void added to Dispose() and Dispose(bool manual) method signatures
2. GLContext renamed to GraphicsContext
3. IGLContext renamed to IGraphicsContext

Dispose(bool manual) still needs rewriting.

the Fiddler's picture

Corrected a few more occurences of GLContext, added a few comments and marked the whole page as a work-in-progress.

nythrix's picture

Corrected a few more occurences of GLContext
Right, I forgot about the text.

Why I'm being displayed as the author of the page?

the Fiddler's picture

Oddity of the site software. The last non-admin editor of a page automatically takes ownership. A bit dump if you ask me, but fixing this probably isn't worth the time investment.

martinsm's picture

Is this intended that GraphicsContext.RegisterForDisposal method in SVN is private, so this piece of code is currently unusuable?

Also I have doubts about implementation presented here. It is dangerous and not recommended to use another managed class in finalizer. Here GraphicsResource from its finalizer uses GraphicsContext (context member). If GraphicsContext will be already disposed then graphics resource can not be freed. User forgot to correctly free graphics resource - I would think that this situation should not occur, and developer should be informed about resource leak.

I like the way SlimDX (DirectX wrapper for .NET) guys do it. They maintain global dictionary of currently allocated DirectX objects. In resource constructor they add resource to this dictionary, and in resource Dispose methode they remove it. And resource class does not have finalizer. When application exits (or maybe it can also be done in GraphicsContext dispose method) runtime checks if this table is empty. And if not - then leaks are displayed in debugger window. Developer can also manually check if table is or is not empty.

the Fiddler's picture

The implemnentation is private because this code is not production-ready.

If GraphicsContext will be already disposed then graphics resource can not be freed.
The idea is to automatically dispose all registered resources when the GraphicsContext is disposed (but see below).

I like the way SlimDX (DirectX wrapper for .NET) guys do it. They maintain global dictionary of currently allocated DirectX objects.
It's not that simple with OpenGL, because you can have multiple contexts with shared resources (I don't know if this is possible with DX). The idea of simply displaying resource leaks is interesting and certainly easier to implement.

If you find this piece of code helpful or interesting and have some free time, it would be great if you could lend a hand bringing it up to par. Just open a feature request so we can track and help with the development of the new code.

nythrix's picture

The GL context API has undergone changes which are not reflected on this page. Therefore the code above is not to be relied upon.
In my project I use a similar technique to the one you mentioned. Every resource class has a static list of its instances. Any item that's not released manually is deleted before the context gets destroyed.

the Fiddler's picture

This page was updated recently, have we missed anything? (This code has been stale for a long time)

martinsm's picture

It's not that simple with OpenGL, because you can have multiple contexts with shared resources (I don't know if this is possible with DX).
Well then instead of one global collection of resources each context should have collection of resources that was created when it was active. And when context is destroyed it should check if there is some undisposed resources left.

The idea is to automatically dispose all registered resources when the GraphicsContext is disposed (but see below).
Yes, I understand that. But it can not be done automatically. User should checks manually if resources are disposed - because when resource finalizer is called it is not able to notify graphics context to register itself for disposal later. That is because object should not access other managed objects from finalizers (it can access only value types).
Only option I see is for user manually dispose resources that are stored somwhere - like nythirx described. Or how SlimDX is doing - displaying leaked objects: http://code.google.com/p/slimdx/source/browse/trunk/source/ObjectTable.c... But then user should manually register resources that he allocates - again very not friendly. And unfortunately (comparing to Direct3D) every resource in OpenGL is destroyed differently (glDeleteTextures, glDeleteLists, ...) so universal Dispose mechanism inside GraphicsContext for resource is impossible. At least currently I do not see clean way to do it. Probably easiest way is to track resources manually instead of implementing something into inside OpenTK. Like user should dispose resources for previous game level when next is loading or something similar.

the Fiddler's picture

Well then instead of one global collection of resources each context should have collection of resources that was created when it was active. And when context is destroyed it should check if there is some undisposed resources left.
This will still not work, because shared resources should not be destroyed (or marked as leaks) until all relevant contexts are destroyed first. We need some form of reference counting first (resource A belongs to contexts X, Y, Z).

Yes, I understand that. But it can not be done automatically. User should checks manually if resources are disposed - because when resource finalizer is called it is not able to notify graphics context to register itself for disposal later. That is because object should not access other managed objects from finalizers (it can access only value types).
As long as

  1. the GraphicsContext maintains a list of resources, and
  2. it can be made current in the finalizer thread

it should be able to correctly dispose resources without any ordering guarantee. In your example, the context is finalized first, which causes its resources to be Disposed - this means the finalizers will never be called, sidestepping the issue.

I'm pretty sure this falls under "abusing the rules" and there are a number of potential dangers in this approach (throwing an exception will cause a critical shutdown, rule #2 is difficult/impossible to guarantee.)

Until we can guarantee that this will work reliably under all circumstances, this API should stay hidden.

ut then user should manually register resources that he allocates - again very not friendly.
That was the idea from the very start. OpenTK cannot guarantee resource disposal by itself, it can only provide an API that can be used to help with the process. HIgher-level wrappers, like OOGL, a hypothetical OpenTK framework or your own code, could then use this API to track leaks and ensure correct disposal.