3

私はかなり長い間 d3d11 を使用してきましたが、directx デバッガーを発見した後、最近、私のプログラムが適切に解放されていないすべての com オブジェクトからメモリをリークしていることを発見しました。少しスヌーピングし、何時間もコードを見つめた後、これらの予期しない参照カウントの増加が発生している場所を特定する方法をいくつか開発しました。

まず、すべてのオブジェクトが std::shared_ptrs にラップされ、それぞれのリリース関数を呼び出すカスタム デリーターが含まれています。これを行ったのは、addref を呼び出す必要がないようにするためであり、オブジェクトがスコープ外になったときにのみ、deleter 内の release への最初の呼び出しが呼び出されるようにしました。次のようになります。

// in D3D11Renderer.h
...
// declaration
std::shared_ptr<ID3D11Device *> m_Device;
...

// after call to ID3D11CreateDeviceAndSwapChain
m_Device.reset(device, [](ID3D11Device * ptr){ptr->Release();})

問題は、API呼び出しの特定のランダム関数が参照カウントをランダムに増加させるだけで、後で対処する必要があることです。

私が診断に役立つと思ったのは、次のような関数でした。

template <typename T>
int getRefCount(T object) 
{
    object->AddRef();
    return object->Release();
}

これは、そのカウントをインクリメントおよびデクリメントして、そのオブジェクトの現在の参照数を取得します。これを使用して、カスタム デリーターのリリースが呼び出される直前に、作成した 1 つの ID3D11Device への未解決の参照が 10 個あることがわかりました。不思議なことに、私はゆっくりとバックトラックし、最初に作成した場所まで、プログラムをずっとさかのぼってこの関数を呼び出しました。面白いことに、最初にオブジェクトを作成した直後 (shared_ptr が所有権を取得する前であっても)、未処理の参照の数はすでに 3 です! これは、この直後に発生します。

result = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, &featureLevel, 1, 
                           D3D11_SDK_VERSION, &swapChainDesc, &swapChain, &device, NULL, &deviceContext);
    if(FAILED(result))
    {
        return false;
    }

デバイスを作成するこのような関数を呼び出すのはこれが初めてで、直後に参照がいくつあるかを確認すると、3 と表示されます。明らかに、これらの com オブジェクトの処理方法について、私は何かを誤解しています。ナンセンスをカウントする舞台裏の参照を使用するのではなく、手動でそれらを削除できる方法はありますか?

4

2 に答える 2

7

バッファーやシェーダー、またはデバイスに依存するものを作成するたびに、そのオブジェクトにはデバイスへの参照が含まれている可能性が高いため、参照カウントを増やして、まだ使用している間に削除されないようにします。

基本的にコード内のデバイスへの単一の参照を保持して削除を停止し、すべての内部参照がなくなったらそれを解放するため、アプローチは全体的にうまくいくように思えます。ただし、d3d は引き続き独自の参照カウントを実行するため、参照カウントは、他のすべての関連オブジェクトへのすべての参照を解放したときにのみゼロになります。スワップ チェーンとデバイスを作成するだけでも、デバイスへの参照を維持する必要があるバック バッファーなどが作成されます。

私はこれと同じアイデアをしばらく試しました...そして最終的には、

#include <atlbase>

次に使用します

CComPtr<ID311Device> m_Device

それは、そのクラスが設計されているものとほぼ同じであり、オブジェクトには既に参照カウンターがあるため、 std::shader_ptr よりも軽量であるため、別のカウンターを保持する必要はありません。

于 2012-11-30T12:39:40.280 に答える
1

カスタム デリーターを使用している場合でも、shared_ptr を Direct3D (COM) オブジェクトに対して使用することは正しくありません。

まず、COM オブジェクトは侵入参照カウントを使用します。つまり、参照カウントはオブジェクト自体に格納されます。一方、shared_ptr は非侵入型の参照カウントを使用します。つまり、参照カウントはスマート poiter オブジェクトに格納されます。したがって、COM オブジェクトに shared_ptr を使用するということは、COM オブジェクトと shared_ptr という 2 つの別個の独立した参照カウントを持つことを意味します。

次に、カスタムのデリータを使用すると、オブジェクトを適切に解放するという問題は解決しますが、オブジェクトへの参照を適切に取得するという問題は解決しません。COM オブジェクトを shared_ptr に割り当てると、shared_ptr の参照カウントは増加しますが、オブジェクトの参照カウントは増加しません。

これが、オブジェクトをリークしている理由を説明しています。D3D メソッドはオブジェクトの参照カウントをインクリメントしますが、オブジェクトの参照カウントをオブジェクトの存続期間全体で 1 回だけデクリメントする shared_ptrs を使用しています (オブジェクトを指すすべての shared_ptrs が破棄されたとき)。

したがって、ATL のCComPtrなどの COM スマート ポインターを使用する必要があります。

于 2012-11-30T12:19:00.850 に答える