The downside of using the events


Managed DirectX has plenty of events that it captures, and fires in a normal application.  Every single
managed graphics object will hook certain events on the device to ensure it can behave
correctly. 

For example, when a device is reset (due to a window resize, or a switch to or from
full screen), any object stored in the default memory pool will need to be disposed. 
Many objects will also have something to do after the device has been reset. 
In the default case, each object that will need to be disposed before a reset will
hook the DeviceLost event, while the items who also have post reset work will hook
the DeviceReset event.

This doesn’t even consider the fact that each object hooks the device’s dispose event. 
In short, if events are being hooked, every d3d object will have a hook on the device. 
So why is this a problem?

Take this seemingly ‘safe’ code an example (assume swapChain is a valid Swap Chain,
and device is a valid Direct3D device):

device.SetRenderTarget(0, swapChain.GetBackBuffer(0, BackBufferType.Mono));
device.DrawPrimitives(PrimitiveType.TriangleList, 0, 500);

Looks simple enough, just using the back buffer of the swap chain as the render target. 
However, if the events are being hooked, there are a lot more things going on here. 
The GetBackBuffer call will return a new surface representing the current back buffer
of the swap chain.  This object will hook the device lost and disposing events,
which will be at least 3 allocations (the actual surface, along with the 2 event handlers).

Worst than that though, this object (which is only used for a brief period of time)
will never be collected as long as the device is still alive since it has hooked events
on the device, so this memory (and these objects) will never be reclaimed.  They
will eventually get promoted to generation 2, and memory usage on your application
will just steadily rise.  Imagine a game running at 60 frames per second, each
frame calling this code..

To think, we haven’t hit the end of the problems yet either!  Imagine the game
running @ 60 frames per second that has been running for 2 minutes.  Now, the
devices dispose event has 7200 objects hooked, and the dispose method has just been
caused since the application is shutting down.  It takes a significant amount
of time to propogate this event, and it will appear your application has locked up
(when in reality it is simply notifying every object the device is now gone).

A much more efficient way of writing this code would be something like:

using (Surface buffer = swapChain.GetBackBuffer(0, BackBufferType.Mono))
{
    device.SetRenderTarget(0, buffer);
    device.DrawPrimitives(PrimitiveType.TriangleList, 0, 500);
}
In this scenario you get rid of the objects immediately.  Yet you still have
the underlying ‘problem’ of the event hooking.

An even better solution would be to turn off the event hooking within d3d completely. 
There is a static property on the device you can use to do this, such as:

Device.IsUsingEventHandlers = false;

If you do this before you create your device, this will completely turn off the internal
event handling for d3d.  Beware of doing this though, since you will need to
manage the object lifetimes yourself.

The default behavior of device hooks is extremely convient, but if you want top performance,
you may want to avoid the default event code.  At the very least understand when
and how the events are hooked and structure your code to be as fast as possible (such
as the 2nd code snippet over the first).

Comments (7)

  1. This is a screenshot of my first managed DX/C# app.  It’s showing a scene made up of a post-process corona effect and a gooch style shader – 2 of my favourite effects from the selection we ship with FX Composer….

  2. This is a screenshot of my first managed DX/C# app.  It’s showing a scene made up of a post-process corona effect and a gooch style shader – 2 of my favourite effects from the selection we ship with FX Composer….