As Stefan Hanke as alreadt stated, you don't.
When you can call glDeleteTextures
? When the OpenGL context that has created the underlying texture(s) (or more precisely, it shares the object name space) is current on the calling thread.
The finalizer (the class destructor) is running the GC thread, but actually I don't know whether it's specified how GC is executed (it is responsability of the .NET JIT); I think that the most obvious implementation is a separated thread. Indeed, it's not a good idea to call glDeleteTextures
in the finalizer, since you don't know whether an OpenGL context is current on that thread.
Implementing IDisposable
could be an idea, but the problem remains, since the Dispose implementation must know whether an OpenGL context is really current. For this purpose, you can use platform dependent routines, such as wglGetCurrentContext.
I faced the very same problem, and I've ended up with the following solution.
A context abstraction (RenderContext), that maps the OpenGL context with a thread. Here is the MakeCurrent implementation:
public void MakeCurrent(IDeviceContext deviceContext, bool flag)
{
if (deviceContext == null)
throw new ArgumentNullException("deviceContext");
if (mDeviceContext == null)
throw new ObjectDisposedException("no context associated with this RenderContext");
int threadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
if (flag) {
// Make this context current on device
if (Gl.MakeContextCurrent(deviceContext, mRenderContext) == false)
throw new InvalidOperationException("context cannot be current because error " + Marshal.GetLastWin32Error());
// Cache current device context
mCurrentDeviceContext = deviceContext;
// Set current context on this thread (only on success)
lock (sRenderThreadsLock) {
sRenderThreads[threadId] = this;
}
} else {
// Make this context uncurrent on device
bool res = Gl.MakeContextCurrent(deviceContext, mRenderContext);
// Reset current context on this thread (even on error)
lock (sRenderThreadsLock) {
sRenderThreads[threadId] = null;
}
if (res == false)
throw new InvalidOperationException("context cannot be uncurrent because error " + Marshal.GetLastWin32Error());
}
}
public static RenderContext GetCurrentContext()
{
int threadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
lock (sRenderThreadsLock) {
RenderContext currentThreadContext;
if (sRenderThreads.TryGetValue(threadId, out currentThreadContext) == false)
return (null);
return (currentThreadContext);
}
}
private static readonly object sRenderThreadsLock = new object();
private static readonly Dictionary<int, RenderContext> sRenderThreads = new Dictionary<int,RenderContext>();
If (and only if) the context currency is performed using MakeCurrent method, the RenderContext class can know whether it is current to the calling thread. In conclusion, the Dispose implementation can use the RenderContext class to really delete OpenGL objects.
But, what if the calling thread has no OpenGL context current? I've resolved this issue by introducing a GraphicGarbageCollector, which collect list of resources (texture names, ...) which are freed in the appropriate thread (when the correct OpenGL context is current).
Essentially each resource has an object name space (OpenGL context sharing list; I've defined by using a GUID). Using the object name space, the resource instance can get the appropriate GraphicGarbageCollector, enqueue the resource name (the texture Id, for example); when more appropriate, the GraphicGarbageCollector free the enqueued resources using the underlying context.
Same mechanism can be used with a reference system: when the reference count reachs 0, it dispose the resource, collecting it for garbage collection. It a consistent implementation: you can find the mine here.
SynchronizationContext
to schedule deletion on the OpenGL thread. 2. Create a secondary context withwglShareLists
whose sole purpose is to release OpenGL handles. I don't think there is a solution not making you cringe... – Reichstag