我在创建DirectX 10缓冲区时泄漏了内存

时间:2012-04-06 14:06:33

标签: c++ memory-leaks directx

我有一个简单的渲染过程,它将一组顶点发送到几何着色器并从该信息中呈现精灵。

小应用程序的内存使用量不断增加。我使用_CrtDumpMemoryLeaks()和Visual Leak Detector进行了快速测试,他们都声称没有泄漏。

我有一个设备和一个装满顶点信息的容器:

ID3D10Device* pD3DDevice;
std::vector<SpriteVertex>* sprites;

然后在我的RenderSprites()方法中,我几乎注意到了所有内容,直到泄漏停止(渲染为止)。

出现问题的地方就在这里:

void DirectX10Renderer::RenderSprites()
{
    D3D10_SUBRESOURCE_DATA initData;
    initData.pSysMem = &((*sprites)[0]);

    D3D10_BUFFER_DESC bd;
    bd.Usage = D3D10_USAGE_DEFAULT;
    bd.ByteWidth = sizeof(SpriteVertex)*(numSprites);
    bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
    bd.CPUAccessFlags = 0;
    bd.MiscFlags = 0;

    // As soon as the following line is uncommented, memory starts leaking
    pD3DDevice->CreateBuffer(&bd, &initData, &m_SpriteBuffer)); // <-- this is leaking

    // the rest of the rendering code is for now commented out
}

我不确定在每个帧上发布或删除的内容会阻止这种情况发生。

3 个答案:

答案 0 :(得分:4)

由于其他答案已经快速假设了解决方案而且评论有点太短 - 这就是 - 另一个快速。 您询问在运行时更新创建的缓冲区还需要什么

您可以将缓冲区使用率从D3D10_USAGE_DEFAULT(仅限GPU读/写)更改为D3D10_USAGE_DYNAMIC,从而使CopyResource()CopySubresourceRegion()等工具正常工作。您可以更新部件或整个缓冲区等。它具有一些性能影响,但这是预期的,因为动态本质上比静态初始化常量缓冲区更昂贵。我会躲过太多的技术性工作,并给你留下调查的乐趣。

关于开场问题的长答案

Yarrr,我爽朗,新数据。还有一大堆朗姆酒。

我会尝试提供有关其背后原因的更多信息,但仍然保持阅读的乐趣。包括维基百科在内的各种网站都吸引了与计算机科学相关的所有内容。关键点是给程序员直观的理解,为什么让他分析如何回应这个原因。就像一个简单的数学概念,就像一个全等的平行,对于初学者来说听起来最神秘,每单位时间会产生大量的咒骂词。正如我已经向我证明了一百万次,对动机的理解对于正确理解还有很长的路要走。

内存泄漏问题是由于您没有正确管理内存这一事实,正如其他人迄今为止所说的那样。通常,一个很好的指标是否你不必担心内存(当经典 C / C ++指标不像new,delete,malloc等那样运行时)就是你有一个指针来自的任何Microsoft (通常是COM接口,用于调解您与底层COM组件实例之间的协商,或者,如果您将,COM对象)。这还包括您不完全了解的其他API,这些API是您不了解的人开发的。但是,让我们首先谈谈COM和其中一个怪癖 - 内存管理。它实际上非常简单,如果你采用特殊的创建/初始化方法,它与经典C ++对象的区别并不大。

虽然缓冲区本身没有手动分配(手头的情况),但至少在经典 C / C ++方式中,任何在COM上运行的东西兼容的接口(挂起,我会回到它)并使你的初始空指针工作很可能依赖你调用Release()函数当你完成了。

Release()是我们都学会珍惜和爱护的delete关键字的直接翻译。所以,是的,确保你通过查询各种创建函数(构造函数的等价物)来管理你获得的COM对象的Microsoft指令,这些指令将填充你的接口指针,地址为实际初始化的COM组件实例或对象。为了将来参考和满足好奇心,每个COM接口都派生自IUnknown,它给它Release()(并要求它覆盖以考虑继承组件的新依赖关系)和其他功能,如引用计数。 / p>

为什么要对COM大惊小怪?

