4

OpenGLテクスチャの周りにマネージラッパークラスを実装しようとしています。オブジェクトファイナライザーでを呼び出したいと思いますglDeleteTextures

したがって、ファイナライザーを呼び出すスレッド(GCスレッド?)は、を呼び出すことにより、テクスチャが属するOpenGLレンダリングコンテキストにバインドする必要がありますwglMakeCurrent

ただし、wglMakeCurrentドキュメントには、OpenGLレンダリングコンテキストを同時に複数のスレッドの現在のレンダリングコンテキストにすることはできないと明記されています。

GCがいつでもトリガーできる場合、それが発生したときに他のスレッドがコンテキストを使用していないことを保証することはできません。

glDeleteTextures.netオブジェクトのファイナライザーを呼び出す適切な方法は何ですか?

編集

このラッパークラスは、「オンデマンドでの読み込み」シーングラフの複雑なシステムで使用され、キャッシング戦略WeakReferenceなどによって実装されます。したがって、「手動廃棄」は私が検討したいオプションではありません。私は本当にGCにそれを処理させたいのです。

4

3 に答える 3

4

あなたはそうしない。

インターフェイスを使用しIDisposableて、割り当て解除を決定論的にします。テクスチャオブジェクトを解放するときは、disposeメソッドを呼び出します。内部で、テクスチャを削除します。

これは、管理されていないリソースを割り当てるすべてのオブジェクトに当てはまります。この場合、OpenGLが処理します。


編集: Disposeパターンはここでは適用されないようです。リソースは作成スレッドでファイナライズする必要があり、これを行う標準的な方法ではこのケースを処理できません。

于 2012-04-23T14:47:21.817 に答える
1

ステファン・ハンケがすでに述べたように、あなたはそうしません。

いつ電話できますglDeleteTexturesか?基になるテクスチャを作成した(より正確には、オブジェクトの名前空間を共有する)OpenGLコンテキストが、呼び出し元のスレッドで現在使用されている場合。

ファイナライザー(クラスデストラクタ)はGCスレッドを実行していますが、実際には、GCの実行方法が指定されているかどうかはわかりません(.NET JITの責任です)。最も明白な実装は分離されたスレッドだと思います。実際、glDeleteTexturesOpenGLコンテキストがそのスレッドで最新であるかどうかわからないため、ファイナライザーを呼び出すことはお勧めできません。

実装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に達すると、リソースが破棄され、ガベージコレクションのために収集されます。これは一貫した実装です。ここで鉱山を見つけることができます。

于 2012-05-02T21:55:56.607 に答える
0

さて、これが私がやったことです。

IDisposableリソースオブジェクト(テクスチャ、頂点配列、シェーダーなどの基本クラス)にインターフェイスを実装しました。

破棄されると(または手動で破棄されない場合はファイナライズされると)、リソースはそのIDとタイプをOpenGLコンテキストラッパークラスが所有する同期リストに追加し、最終的にイベントを設定してメインスレッドをウェイクアップします。

アプリケーションメッセージポンプを変更し(現在はMsgWaitForMultipleObjects代わりに使用GetMessageしています)、ループ内でOpenGLコンテキストを取得すると、スレッドはメッセージを処理する前に最初に未使用のリソースを「解放」します(存在する場合)。

これまでのところ、それは魅力のように機能します。

これに関するヒントをくれたStefanHankeに感謝します!

于 2012-05-03T07:22:20.697 に答える