Reputation: 5894
I have a simple rendering process that sends a set of vertices to a geometry shader and renders sprites from that information.
The memory usage of the little app goes through the roof, continuously increasing. I ran a quick test with _CrtDumpMemoryLeaks()
and Visual Leak Detector, and they both claim no leaks.
I have a device and a container full of vertex information:
ID3D10Device* pD3DDevice;
std::vector<SpriteVertex>* sprites;
And then in my RenderSprites()
method I've commented out pretty much everything until the leak stops (as does the rendering ;)
The point where things are going awry is here:
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
}
I'm not sure what to release or delete on each frame that will stop this.
Upvotes: 2
Views: 1743
Reputation:
As other answers have postulated the solution in a quick manner and comments are a bit too short - here goes - another quickie. You asked what else is needed to update the created buffers at runtime:
You could change the buffer usage from D3D10_USAGE_DEFAULT
(GPU read/write only) to D3D10_USAGE_DYNAMIC
which enables facilities like CopyResource()
and CopySubresourceRegion()
to work. You can update parts or the whole buffer, etc. It has some performance implications, but that is to be expected since dynamic is inherently more expensive than static on-initialization-constant buffers. I'll evade spewing too much technicalities, and leaving the pleasure of investigation to you.
I'll try to give more information on the why behind the how, but still keep it fun to read. Various websites, including Wikipedia, suck the fun out of everything computer science related. The key point is to give the programmer intuitive understanding, the why and let him analyze the how in response to that why. Just like a simple concept from mathematics, like a congruent paralletope, will sound cryptic at best to a beginner, yielding a severe amount of swear words per unit time. As it has proven with me a million times, an understanding of motivation goes a long way towards proper understanding.
The memory leak problem is caused by the fact you're not managing memory properly as others have said thus far. Usually, a good indicator whether or not you have to worry about memory (when classic C/C++ indicators don't run around like new, delete, malloc etc.) is if you have a pointer to anything Microsoft in origin (usually COM interfaces which mediate negotiations between you and underlying COM component instances or, if you will, COM objects). That also includes other APIs you don't fully understand developed by people you don't know. But, let's talk about COM and one of the quirks first - memory management. It's really simple actually, doesn't really differentiate much from classic C++ objects, if you take out the special creation/initialization approach.
While there is no manual allocation per se of a buffer (the case at hand), in the classic C/C++ manner at least, anything that operates on COM-compliant interfaces (hang on, I'll get back to it) and makes your initial null-pointers to these interfaces work is very likely relying on you to call the Release()
function when you're done.
Release()
is a direct translation to the delete
keyword we all learned to cherish and love. So, yeah, make sure you follow Microsoft's directives on managing their COM objects you acquire by querying various creation functions (equivalents of constructors) which will fill your interface pointers with addresses to actual initialized COM component instances or objects. For future reference and satisfaction of curiosity, every COM interface derives from IUnknown
which gives it the Release()
(and requires it overriden to account for new dependencies of the inheriting component) and other functionality like reference counting.
A lot of Microsoft APIs are dependent/based on COM (Component Object Model), it's their way of implementing the usual interface-driven code which in pure C++ is done by abstract classes and happy fun time with vtables and inheritance which drive the notion of polymorphism. But, you must be curious why simply not use C++? You see, DirectX, which is quite old by birth ( think 1994./1995.), around the time COM first launched, was a natural choice for future reiterations of the basic COM ideas.
Initially, COM was developed to assist cross-tool cooperation (Microsoft Office suite) or, more professionally, inter-process communication. In that basket, you can also drop OLE. From that, Microsoft's software engineers saw potential to extend this a bit more. The reasoning argued to enable creation of various resources in a form of COM components which would respond to a standardized interface accessible through a language in which a COM component makes its debut to solve problems. That way, Microsoft could encapsulate a lot of their work in a scalable formfactor, assign it various unique identifiers (__uuidof(), you've come across it, that's how COM registers/tracks all the classes), make a cross-language solution which would enable stuff compiled somewhere else abiding by the COM rules to work in entirely different languages.
It has actually grown on its basis to provide cross-process cooperation and accessing vice-versa. It also features various problems which relate to all sorts of development problems that arise with OS development and shared functionality (like reusable interactions, the open file dialogue is a famous example). Naturally, DirectX (and, example, ActiveX) fitted perfectly into this picture. They offer common solutions for the OS regarding highly-interactive software like games and simulations.
That's why you see the "I" prefix in ID3D10Device
which stands for Interface, specifically one for a COM component which addresses the actual implementation. You connect an instance of the COM component via a unified function call which requires GUID identification, a pointer to your interface pointer and get back a working D3D10 device based on the description you gave it. That's why you also provide the actual pointer as an LPVOID (or basically saying here are the bytes, do what you have to). If your usage is correct, underlying implementation will resolve it properly for you and make your interface pointer functional. Basically, a query to get a functioning COM instance makes the reference go up with the AddRef()
and down with the Release()
function. Similar to a notion of retain-release.
And all of those components usually get shoved into a bunch of DLLs which have been "rendered" for proper versioning without problems. And you get a decent API implementation which, truth be told, isn't that bad. It's actually kind of pretty.
Today, some of its primary features might be confusing to people who are glimpse-aware of .NET, but it should be noted that COM is a predecessor (and by that, almost just a temporal predecessor, they share surprisingly little) to .NET (which is here since 2000.). It should also be noted that COM's today's feature set is somewhat accidental while the .NET framework has been intentionally developed to offer a cross-language standard platform for all sorts of developers working for Windows. COM's potential was seen after it had initially been implemented. .NET was driven by the potential and the need.
Such a decision was logical for a platform widespread as Windows, you have a lot of developers who are versed in different languages. By making CLS-compliant components, you can enjoy many solutions compiled in many different .NET languages - shortening development time and actually benefiting performance wise. For example, some math functions in C# ( a .NET language ) resolve to external functions which were actually written in C/C++ which is faster (pow() is an example of this).
More to the point, .NET not only outruns COM (if they have to "compete", it's almost pointless), but they are fundamentally different in the way they do things. That's why Microsoft has made various ways to connect COM with .NET, wrapping it around without damaging the core architecture of the .NET framework.
This is simply a little introduction if you're unaware of it, simplified to make it semi-enjoyable, whole books have been written on the topic of COM. Usage of it requires a simpler, intuitive understanding. Hope it helps and happy coding. And try not to create stuff in the render function, especially if you're responsible for its memory management.
Upvotes: 4
Reputation: 1325
As Tom already said you need to release your buffer after creating it. Howhever I don't think it is a good thing to create your buffer inside a render function. This means that your are creating a buffer everytime again and again. This is not only leaking memory but also very slow and not required.
So just create a initialization function which creates the buffers. Also create a function which releases the buffer when you don't need it anymore (at the end of your scene or so). Then the render function should just contain the calls to the drawing of the buffer.
Hope this helps you on the way.
Good luck!
Upvotes: 1
Reputation: 541
Each successful call to ID3D10Device::CreateBuffer needs to be matched with a call to Release()
Upvotes: 2