Boost smart ptrs + Directx woes

Started by
14 comments, last by njpaul 18 years, 7 months ago
It seems in my development thus far, a lot of my problems have come from trying to use boost::shared_ptr's instead of just a standard pointer + SafeDelete methods. Most of the time, I've been able to figure out what I was doing wrong, but this last issue has got me completly stumped. Inside my engine I'm working on, I just implemented a SoundEngine, that really does nothing but construct itself. My engine used a boost::shared_ptr to the SoundEngine, initialized it to a new SoundEngine in the game engine constructor, and didn't worry about it at all (since it was a smart pointer) in the destructor. The problem is that when the game shut down, I started getting "memory can not be "read" " errors when I released my Direct3D Device and unregistered my window class. I can't imagine what my sound engine creation could have been doing to cause this, but sure enough, if I removed the "m_SoundEngine.reset(new SoundEngine)" call, all my problems went away. Also, if I switched to just using a regular pointer, everything was fine. I'll post the jist of what is going on, in case anyone has any idea what so ever as to what is causing this. I wish I could be more specific, but I'm really at a loss now. Basically, I use shared_ptrs for everything except devices (since the devices need to be released anyway). My engine constructor basically calls: CoInitializeEx( NULL, COINIT_MULTITHREADED ); <init window, directx, directinput, etc..> m_pSoundEngine.reset(new SoundEngine(1.0f)); The game engine destructor (where the crash is occuring) does the following: // Close the current game state if(m_pCurrentState.get() != NULL) m_pCurrentState->Close(); // release the DX Devices SafeRelease(m_pFont); SafeRelease(m_pD3DDevice); // one memory error happens here SafeRelease(m_pD3D); // Uninit the COM and unregister the window CoUninitialize(); UnregisterClass(DEFAULT_GAME_CLASS, GetModuleHandle(NULL)); // and another here I thought maybe something was amiss in the constructor or destructor of my SoundEngine, so I completly commented out the code in both, and still the memory errors popped up. It seems very odd since other objects, such as my input object, use shared_ptrs in the exact same way. If anyone has any idea what could be causing this, I'd love to hear the suggestions. Thanks
Advertisement
Don't use smart pointers for the DirectX devices. You don't want to call delete on those. Make them raw pointers and just call release and let DirectX manage them. Whenever I get those errors, it is usually because I try to release something from directx twice. I use the boost smart pointers for everything else though.
you can always use boost::intrusive_ptr for your com-stuff

inline void intrusive_ptr_add_ref( IUnknown* p ) { p->AddRef(); }inline void intrusive_ptr_release( IUnknown* p ) { p->Release(); }class MyDevice{ public:  MyDevice(); private: IDirect3DDevice9* m_device; boost::intrusive_ptr < IDirect3DDevice9 > m_device_ptr;}MyDevice::MyDevice(){ m_device = CreateMyNewIDirect3DDevice(); m_device_ptr = boost::intrusive_ptr < IDirect3DDevice9 >( m_device, false )}// create the deviceboost::shared_ptr < MyDevice > pDevice( new MyDevice )


Using both intrusive and shared_ptr you do NOT have to worry about a single memory leak

Edit: Oh and if you need a raw pointer to the IDirect3DDevice9 object just add this line of code in your constructor:

m_device = m_device_ptr.get();
Ethereal
The shared ptr you're using is destroyed when it gets out of the scope of yor function (at least not earlier), so it is at the very end, after the window has been closed, and all devices released. Leaving this that way is messy, since you cannot explicitly control the order of destruction.

Also, what does it have to do with DX? SoundEngine is not a DX COM interface, AFAIK, or am I wrong? I'm guessing SoundEngine has some pointers to DX resources, that it's trying to release. And as either: device or window has been previously destroyed, the resources have been already automatically reclaimed by the application. So it's double-releasing.
~def
Well.. if the SoundEngine is written using DirectSound it sure is an com-object
Ethereal
njpaul: Yeah, I'm not using smart pointers for the Devices. I just call a SafeRelease on them in the engine destructor.

deffer: My SoundEngine uses DirectSound, as Metus said. I create 2 COM objects via CoCreateInstance in the constructor (although my errors were happening even when the constructor/destructor was commented out).

Along the lines of what Deffer said, though, is there a possible problem with smart pointers being deleted after the window closes?

for example, I had 3 smart pointers in my engine (all boost::shared_ptr)
m_Input
m_ResourceManager
m_Script

which all worked fine, but I started getting crashes when I added

m_SoundEngine

