2

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

小应用程序的内存使用量飙升,不断增加。_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
}

我不确定在每一帧上释放或删除什么会阻止这种情况。

4

3 回答 3

4

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

您可以将缓冲区使用情况从D3D10_USAGE_DEFAULT(仅限 GPU 读/写)更改D3D10_USAGE_DYNAMIC为启用CopyResource()CopySubresourceRegion()工作之类的设施。您可以更新部分或整个缓冲区等。它具有一些性能影响,但这是可以预料的,因为动态本质上比静态初始化常量缓冲区更昂贵。我会回避过多的技术细节,把调查的乐趣留给你。

开场问题的长答案

亚尔,我很高兴,新数据。还有一桶朗姆酒。

我将尝试提供更多关于如何背后的原因的信息,但仍然保持阅读的乐趣。包括维基百科在内的各种网站都从与计算机科学相关的一切事物中汲取乐趣。关键是要给程序员直观的理解,why,让他分析why如何响应那个why。就像数学中的一个简单概念,就像一个全等的平行线,对于初学者来说充其量听起来很神秘,每单位时间会产生大量的脏话。正如我一百万次证明的那样,对动机的理解对正确理解大有帮助。

内存泄漏问题是由于您没有像其他人所说的那样正确管理内存。通常,您是否需要担心内存(当经典的C/C++ 指标不像 new、delete、malloc 等那样到处乱跑时)的一个很好的指标是,如果您有一个指向任何 Microsoft来源的指针(通常是 COM 接口)它调解您与底层 COM 组件实例或 COM 对象之间的协商)。这还包括由您不认识的人开发的您不完全理解的其他 API。但是,让我们先谈谈 COM 和其中一个怪癖——内存管理。它实际上非常简单,与经典的 C++ 对象并没有太大区别,

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

Release()是我们都学会珍惜和喜爱的关键字的直接翻译。delete所以,是的,请确保您遵循 Microsoft 的指令,通过查询各种创建函数(等效于构造函数)来管理您获得的 COM 对象,这些函数将使用指向实际初始化的 COM 组件实例或对象的地址填充您的接口指针。为了将来的参考和满足好奇心,每个 COM 接口都派生自IUnknownRelease()(并要求重写它以说明继承组件的新依赖关系)和其他功能,如引用计数。

为什么要对 COM 大惊小怪?

许多 Microsoft API 都依赖/基于 COM(组件对象模型),这是他们实现通常的接口驱动代码的方式,在纯 C++ 中,这些代码由抽象类完成,并通过 vtables 和继承来驱动概念多态性。但是,你一定很好奇为什么干脆不使用 C++?你看,DirectX,它诞生时已经很老了(想想 1994./1995.),大约在 COM 首次推出的时候,它是未来重申基本 COM 思想的自然选择。

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

它实际上已经在其基础上发展起来,以提供跨流程合作,反之亦然。它还具有与操作系统开发和共享功能(如可重用交互,打开文件对话是一个著名的例子)相关的各种开发问题相关的各种问题。自然地,DirectX(以及,例如,ActiveX)完美地融入了这张图片。它们为操作系统提供有关游戏和模拟等高度交互软件的通用解决方案。

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

所有这些组件通常都被塞进一堆 DLL 中,这些 DLL 已被“渲染”以进行正确的版本控制而不会出现问题。你会得到一个不错的 API 实现,说实话,这还不错。它实际上有点漂亮。

.NET 和 COM 之间的“区别”

今天,它的一些主要特性可能会让那些了解 .NET 的人感到困惑,但应该注意的是,COM 是 .NET 的前身(因此,几乎只是暂时的前身,它们共享的很少) (自 2000 年以来就在这里。)。还应该注意的是,COM 的今天的功能集有些意外,而 .NET 框架的开发是为了为各种使用 Windows 的开发人员提供跨语言标准平台。COM 的潜力是在最初实施之后才显现出来的。.NET 是由潜力和需求驱动的。

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

更重要的是,.NET 不仅超越了COM(如果它们必须“竞争”,那几乎是毫无意义的),而且它们在做事方式上也有着根本的不同。这就是为什么微软采用各种方法将 COM 与 .NET 连接起来,将其包裹起来而不破坏 .NET 框架的核心架构。

如果您不知道,这只是一个简单的介绍,经过简化使其变得半有趣,整本书都是关于 COM 主题的。使用它需要更简单、直观的理解。希望它有所帮助并快乐编码。并且尽量不要在渲染函数中创建东西,特别是如果你负责它的内存管理。

于 2012-04-08T04:43:20.693 回答
2

对 ID3D10Device::CreateBuffer 的每次成功调用都需要与对 Release() 的调用相匹配

于 2012-04-06T14:42:51.187 回答
1

正如汤姆已经说过的那样,您需要在创建缓冲区后释放它。但是,我认为在渲染函数中创建缓冲区并不是一件好事。这意味着您一次又一次地创建缓冲区。这不仅会泄漏内存,而且非常慢且不需要。

所以只需创建一个创建缓冲区的初始化函数。还创建一个函数,当您不再需要它时(在场景结束时左右)释放缓冲区。那么渲染函数应该只包含对缓冲区绘图的调用。

希望这对您有所帮助。

祝你好运!

于 2012-04-06T14:56:54.037 回答