许多Microsoft API依赖于/基于COM(组件对象模型),它是实现通常的接口驱动代码的方式,纯C ++中的代码是通过抽象类完成的,而快乐的时候是用vtable完成的和继承,它推动了多态性的概念。但是,你必须好奇为什么不使用C ++?你看,DirectX,出生时已经很老了(想想1994./1995。),在COM首次推出的时候,是未来重复COM基本思想的自然选择。

最初,COM的开发是为了协助跨工具合作(Microsoft Office套件),或者更专业地进行进程间通信。在那个篮子里,你也可以删除OLE。从那时起,微软的软件工程师看到了进一步扩展这一点的潜力。推理认为能够以COM组件的形式创建各种资源,这些组件将响应可通过COM组件首次解决问题的语言访问的标准化接口。这样,微软可以将他们的大部分工作封装在一个可扩展的形式因子中,为它分配各种唯一标识符(__uuidof(),你已经遇到它,这是COM 如何注册/跟踪所有类),制作一个跨语言的解决方案,使得在其他地方编译的东西能够遵守COM规则,以完全不同的语言工作。

它实际上已经在其基础上发展,提供跨进程合作,反之亦然。它还具有各种问题,这些问题与OS开发和共享功能(如可重用交互,开放文件对话是一个着名的示例)中出现的各种开发问题有关。当然,DirectX(以及示例,ActiveX)非常适合这张图片。它们为操作系统提供了有关高交互性软件(如游戏和模拟)的通用解决方案。

这就是为什么你会看到&#34;我&#34; ID3D10Device中的前缀代表接口,特别是用于解决实际实现的COM组件的前缀。您通过统一的函数调用连接COM组件的实例,该调用需要GUID标识,指向您的接口指针的指针,并根据您给出的描述返回工作D3D10设备。这就是为什么你还提供实际指针作为LPVOID(或者基本上说这里是字节,做你必须做的)。如果您的使用是正确的,底层实现将为您正确解决它并使您的接口指针正常工作。基本上,用于获取正常运行的COM实例的查询使得引用使用AddRef()并使用Release()函数向下。类似于保留释放的概念。

所有这些组件通常都会被塞进一堆已经被“渲染”的DLL中。正确版本化没有问题。你得到了一个不错的API实现,事实上,这并不是那么糟糕。它真的很漂亮。

&#34; .NET与COM之间的区别&#34;

今天,它的一些主要功能可能让那些一瞥.NET的人感到困惑,但应该指出的是COM是一个前身(并且由此,几乎只是一个时间的前身,它们分享的惊人的少)到.NET(自2000年起在这里)。还应该注意的是,COM今天的功能集是有点偶然,而.NET框架是有意开发的,为各种开发人员提供跨语言标准平台为Windows工作。 COM最初实施后的潜力被看到了。 .NET是由潜力和需求驱动的。

这样的决定对于作为Windows广泛使用的平台来说是合乎逻辑的,你有很多精通不同语言的开发人员。通过制作符合CLS的组件,您可以在许多不同的.NET语言中享受许多编译的解决方案 - 缩短开发时间并实际上有益于性能。例如,C#(.NET语言)中的一些数学函数解析为实际用C / C ++编写的外部函数,这些函数更快(pow()就是这个例子)。

更重要的是,.NET不仅 outruns COM(如果他们必须&#34;竞争&#34;,它几乎毫无意义),但它们在根本上是不同的他们做事的方式。这就是为什么微软已经采用各种方法将COM连接到.NET,将其包装起来而不破坏.NET框架的核心架构。

如果你不知道它,简化为半熟练,这完全是关于COM主题的书,这只是一个小小的介绍。使用它需要更简单,直观的理解。希望它有助于编码。并尝试不在渲染功能中创建内容,特别是如果您负责其内存管理。

答案 1 :(得分:2)

每次成功调用ID3D10Device :: CreateBuffer都需要调用Release()

答案 2 :(得分:1)

正如Tom已经说过你需要在创建缓冲区之后释放缓冲区。 但是我认为在渲染函数中创建缓冲区并不是一件好事。 这意味着您每次都在反复创建缓冲区。这不仅会泄漏内存,而且非常慢,不需要。

所以只需创建一个创建缓冲区的初始化函数。还可以创建一个函数,在不再需要缓冲区时(在场景结束时)释放缓冲区。然后render函数应该只包含对缓冲区绘图的调用。

希望这能帮到你。

祝你好运!