WRL::ComPtr 导致 d3d 中的对象泄漏?

Posted

技术标签:

【中文标题】WRL::ComPtr 导致 d3d 中的对象泄漏?【英文标题】:WRL::ComPtr causes object leaks in d3d? 【发布时间】:2021-04-26 17:56:55 【问题描述】:

我正在制作一个简单的 d3d 11 应用程序,我正在使用 Microsoft::WRL::ComPtr 自动释放资源。出于某种原因,当应用程序退出时,我从 d3d 收到 30-40 条 Live Object 警告。我可能会错过一两个,但这似乎更像是我拥有的所有对象的计数。我显然做错了什么。我的一般用法是在某个包装类中拥有一个成员:

class graphics_device

   // ...
   ComPtr<ID3D11Device> hwdevice;

理论上应该在类销毁时释放对象。 (我在堆栈上创建了大多数对象,并且我仔细检查了我是否删除了堆上的对象)

当我创建我的功能时,我将它们作为参考传递。例如:

void graphics_device::create_constant_buffer(u32 size, const void* data, ComPtr<ID3D11Buffer>& constantBuffer)

    D3D11_BUFFER_DESC bufferDesc;
    ZeroMemory(&bufferDesc, sizeof(bufferDesc));

    bufferDesc.Usage = D3D11_USAGE_DEFAULT;
    bufferDesc.ByteWidth = size;
    bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    bufferDesc.CPUAccessFlags = 0;

    D3D11_SUBRESOURCE_DATA initData;
    ZeroMemory(&initData, sizeof(initData));
    initData.pSysMem = data;

    device->CreateBuffer(&bufferDesc, &initData, &constantBuffer);

任何想法我缺少什么?

编辑:忘记发布来自 d3d11 的警告:

D3D11 WARNING:  Live Object at 0x0061D93C, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING:  Live Object at 0x0061DBE4, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING:  Live Object at 0x0061DE8C, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live                         Object :     31 [ STATE_CREATION WARNING #0: UNKNOWN]
DXGI WARNING: Live Producer at 0x005ADDC0, Refcount: 4. [ STATE_CREATION WARNING #0: ]
DXGI WARNING:   Live Object at 0x005B7FD0, Refcount: 2. [ STATE_CREATION WARNING #0: ]
DXGI WARNING: Live                         Object :      1 [ STATE_CREATION WARNING #0: ]

还有更多类似的。在运行时我没有收到任何其他警告。

【问题讨论】:

ComPtr 不应该泄漏任何东西。你有一个复制项目吗?您是否在调试模式 (F5) 下运行过?注意一个对象可以通过级联引用来引用很多其他对象,您也可以命名对象以确定谁分配了它们:walbourn.github.io/object-naming 好主意,我会尝试命名,看看是什么 - 也许这会让我更清楚。还发现了这个可能有共同点的问题,我将在明天阅读:***.com/questions/20032816/… 【参考方案1】:

获得“干净的退出”可能有点挑战。如果使用正确,它不太可能是ComPtr。根据评论,一个好的起点是object naming。请记住,输出发生在 Direct3D 设备被释放时,因此您可能需要查看“释放顺序”以使其干净地退出。还有“内部”与“外部”参考计数,因此它们可能会有些混乱。有关调试层使用的一些说明,请参阅this blog post。

要记住的另一件事是,Direct3D 对象报告不能真正告诉您 DXGI 拥有的对象。这就是为什么使用DXGI Debug Device 也很有用的原因。

directx-vs-templates 将这些博客文章中的所有建议放在一个地方,因此您可能想查看它们。

最后,将ComPtr 作为参数传递通常不是最好的用例,即使通过引用也是如此。使用ComPtr 作为数据成员和局部变量很好,但通常作为参数最好使用“原始指针”,因为生命周期已经是隐式的。所以你的函数签名会更好:

void graphics_device::create_constant_buffer(u32 size, const void* data, ID3D11Buffer** constantBuffer);

然后在调用该函数时,您使用ComPtr

ComPtr<ID3D11Buffer> cb;
graphicsdevice->create_constant_buffer(size, data, &cb);

请参阅this wiki on ComPtr 以获取有关最有效使用它的一些提示。

【讨论】:

为什么传递 ComPtr 作为参考是个坏主意?这不会创建一个新副本(从而增加引用计数),对吧?我觉得这很方便,因为我可以在包装器中管理原始数据,而在实现中不关心它。如果我传递原始指针,我必须记住每次何时释放以及何时只获取原始指针。 (a) 如果您使用原始指针编写函数,则不会强制函数的客户端使用特定的智能指针,(b) 在您将传递原始指针的函数中,生命周期已经是隐含的,所以没有理由首先需要更改引用计数,(c) 在“工厂”函数中,类创建函数已经返回 1 的引用计数,并且 (d)如果您忘记使用 by-ref,那么每次调用都会花费您一个无用的 refcount 循环。我到处都在使用 ComPtr……我只是发现将它用于数据而不是用于参数会更好 感谢您的详细解释:)

以上是关于WRL::ComPtr 导致 d3d 中的对象泄漏?的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL应用程序导致d3d11.dll中的堆栈溢出[关闭]

ObjectMapper SerializerCache 的单个实例中的对象过多导致内存泄漏

LeakCanary检测内存泄漏

Java中的内存泄露的几种可能

如何确定哪些对象持有对导致 Objective-c 内存泄漏的其他对象的引用?

D3D12 不可避免的泄漏报告