Sign in to follow this  
TheUnbeliever

Does Managed DirectX keep track of memory?

Recommended Posts

At the moment, I'm playing around a little with MDX in C#, building a little demo application bit-by-bit to get a grip of the basics before moving off MDX (which is now no longer under development) to XNA or 'normal' DirectX. However, I've discovered that I very, very rapidly start running into D3DERR_OUTOFVIDEOMEMORY exceptions unless I manually dispose of (for example) a Direct3D.Font object used to print a framerate. Also, if I don't manually dispose of the Device object when the application exits, it appears that I gradually build up to these errors. While I'm glad to have worked out where I was going wrong, I'm confused because I assumed that managed DirectX would take care of memory (actually, that was one of the reasons I started off with it, so I didn't have so much overhead to deal with in terms of the programming).

Share this post


Link to post
Share on other sites
Well, if you simply let objects float off into the void they will get collected eventually by the GC and their relevant unmanaged objects will be released, so in that sense, yeah, it will keep track of memory.

Problem is, the GC only knows about their miniscule memory consumption on the managed heap, not their unmanaged system heap and GPU memory consumption. So it sees a bunch of microscopic objects lying around and there's really no reason to collect them.

The long and short of it is that when it comes to managing memory and objects, MDX is the same as unmanaged DX -- you need to destroy everything you create manually.

Share this post


Link to post
Share on other sites
Yes, Managed Direct X keeps track of memory. Actually C# keeps track of memory, MDX has very little to do with it. That said, with the way GC works, you can still run out of memory.

As a rule of thumb, if you are using anything in a loop, or frequently called event, either wrap it in a using block or call Dispose() when you are done with it. Since it was so recently allocated, the GC wont know that it should be disposed unless you tell it, so for example if you do something like

for(int i =0; i < 100000; i++)
{
Bitmap myBitmap = new Bitmap(somefile);
}

You will run out of memory. DrDobbs has a pretty good article on how .net GC works. But, as a rule of thumb, if your done with it, and it implements IDisposable, Dispose() it when you are done.

Share this post


Link to post
Share on other sites
Thanks both. I'm not quite certain how the GC works, so I think I'll do some reading (starting with that DDJ article Serapth mentioned).

A further question - is there any way of clearing the error (i.e. freeing the video memory that remains allocated even after the application has terminated) other than simply rebooting?

Share this post


Link to post
Share on other sites
Quote:
Original post by TheUnbeliever

A further question - is there any way of clearing the error (i.e. freeing the video memory that remains allocated even after the application has terminated) other than simply rebooting?


Wow.. ok... that shouldnt be happening. When your application ends, all the managed memory it allocated will be purged and available again. Are you positive your app isnt still running in the background after you close it ( aka, does it show up in task manager? ).

Share this post


Link to post
Share on other sites
That was my understanding, I just assumed something different was happening with video memory.

Here's a slightly more detailed account:

I've got an application that, at the moment, does nothing more than create a window, initialise a device, and draw the framerate to it (using a Microsoft.DirectX.Direct3D.Font object and its DrawFont method). The framerate reads about 60 fps or so (which seems a little slow - I've got v-sync turned on, but I'm running it windowed and at 1280x1024@32bit & 75Hz). I can post the source if necessary.

After a variable length of time (longer the first time, and shorter thereafter, becoming shorter until it happens immediately), the text characters become replaced with white rectangles - although they still move, showing that the text is changing.

Now, my app doesn't pick up (despite a fair attempt at trying to keep the code clean and put in proper exception handling) the D3DERR_OUTOFVIDEOMEMORY exception. However, running absolutely any of the sample programs from the DirectX SDK does result in that error when they try to create a device.

All applications close correctly, in terms of what I can see in that they disappear from the Applications and Processes tabs of Task Manager.

Specs, in case they come into it:

