ステファン・ハンケがすでに述べたように、あなたはそうしません。
いつ電話できますglDeleteTextures
か?基になるテクスチャを作成した(より正確には、オブジェクトの名前空間を共有する)OpenGLコンテキストが、呼び出し元のスレッドで現在使用されている場合。
ファイナライザー(クラスデストラクタ)はGCスレッドを実行していますが、実際には、GCの実行方法が指定されているかどうかはわかりません(.NET JITの責任です)。最も明白な実装は分離されたスレッドだと思います。実際、glDeleteTextures
OpenGLコンテキストがそのスレッドで最新であるかどうかわからないため、ファイナライザーを呼び出すことはお勧めできません。
実装IDisposable
はアイデアかもしれませんが、Dispose実装はOpenGLコンテキストが本当に最新であるかどうかを知る必要があるため、問題は残ります。この目的のために、wglGetCurrentContextなどのプラットフォームに依存するルーチンを使用できます。
私はまったく同じ問題に直面し、次の解決策にたどり着きました。
OpenGLコンテキストをスレッドにマップするコンテキスト抽象化(RenderContext )。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に達すると、リソースが破棄され、ガベージコレクションのために収集されます。これは一貫した実装です。ここで鉱山を見つけることができます。