Reputation: 346
The most satisfying part of writing a DIY memory manager is structuring it so you can forget about [delete] (e.g. by bundling everything into a singly allocated/deallocated buffer).
The way DirectX handles object creation means that you can't simply cast void memory into the types you want to make (can you?) and you have to rely on COM (+ remembering to [Release] local memory in every destructor) instead. Is there any way to subvert this and convince DirectX to use my pointers?
Instead of:
ID3D12Thing* thng = DirectX::CreateThing();
...
thng->Release();
thng = nullptr;
I want:
ID3D12Thing* thng = DirectX::CreateThingAt(Allocator->AlignedAlloc(sizeof(ID3D12Thing));
...
[memory emptied during shutdown]
Upvotes: 0
Views: 1381
Reputation: 70673
Microsoft recently released a new library (WIL) for modern C++. It includes a new com pointer type:
wil::com_ptr<ID3D12Thing> thng{DirectX::CreateThing())};
Upvotes: 2
Reputation: 1847
Edit: This answer has been superseded by Chuck Walbourn's parallel answer.
While my answer is not completely wrong, after some investigation I'm pretty sure Chuck Walbourn's suggestion to use Microsoft::WRL::ComPtr
is the better choice (Please see also comment section under C.B.'s answer for reasons). /Edit
To my knowledge it is not possible to plant external memory on Direct X structures, since they are all created by Direct X itself.
Nevertheless, if the main goal is maintenance and safety, it is possible to define a std::unique_ptr
with a custom deleter which does the job of releasing.
struct DxDeleter {
void operator() (IUnknown* _moribund) { if(_moribund) _moribund->Release(); }
};
typedef std::unique_ptr<ID3D11Buffer,DxDeleter> dx_buffer_ptr;
typedef std::unique_ptr<ID3D11ShaderResourceView,DxDeleter> dx_resource_ptr;
typedef std::unique_ptr<ID3D11UnorderedAccessView,DxDeleter> dx_access_ptr;
...
The pointers then are simply assigned with e.g. myUniqueDxBufferPtr.reset( myRawBufferPtr )
or similar.
This has the high benefit, that releases don't need to be tracked. It is a good way not only for Direct X, but for all kinds of external libraries, which need allocation and deallocation (for example, I use the same technique also for Freeimage and ffmpeg).
It is also possible to define std::shared_ptr
s for resources, which are shared by multiple items (e.g. you could keep your device alive as long as any buffers exist). But this is to handle with care - in opposite of std::unique_ptr
, std::shared_ptr
s don't have the deleter as a template parameter, but as a ctor parameter - which makes it terribly easy to erroneously assign something with a missing deleter (leaks).
void g_dxDelete( IUnknown* _moribund ) {
if( _moribund )
_moribund->Release();
}
typedef std::shared_ptr<ID3D11Device> dx_device_shared;
inline dx_device_shared assignDevice( ID3D11Device* _device ) {
return std::move( dx_device_shared( _device, g_dxDelete ) );
}
In that case you'd have to always be sure to assign the created device with dx_device_shared myDevice = assignDevice(myRawDevicePtr)
, but NEVER with dx_device_shared myDevice = myRawDevicePtr
.
Upvotes: 5
Reputation: 41057
TL;DR: Use a smart-pointer like ComPtr.
I'm a big fan of std::unique_ptr
and std::shared_ptr
, but neither of them really correctly handle COM objects such as Direct3D interfaces which have their own built-in reference counts. A better choice is to use WRL class Microsoft::WRL::ComPtr
or the C++/WinRT class winrt::com_ptr
.
Some older coders might suggest the old ATL
CComPtr
, but the more modernMicrosoft::WRL::ComPtr
is a better choice.
Upvotes: 4