Windows XP Professional SP2
Athlon XP 2800+ (Barton core, so not overclocked or anything like that)
512MB DDR SDRAM
Radeon 9600SE 128MB

Share this post


Link to post
Share on other sites
Nope, it's not at all.

using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Direct3D = Microsoft.DirectX.Direct3D;

namespace renderer
{
public class frmMain : Form
{
FrameCounter framecounterFPS = new FrameCounter();
Device deviceRendering = null;
PresentParameters parametersPresent = null;

Direct3D.Font fontFrames = null;

// Single threaded COM model
[STAThread]
public static void Main()
{
// Attempt to initalize graphics and show form
using (frmMain formMain = new frmMain())
{
if (!formMain.InitialiseGraphics())
{
MessageBox.Show("Initialisation of Direct3D failed. This application will now terminate.", "Initialisation failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
formMain.Show();

while (formMain.Created)
{
formMain.Render();
Application.DoEvents();
}

formMain.deviceRendering.Dispose();
formMain.fontFrames.Dispose();
}
}

public frmMain()
{
this.ClientSize = new Size(640, 480);
this.Text = "ALRenderer";
}

public bool InitialiseGraphics()
{
try
{
parametersPresent = new PresentParameters();
parametersPresent.Windowed = true;
parametersPresent.SwapEffect = SwapEffect.Discard;
parametersPresent.EnableAutoDepthStencil = true;
parametersPresent.AutoDepthStencilFormat = DepthFormat.D16;

deviceRendering = new Device(
0,
DeviceType.Hardware,
this,
CreateFlags.HardwareVertexProcessing,
parametersPresent);
deviceRendering.DeviceReset += new EventHandler(deviceRendering_DeviceReset);
deviceRendering_DeviceReset(deviceRendering, null);

return true;
}
catch (DirectXException)
{
int errorcodeLastError = DirectXException.LastError;
string errorstringLastError = DirectXException.GetDirectXErrorString(errorcodeLastError);

MessageBox.Show(
"DirectX exception " +
errorcodeLastError.ToString() +
" occurred while initialising Direct3D device.\n\nThe error string for this error code is:\n" +
errorstringLastError, "Device initialisation failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}

private void deviceRendering_DeviceReset(object sender, EventArgs e)
{
Device deviceLocal = (Device)sender;
deviceLocal.RenderState.ZBufferEnable = true;
deviceLocal.RenderState.Ambient = Color.WhiteSmoke;
}

protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Render();
}

public void Render()
{
try
{
if (deviceRendering == null)
{
throw new DirectXException("Rendering device is reference type and null.");
}

deviceRendering.Clear(
ClearFlags.Target | ClearFlags.ZBuffer,
Color.Black,
1.0f,
0);

// Setup view and projection matrices
SetupMatrices();

deviceRendering.BeginScene();
/*--- BEGIN RENDERING OBJECTS ---*/
PrintFramerate();

/*--- ENDETH OBJECT RENDERING ---*/
deviceRendering.EndScene();

// Present next backbuffer to display
deviceRendering.Present();

framecounterFPS.FrameDrawn();
}
catch (DirectXException)
{
int errorcodeLastError = DirectXException.LastError;
string errorstringLastError = DirectXException.GetDirectXErrorString(errorcodeLastError);

MessageBox.Show(
"DirectX exception " +
errorcodeLastError.ToString() +
" occurred while rendering.\n\nThe error string for this error code is:\n" +
errorstringLastError, "Device initialisation failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}

private void SetupMatrices()
{
deviceRendering.Transform.View = Matrix.LookAtLH(
new Vector3(0, 3.0f, -5.0f),
new Vector3(0, 3.0f, 0),
new Vector3(0, 1.0f, 0));

deviceRendering.Transform.Projection = Matrix.OrthoLH(
16,
12,
1.0f,
100.0f);
}

private void PrintFramerate()
{
if (fontFrames == null)
{
fontFrames = new Direct3D.Font(
deviceRendering,
new System.Drawing.Font(
new FontFamily(System.Drawing.Text.GenericFontFamilies.Monospace),
12));
}

fontFrames.DrawText(
null,
framecounterFPS.GetFrameRate() + " fps",
new Point(0, 0),
Color.White);
}
}

public class FrameCounter
{
protected long NumberOfFrames = 0;
protected int tickcountFirstFrame = 0;
protected int tickcountLastFrame = 0;
protected int tickcountThisFrame = 0;
protected double FrameRate = 0.0f;

public double FrameDrawn()
{
tickcountLastFrame = tickcountThisFrame;
tickcountThisFrame = Environment.TickCount;
NumberOfFrames++;

if (tickcountLastFrame == 0)
{
tickcountFirstFrame = tickcountThisFrame;
}

return CalculateFrameRate();
}

protected double CalculateFrameRate()
{
FrameRate = 1000.0f / (tickcountThisFrame - tickcountLastFrame);

return FrameRate;
}

public double GetFrameRate()
{
return FrameRate;
}

public long GetNumberFramesDrawn()
{
return NumberOfFrames;
}

public int GetTimeSinceFirstFrame()
{
return tickcountFirstFrame;
}
}
}

Share this post


Link to post
Share on other sites
Is this the version of your code that is causing the D3DERR_OUTOFVIDEOMEMORY error?

If so, i've been running it about 25 minutes now without incident. System memory is staying pretty much steady and I havent received any errors.

If this is the version causing you problems, it may not be a code related problem. Quick glance over your code and I dont see any major problems, but ive mostly been waiting for my computer to error out.

Share this post


Link to post
Share on other sites
A minor addition: as can be seen from the source, it starts off in windowed mode at 640x480. If I then maximise the window, then the try..catch in the render function catches DirectX exception 127 - with the error string being "Unknown". This also brings up an "Unhandled exception" dialog box, with the following being available as 'Details' (to summarise the important part - it says that the unhandled exception is the D3DERR_OUTOFVIDEOMEMORY error).

See the end of this message for details on invoking 
just-in-time (JIT) debugging instead of this dialog box.

************** Exception Text **************
Error in the application.
-2005532292 (D3DERR_OUTOFVIDEOMEMORY)
at Microsoft.DirectX.Direct3D.Device.Reset(PresentParameters[] presentationParameters)
at Microsoft.DirectX.Direct3D.Device.OnParentResized(Object sender, EventArgs e)
at System.Windows.Forms.Control.OnResize(EventArgs e)
at System.Windows.Forms.Form.OnResize(EventArgs e)
at System.Windows.Forms.Control.OnSizeChanged(EventArgs e)
at System.Windows.Forms.Control.UpdateBounds(Int32 x, Int32 y, Int32 width, Int32 height, Int32 clientWidth, Int32 clientHeight)
at System.Windows.Forms.Control.UpdateBounds()
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
at System.Windows.Forms.ContainerControl.WndProc(Message& m)
at System.Windows.Forms.Form.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)


************** Loaded Assemblies **************
mscorlib
Assembly Version: 2.0.0.0
Win32 Version: 2.0.50727.42 (RTM.050727-4200)
CodeBase: file:///J:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/mscorlib.dll
----------------------------------------
Renderer
Assembly Version: 0.0.0.0
Win32 Version: 0.0.0.0
CodeBase: file:///J:/Documents%20and%20Settings/Administrator/My%20Documents/Visual%20Studio%202005/Projects/Renderer/Renderer/bin/Release/Renderer.exe
----------------------------------------
System.Windows.Forms
Assembly Version: 2.0.0.0
Win32 Version: 2.0.50727.42 (RTM.050727-4200)
CodeBase: file:///J:/WINDOWS/assembly/GAC_MSIL/System.Windows.Forms/2.0.0.0__b77a5c561934e089/System.Windows.Forms.dll
----------------------------------------
System
Assembly Version: 2.0.0.0
Win32 Version: 2.0.50727.42 (RTM.050727-4200)
CodeBase: file:///J:/WINDOWS/assembly/GAC_MSIL/System/2.0.0.0__b77a5c561934e089/System.dll
----------------------------------------
System.Drawing
Assembly Version: 2.0.0.0
Win32 Version: 2.0.50727.42 (RTM.050727-4200)
CodeBase: file:///J:/WINDOWS/assembly/GAC_MSIL/System.Drawing/2.0.0.0__b03f5f7f11d50a3a/System.Drawing.dll
----------------------------------------
Microsoft.DirectX.Direct3D
Assembly Version: 1.0.2902.0
Win32 Version: 9.05.132.0000
CodeBase: file:///J:/WINDOWS/assembly/GAC/Microsoft.DirectX.Direct3D/1.0.2902.0__31bf3856ad364e35/Microsoft.DirectX.Direct3D.dll
----------------------------------------
Microsoft.DirectX.Direct3DX
Assembly Version: 1.0.2902.0
Win32 Version: 5.04.00.3900
CodeBase: file:///J:/WINDOWS/assembly/GAC/Microsoft.DirectX.Direct3DX/1.0.2902.0__31bf3856ad364e35/Microsoft.DirectX.Direct3DX.dll
----------------------------------------
Microsoft.DirectX
Assembly Version: 1.0.2902.0
Win32 Version: 5.04.00.2904
CodeBase: file:///J:/WINDOWS/assembly/GAC/Microsoft.DirectX/1.0.2902.0__31bf3856ad364e35/Microsoft.DirectX.dll
----------------------------------------

************** JIT Debugging **************
To enable just-in-time (JIT) debugging, the .config file for this
application or computer (machine.config) must have the
jitDebugging value set in the system.windows.forms section.
The application must also be compiled with debugging
enabled.

For example:

<configuration>
<system.windows.forms jitDebugging="true" />
</configuration>

When JIT debugging is enabled, any unhandled exception
will be sent to the JIT debugger registered on the computer
rather than be handled by this dialog box.


(Placed in source tags so that it can be scrolled)

If I then click OK on that first exception that was caught by my app, I then get a whole series of exceptions trapped - the vast majority are DirectX exception 0, with the error string being "S_OK" (S'Ok? Nope... S'not!), with a few exception 5 ("ERROR_ACCESS_DENIED").

Share this post


Link to post
Share on other sites
Quote:
Original post by Serapth
Is this the version of your code that is causing the D3DERR_OUTOFVIDEOMEMORY error?

If so, i've been running it about 25 minutes now without incident. System memory is staying pretty much steady and I havent received any errors.

If this is the version causing you problems, it may not be a code related problem. Quick glance over your code and I dont see any major problems, but ive mostly been waiting for my computer to error out.


Thanks for the time taken. :)

Hmm, I've got another couple computers here, I'll try shoving it on them and report back.

Share this post


Link to post
Share on other sites
Well it appears to work without anything of note happening. However, I did make one change to that program before posting, that I had tested here, but hadn't rebooted for (so the error was still there before rebooting - and so perhaps not 'its fault'). I'll reboot this computer and give it a check, although I rather hope it'll work fine.

The only change was that I was creating (and calling Dispose() on) fontFrames in the PrintFrames() function - horrifically inefficient in any case. I think this might have caused the problem, although I still don't understand why the problem persists after the application has been closed.

EDIT: Yep, now works without fault on my computer. Thanks a whole lot for the help, Serapth! :D

Share this post


Link to post
Share on other sites
No worries.

Isnt it fun encountering a bug or glitch when you are learning something new. Without fail you always assume you're doing something wrong. Can spend hours on stupid problems like that.

Might want to try downloading new drivers. Also, check your DX dll versions between the computers that work and those that dont.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this