ObjectTable Hacks in SlimDX

I simultaneously love and hate April Fool's day. On the one hand, a lot of the stuff that happens is absolutely hilarious. On the other hand, some people do really stupid or annoying things. It's also apparently become fashionable to start posting things a day early, which is bull. Go to hell, TechARP. Go. To. Hell.

ObjectTable Hacks in SlimDX

For the most part, SlimDX is an amalgation of disconnected parts. There are groups of related classes that work together, but outside those groups, the various pieces are completely unrelated. However, most of the library is built up on top of COM objects, which all require very similar behavior and treatment in order to work correctly. Unfortunately, COM objects don't really mesh well with the .NET model for object lifetimes, and DirectX objects in particular can be quite touchy about threading issues. I'm not going to discuss this problem in detail, because jpetrie already has. I'm assuming you've already read that post before continuing on this one.

What I want to look at is the actual implementation of the hashtable that jpetrie mentions, and some of the consequences of its existence. The design goal of the hashtable, called ObjectTable, was that we would be able to take any native pointer to a COM object and retrieve an already constructed managed object for it whenever available. That is very useful, because it means that we don't have to spawn distinct managed instances any more than strictly necessary. In order to accomplish that goal, it's necessary to keep a table of every SlimDX object that has been created, and to remove entries from that table whenever a SlimDX object is disposed. Insertion into and removal from the table also correlates 1:1 to ref count changes by SlimDX. And in the very few instances that SlimDX internally causes an add ref on an object that won't be given to the user, it always releases it before returning control back to the client.

As a result, we now have a definitive and reliable table of every live instance of a ComObject derived class. (Technically, SlimDX could already do that, but it was optional and not useful thanks to the massive duplication of objects.) Every native COM instance is guaranteed to have exactly one corresponding managed ComObject instance. In other words, you can determine, at any arbitrary point in time, exactly how many ComObjects are outstanding and what types they are. In addition, SlimDX gives you the option to tag objects with their call stack when they're created, and even give them a string name of your choice. Want to fix your leaks? Just go through the list (or use the handy utility function to dump it for you) and see where your lost objects are coming from.

Or -- and I'm dead serious here -- just iterate over the list, and dispose everything. In some cases this is a fantastic way of masking serious bugs in your resource management. But not everybody is doing full management/streaming of resources. A lot of times, you're just writing a demo that loads a whole bunch of things are startup and nukes them at shutdown. You can get even more clever than that. Want to unload one level completely before moving onto the next? Do the exact same thing, but skip disposing the Device (and possibly Surfaces if you're using render targets) as you go through. It's ridiculous and absurdly lazy, but it will also work perfectly.

I realized that this hack was possible during development and made sure to expose it. Then, we decided to take things a little farther. We were already recording some extra information in every object; why not extend that a bit more? So we added two more bits of metadata that are D3D9 specific. First, every ComObject now has an IsDefaultPool flag that SlimDX sets where appropriate. Second, a number of classes derive from a new interface called IResettable that provides OnLostDevice and OnResetDevice functions. Both of these are useful for device lost and reset situations, obviously.

That's where the madness ends, for now. I'm kicking around a few ideas for more interfaces to add in the next version, but I don't have any ideas that have shown themselves to be particularly compelling yet. The only one that might actually go in is IDeviceObject, which simply provides GetDevice. There's no use for that which I've found yet, but I'm still hoping. I'm also open to suggestions, of course.

[EDIT] There's one thing I forgot to mention. In earlier incarnations of SlimDX, ObjectTracker (which became ObjectTable) only recorded objects while Configuration.EnableObjectTracking was true. Now, things always go into the table. That flag only controls whether or not the call stack is recorded at the point of creation. Disabling tracking is a performance optimization. The default is enabled. It's a good idea to turn it off in release mode, because it can actually get quite expensive.

Share this comment

Link to comment

Original post by PromitIt's also apparently become fashionable to start posting things a day early, which is bull. Go to hell, TechARP. Go. To. Hell.

The rest of the world thinks its annoying that Americans post April Fools things a day late. Keep in mind that America comes at the end of world's daily cycle. Here in Australia we are already at work the next day before you guys finish work the previous day.