3

我使用 d3d11 已经有一段时间了,在发现了 directx 调试器之后,我最近发现我的程序到处都在从所有未正确释放的 com 对象中泄漏内存。经过一番窥探和数小时的代码盯着之后,我开发了一些方法来隔离我在哪里得到这些意外增加的引用计数。

首先,所有对象都包含在 std::shared_ptrs 中,并带有调用它们各自释放函数的自定义删除器。我这样做是为了永远不必调用 addref,并且第一次调用 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();
}

其中,只是增加和减少计数以获得该对象上的当前引用计数。使用这个,我发现,就在调用自定义删除器中的发布之前,有 10 个对我创建的 1 ID3D11Device 的未完成引用。好奇,我慢慢地回溯,在程序中一直调用这个函数,直到我最初创建它的地方。有趣的是,就在我第一次创建对象之后(甚至在 shared_ptr 获得所有权之前),未完成的 refs 的数量已经是 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

对于 Direct3D (COM) 对象,使用 shared_ptr 是不正确的,即使您使用自定义删除器也是如此。

首先,COM 对象使用侵入式引用计数,这意味着引用计数存储在对象本身中。另一方面,shared_ptr 使用非侵入式引用计数,这意味着引用计数存储在智能指针对象中。因此,对 COM 对象使用 shared_ptr 意味着您有两个独立的引用计数:COM 对象和 shared_ptr。

其次,使用自定义删除器解决了正确释放对象的问题,但并没有解决正确获取对象引用的问题。将 COM 对象分配给 shared_ptr 将增加 shared_ptr 的引用计数,但不会增加对象的引用计数。

这就解释了为什么要泄漏对象:D3D 方法会增加对象的引用计数,但您使用的 shared_ptrs 在对象的整个生命周期内仅减少一次对象的引用计数(当指向对象的所有 shared_ptrs 都被销毁时)。

因此,您需要使用 COM 智能指针,例如 ATL 的CComPtr

于 2012-11-30T12:19:00.850 回答