D3D11无处不在增加了参考数量?

时间:2012-11-30 11:28:52

标签: c++ com c++11 directx direct3d11

我一直在使用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();
}

,只是递增和递减计数以获得该对象的当前refs数。使用这个,我发现,在调用自定义删除器的发布之前,有10个对我创建的ID3D11Device的未完成引用。好奇,我慢慢地回溯,在程序中一直调用这个函数,直到我最初创建它的地方。有趣的是,在我第一次创建对象之后(甚至在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对象应该被处理的方式。有没有这样的方法,我可以手动删除它们,而不是在那里使用幕后引用计数废话?

2 个答案:

答案 0 :(得分:6)

每次创建缓冲区或着色器或依赖于设备的任何内容时,该对象都可能包含对设备的引用,因此会提高它的引用计数,以确保它在仍在使用时不会被删除。 / p>

听起来你的方法可能总体上起作用,因为你基本上会在代码中保留对设备的单一引用以阻止它被删除,并且当你的所有内部引用都消失后释放它。但是d3d仍然会执行它自己的引用计数,因此当您释放对每个其他相关对象的每个引用时,引用计数将仅降至零。即使只是创建交换链和设备也会产生回缓冲等等,这可能需要维护对设备的引用。

我尝试了同样的想法......最后发现它更容易

#include <atlbase>

然后使用

CComPtr<ID311Device> m_Device

因为这几乎就是该类的设计,并且它比std :: shader_ptr更轻量级,因为对象中已经有一个引用计数器,所以不需要单独保存它。

答案 1 :(得分:1)

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

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

其次,使用自定义删除器可以解决正确释放对象的问题,但它无法解决正确获取对象引用的问题。将一个COM对象分配给shared_ptr将增加shared_ptr的引用计数,但不会增加该对象的引用计数。

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

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