Does Managed DirectX keep track of memory?

Started by
11 comments, last by Serapth 17 years, 6 months ago
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).
[TheUnbeliever]
Advertisement
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.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
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.
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?
[TheUnbeliever]
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? ).
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
[TheUnbeliever]
Can you post the source? IT doesnt sound that large.
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;        }    }}
[TheUnbeliever]
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.
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 thisapplication or computer (machine.config) must have thejitDebugging value set in the system.windows.forms section.The application must also be compiled with debuggingenabled.For example:<configuration>    <system.windows.forms jitDebugging="true" /></configuration>When JIT debugging is enabled, any unhandled exceptionwill be sent to the JIT debugger registered on the computerrather 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").
[TheUnbeliever]

This topic is closed to new replies.

Advertisement