Stream Leaks in SharpDX

Started by
18 comments, last by cephalo 8 years, 12 months ago

Note: I chose SlimDX for a topic prefix, but actually I'm using SharpDX (that wasn't an option in the list), but the code is virtually identical. I'm using C# in VS.NET, and no other API that would be relevant to these graphical issues.

Anyway, the problem I'm having is that when I run my program for a couple minutes, it crashes with an exception that says I have a memory leak. It's a very educated guess that it's being caused by these stupid DataStream objects, because there's really nothing else in the code that could possibly be causing it.

I need to be able to change the vertices within the stream at any time, so I tried setting the position to 0 at every frame and rewriting all the data, but that doesn't seem to work. So I tried disposing of the stream and recreating it and rewriting the data every frame, but I still get the memory leak.

How exactly am I supposed to use these streams and still be able to modify my data? Isn't there a better way to store the data as just an array or something and then use that instead of a stream, and change the values whenever I want? I NEED to be able to move vertices in any way I want at any time.

Unfortunately, I really can't provide source code, so please don't request it. But this is more of a conceptual issue, not specifically about code.

Advertisement

I'm using SlimDX targeting Dx9 (your mileage may vary), and an example of how I use the DataStream object is as follows:


            using (var dataStream = _indexBuffer.Lock(0, sizeInBytes, LockFlags.Discard))
            {
                dataStream.WriteRange(_indices);
                _indexBuffer.Unlock();
            }

My understanding of DataStreams is that they are temporary objects that are used to get data in and out of the D3D objects.

So with that in mind, I keep an in-memory copy of the raw data in an array (_indices in the above code), and when that is updated, I call the above code to commit those changes to the actual index buffer.

[size="2"]Currently working on an open world survival RPG - For info check out my Development blog:[size="2"] ByteWrangler

Well you seem to do it in a totally different way than me! I'm using it for storing triangles to display, and doing stuff something like this (I don't have the code in front of me so it's paraphrased from memory):

DataStream s = new DataStream(sizeof(float) * vertexCount);

s.Position = 0;

for(int i = 0; i < vertexCount; i++)

{

s.Write(new Vector3(x, y, z));

// More vectors if it's a more complicated vertex data format

}

s.Position = 0;

// Draw it here

s.Dispose();

I think this sumarizes it fairly well. I do the whole thing inside a loop so it happens every frame. The reason for the two s.Position = 0; lines is because it seems like I had to start at the beginning to write from there, and then again to draw starting with the first point. I guess my worry is that it isn't being properly disposed, so it keeps making more and more memory allocations until it overflows, but I don't know why that would be.

Honestly, I don't even know where the exception is being thrown, and if it's happening form inside these stream functions, I can't break there, so I'm not sure how to prove exactly where it's happening (do you know how?), but I assume that it must be because of the streams, since they're the only thing in my program that keeps allocating more memory. I can't run it for more than a few seconds at a time and it's really slowing down my progress of testing all other bugs!

Also, in your example, what data type is your _indexBuffer, and what exactly are you doing with it? You don't ever dispose of your stream or set the position?


s.Write(new Vector3(x, y, z));

This is likely the source of the problem. It appears you create (new) vectors without deleting them. You should be able to determine if that's the problem by using a fixed value (i.e., don't use new). It, of course, won't render properly but it will tell you if that's the source of the leak.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.


This is likely the source of the problem. It appears you create (new) vectors without deleting them

Vector3 is a value type, so new in this case is just allocating on the stack. It's not the source of the leak.


DataStream s = new DataStream(sizeof(float) * vertexCount);
s.Position = 0;
for(int i = 0; i < vertexCount; i++)
{
s.Write(new Vector3(x, y, z));
// More vectors if it's a more complicated vertex data format
}
s.Position = 0;
// Draw it here
s.Dispose();

Why are you not using "using" syntax? It may not be the source of your leak, but if any exceptions are thrown in there, the DataStream won't be Dispose'd. It should be written like this:

using(DataStream s = new DataStream(sizeof(float) * vertexCount))

{

// blah blah blah

}

Buckeye, that's a good point. phil_t, are you sure the stack isn't overflowing then?

I didn't know I was supposed to be using using. Using will set the object = null when it's done, right? But it won't actually call the Dispose method, will it, and isn't that necessary? I was just following an example I had seen, and I didn't realize people used the using keyword to do this particular thing.

Buckeye, that's a good point. phil_t, are you sure the stack isn't overflowing then?

Using new to create a stack-allocated variable in a loop like that cannot "overflow the stack." There's only space for one Vector3 allocated on the stack and it's constantly reused in each iteration of the loop. The use of "new" here isn't the problem (it's C#; you basically must use new to create new instances).
Using will set the object = null when it's done, right? But it won't actually call the Dispose method, will it, and isn't that necessary?

No, a using block will not assign the "used" variable null at the end of the scope. It will, however, ensure Dispose is called. It's basically syntactic sugar for a try/finally block. See the documentation for more.
As for your actual problem, what is the actual type of exception thrown and the actual text of the error message? Copy-and-paste it, don't retype it, as much as possible. If you suspect you have a memory leak it's a good idea to run the program through a memory profiler, such as the one built-in to Visual Studio or the excellent third-party one provided by Red Gate Software (the ANTS memory profiler; it has a free evaluation). Also:

Honestly, I don't even know where the exception is being thrown

Debug -> Exceptions, make sure "break on throw" is checked for managed exceptions.

and if it's happening form inside these stream functions, I can't break there,

Enable unmanaged debugging in your project options and you can set breakpoints (or get accurate break-on-throw) results inside the SharpDX code.

And finally:

Note: I chose SlimDX for a topic prefix, but actually I'm using SharpDX (that wasn't an option in the list), but the code is virtually identical. I'm using C# in VS.NET, and no other API that would be relevant to these graphical issues.
Topic prefixes are less important than titles; you should put "SharpDX" in the title. I'll edit it for you.

Well I can't copy and paste because it's on a different computer. But I know it's an OutOfMemoryException. Thanks for the hints on finding it though.

If you're not using the "using" syntax I believe you have to call Close() and then Dispose() on the Datastream to properly clean it up.

[size="2"]Currently working on an open world survival RPG - For info check out my Development blog:[size="2"] ByteWrangler

If you're not using the "using" syntax I believe you have to call Close() and then Dispose() on the Datastream to properly clean it up.

You don't need both. SharpDX's DataStream implements IDisposable correctly, so it's Dispose method -- where all the clean up logic is -- is sufficient on its own. It inherits from System.IO.Stream, which has a Close method that just calls Dispose. So one or the other is fine, but both is redundant (safe, but redundant).

Calling Close will cause the GC to suppress finalization of the DataStream (due to the explicit call to SuppressFinalize in Close), but that does nothing because DataStream doesn't have a finalizer.

Close is most vestigial, an artifact from the pre-IDisposable days.

This topic is closed to new replies.

Advertisement