Coming to DirectX 12: More control over memory allocation
In the next update to Windows, D3D12 will be adding two new flags to the D3D12_HEAP_FLAG enumeration. These new flags are “impermanent” properties, which don’t affect the resulting memory itself, but rather the way in which it’s allocated. As such, it’s important to call out that these flags aren’t reflected from ID3D12Heap::GetDesc or ID3D12Resource::GetHeapProperties. Let’s dive in.
Today, when you ask D3D to allocate a heap or committed resource, the last thing that happens before you get back your object is the memory gets made resident. This is equivalent to a call to ID3D12Device::MakeResident being performed. There’s two problems with this:
- MakeResident’s design is that it blocks your CPU thread until the memory is fully ready to use. Sometimes this isn’t what you want.
- MakeResident will allow you to overcommit memory, beyond what the current process budget indicates you should be using.
Oddly enough, these two reasons are exactly the reasons that we added ID3D12Device3::EnqueueMakeResident. This allows apps to make different choices here, such as waiting for residency using the GPU rather than the CPU, or requesting the residency op to fail rather than going over-budget. By allocating memory in a non-resident state, now app developers can apply both of these benefits to their first use of resources as well.
This is one of those things that D3D has never clearly called out before, but I’m going to go ahead and make this statement: Committed resources and heaps newly created by D3D will almost always* have zeroed contents in them. This used to be an implementation detail, but during the development of the WDDM2.0 memory manager, we attempted to improve things here, by enabling more re-use memory that had never left the confines of a given process without zeroing. As it turns out, this had catastrophic consequences, because applications in the Windows ecosystem have taken hard dependencies on the fact that new resources with given properties are zeroed – maybe without even realizing that they had done so.
* Actual zeroing depending on resource size and CPU visibility, with details of what properties changing from one OS release to another, and from one CPU architecture to another.
So, we had to go back to returning zeroed memory. Why is this bad? Because it’s expensive! It means that the memory manager needs to explicitly write zeroes into the memory before it returns it to you – this might happen during the create call, or it might be deferred until you first access the memory, but it’s going to happen.
Now we’re giving developers the ability to opt out of this cost by simply specifying this new flag during heap/resource allocation. However, it’s important to note that this is not a guarantee, this is only an optimization. You will still get back zeroed memory if the only memory available is coming to you from another process, for security and process isolation purposes, but when you have memory that you’ve freed that can be re-used, that memory can be cheaply recycled without having to re-zero it.
It’s important to note that the rules for accessing uninitialized memory apply here, the same way that they would for creating placed resources or mapping tiles into reserved resources – resources with the render target or depth stencil flags must be cleared, discarded, or copied to before they can be used. See the “Notes on the required resource initialization” section of the documentation for CreatePlacedResource.
These flags sound great, right? What’s the catch? The only catch is you have to make sure they’re available before you can leverage them. These flags don’t require new drivers, all they require is that you’re running on a version of D3D12 which understands them. There’s no dedicated CheckFeatureSupport option for these, they’re available anytime that ID3D12Device8 is exposed, or a check for D3D12_FEATURE_D3D12_OPTIONS7 succeeds.
To use these flags in your application, install the latest Windows 10 Insider Preview build and SDK Preview Build for Windows 10 (20H1) from the Windows Insider Program.