Ok just to break that down into the three situations, as you presented them:
1. If you never instantiate batcher during startup, it's destructor is never called, so any COM object release calls only happen in the automatic call of the System object destructor and when that's called there are no errors. Exits cleanly.
2. If you do instantiate batcher, and it's destructor is called without first manually calling the system destructor, then there are no errors from the spritebatch destructor. Then, when the system object is destroyed automatically, it's destructor gives an exception when you try to release the backbufferRenderTarget COM object. It looks like the backbufferRenderTarget COM object was destroyed along with the device and context by the spritebatch destructor, despite it having that pointer's external reference from the system instance.
3. If you do the same as in 2. but first manually call the system destructor, then that call to the system destructor gives no errors, but the following automatic spritebatcher destructor will error when it tries to release the id3d11device a second time (since system's destructor already released and destroyed it).
So situation 1 is normal, and how it should work, only the pointers with references to COM objects call release on them, everything is happy. So all you really want is to instantiate batcher while retaining the object management of situation 1, right?
In situation 2, The error is caused by releasing pointers that never addref'd the pointed COM objects (batdevice and batdevcontext). I'm honestly not really certain why it's breaking the way that it does. It should give a runtime warning, but I don't understand why it full on errors on the backbufferRenderTarget. It's to do with the chain of internal references between the device, the swapchain, the ID311Texture2D of the backbuffer, and finally the ID3D11RenderTargetView that errors when you release. I'd expect the open external reference from backbufferRenderTarget would keep all the objects alive in that internal reference chain, but it does not. I don't understand the internal relationships of the DXGI/D3D runtimes well enough to say what exactly is going on. I can say how you'd fix it though, either addref the device and context in the spritebatcher constructor, or never call release on them in the spritebatch destructor.
In situation 3 it's the same problem, you're calling release when you never addref spritebatcher's pointers to the device and context, so in this case those objects no longer exist to call release on (since the manual call of the system destructor released and destroyed them first). So once more, either addref the spritebatcher device and context pointers, or never call release on them, and the problem goes away, but also never manually call the system destructor like that
. Even if you fix the problem with spritebatcher, you're in a situation where the system destructor gets called twice, once manually, once automatically, for a single instance of the system. Your checks in the destructor will probably prevent an error, but manually calling destructors is a bad idea in almost all if not absolutely all cases.
I hope it makes sense that the only pointers that call release on a COM object are those which have added an external reference to them. The pointer passed into their creation automatically has it's reference added, so that's the only pointer you really need to call release with. When you create a copy of that pointer, no reference was added automatically, and if you didn't call addref yourself, then why would the copy need to release a reference it never held.
Like I said before, it would be best to consider ownership of the COM objects when you handle pointers to them. You can make it so the pointer that was used in their creation is the sole owner and only this pointer ever calls release on the object. Any other pointers to the same COM object will never call addref or release, only the initial owner pointer will call release once when the object is no longer needed. This is easiest.
A second choice is share ownership of the COM object's between all pointers to them, and to do that make sure every time you copy a COM object pointer that you addref once using the copy, and always call release before that copy goes out of scope or is deleted. This guarantees object lifetime to all pointers but is also more susceptible to mistakes (if you ever forget to addref or release one pointer then the system breaks down, also it's a ton more boilerplate code).
A third choice is to use ComPtr's which will handle calling release and addref for you. They're like smart pointers for COM objects. I personally use ComPtr's to handle ownership, and pass raw pointers when I need to share them, and I never call release or addref on any raw pointers. That's not necessarily advised, but it works for me. This is more complicated to learn, but easiest to use, and if you give up using raw pointers entirely then it guarantees object lifetime just as well as the second choice, but without the risk of mistakes or extra code required.
To be clear I recommend that right now you follow the first choice until you're more comfortable using D3D11 and COM objects. Only look into using ComPtr once you have a handle on how to use COM objects, since ComPtr's have their own wrinkles (assigning by copy, manually releasing them etc) that will only add complication you don't need when starting out.
Edited by backstep, 27 October 2013 - 11:58 PM.