正如斯特凡汉克所说,你没有。
什么时候可以打电话glDeleteTextures
?当已创建底层纹理(或更准确地说,它共享对象名称空间)的OpenGL 上下文在调用线程上处于当前状态时。
终结器(类析构函数)正在运行GC线程,但实际上我不知道它是否指定了GC的执行方式(这是.NET JIT的责任);我认为最明显的实现是一个分离的线程。实际上,调用终结器并不是一个好主意glDeleteTextures
,因为您不知道 OpenGL 上下文是否在该线程上是当前的。
实现IDisposable
可能是一个想法,但问题仍然存在,因为Dispose实现必须知道 OpenGL 上下文是否真的是最新的。为此,您可以使用平台相关例程,例如wglGetCurrentContext。
我遇到了同样的问题,我最终得到了以下解决方案。
上下文抽象(RenderContext),将 OpenGL 上下文映射到线程。这是 MakeCurrent 实现:
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>();
如果(且仅当)上下文货币是使用MakeCurrent方法执行的,RenderContext 类可以知道它是否是调用线程的当前状态。综上所述,Dispose实现可以使用RenderContext类来真正删除 OpenGL 对象。
但是,如果调用线程当前没有 OpenGL 上下文怎么办?我通过引入GraphicGarbageCollector解决了这个问题,它收集在适当线程中释放的资源列表(纹理名称,...)(当正确的 OpenGL 上下文为当前时)。
本质上,每个资源都有一个对象名称空间(OpenGL 上下文共享列表;我使用 GUID 定义)。使用对象命名空间,资源实例可以获得合适的GraphicGarbageCollector,将资源名称(例如纹理 Id)入队;在更合适的时候,GraphicGarbageCollector使用底层上下文释放排队的资源。
引用系统也可以使用相同的机制:当引用计数达到 0 时,它会释放资源,将其收集起来进行垃圾回收。这是一个一致的实现:你可以在这里找到我的。