Since they are all smart pointers, I didn't worry about calling delete on them. Is there something in leaving it up to the smart pointer deletion that could be causing the error? Maybe needing to be deleted (and therefore releasing any COM objects in their class) before releasing the other devices?

Would it be a better idea to call SafeDelete(m_Input.get()), SafeDelete(m_Script.get()), etc.? Or should their deletion order not matter in the destructor of my engine?

I guess the biggest question burning in my mind is how I'm introducing an error at the SafeRelease(m_pD3DDevice) point of my destructor. All that was done was the introduction of a pointer to my SoundEngine class, which after commenting out all the functions, looks very simply like:

class SoundEngine{
public:
SoundSystem(float scale){};
virtual ~SoundSystem(){};

private:
float scale;
IDirectMusicLoader8* loader;
IDirectMusicPerformance8* performance;
IDirectMusicListener8* listener;
};

My engine.h has 1 line with this class mentioned:
boost::shared_ptr< SoundEngine > m_SoundEngine;

and in engine.cpp, I only initialize it:
m_SoundEngine.reset(new SoundEngine(1.0f));

If anyone sees where my errors are coming from, please let me know. I know I can just use a regular pointer as a workaround, but I'd like to know what I'm doing wrong rather than just ignore it.

Thanks for the help so far
Quote:Original post by Antrim
Since they are all smart pointers, I didn't worry about calling delete on them. Is there something in leaving it up to the smart pointer deletion that could be causing the error? Maybe needing to be deleted (and therefore releasing any COM objects in their class) before releasing the other devices?


I thought so at first, but I just made an ultra-minimalistic-test-appTM, using std::auto_ptr holding a class with a pointer to IDirect3DDevice9. Upon app exit, I was releasing IDirect3D9 first, and then let the auto_ptr do its job with IDirect3DDevice9;
class CC{public:   CC(void)   { m_pDev = NULL; };   virtual ~CC(void)   {      if (m_pDev) {         m_pDev->Release();         m_pDev = NULL;      };   };   IDirect3DDevice9* m_pDev;};std::auto_ptr<CC>  g_Cptr;


All went just fine...

So I'm guessing (again!) that the reason for the error lies somewhere else, and it is "infecting" the device somehow. I mean, somewhere you've got buffer overrun, and you didn't noticed it earlier, and now, by coincidence, shared_ptr is the missing link for the error to spread.
~def
Ok, so I made *some* progress (I think)

After much more debugging, it appeared that the overall problem was this:

My SoundEngine created a listener and performance with CoCreateInstance, and then destroyed them in its destructor. Because of SoundEngine being killed by a shared_ptr, it would be destroyed after SafeReleasing my other devices, and calling CoUninitialize(). I believe it was the call happening after CoUninitialize that was the cause.

I modified my engine destructor so that now the SoundEngine has it's destructor called earlier:


// Close the current game state
if(m_pCurrentState.get() != NULL)
m_pCurrentState->Close();

// release the DX Devices
m_InputDevices.reset();
m_SoundSystem.reset();
SafeRelease(m_Font);
SafeRelease(m_D3DDevice);
SafeRelease(m_D3D);

// Uninit the COM and unregister the window
CoUninitialize();
UnregisterClass(DEFAULT_GAME_CLASS, GetModuleHandle(NULL));


Thankfully this solves most of my problem. However something else (seemingly minor) has crept up in its wake. When I run by .exe file, I have no problems. However, if I run the program in debug mode (with VS.NET) I get an "unhandled exception error" in the SoundEngine destructor:

SoundEngine::~SoundEngine(){
m_performance->Stop(NULL, NULL, 0, 0);
m_performance->CloseDown(); // UNHANDLED EXCEPTION OCCURS HERE

SafeRelease(m_loader);
SafeRelease(m_performance);
}

Aside from this, the program seems to function perfectly. The debug output doesn't let me know what the exception is, and only gives the option to view the disassembly, so I really don't know why it is happening.

If anyone has encountered this problem or something similar, I'd appreciate any insight you can give into solving it.

Thanks for all the help so far.
Quote:Original post by Metus
you can always use boost::intrusive_ptr for your com-stuff...


Seems to be a terribly convoluted way of dealing with DirectX. Not to mention obfuscating. I can understand the usefulness of Boost, but I honestly haven't found a reason to use it myself. Consequently, the Boost library currently just hogs diskspace :)











No no no no! :)
Quote:I can understand the usefulness of Boost, but I honestly haven't found a reason to use it myself. Consequently, the Boost library currently just hogs diskspace :)


The shared pointer in boost seems to be the most useful.

This topic is closed to new replies.

Advertisement