D3D12MemoryAllocator icon indicating copy to clipboard operation
D3D12MemoryAllocator copied to clipboard

Is D3D12MA::Allocator::BeginDefragmentation() Work Correctly ?

Open FlyAndNotDown opened this issue 9 months ago • 4 comments

Our in-house engine use D3D12MA as D3D12 backend's native memory allocator, and I found that placed memory fragments get more and more with game running. By read document, I found there is a interface called D3D12MA::Allocator::BeginDefragmentation can help heap allocation defragment. But after transplanted code from official sample, I just got some overlapped placed resources in heaps, like this in NSight Graphics:

image

Our resources' copy, create, access and destroy can happened on multi-thread, and resources' lifecycle is managed by intelligent pointers, but I have checked if a defragment action scheduled, any of resources' operation will blocked by mutex, and old resources' ref count will be set to 0 after defragment action.

So I got puzzled, I can not find any meaningful code on web except official sample, is D3D12MA really support defragment ? Could you have some more complex sample which can shared with us ?

FlyAndNotDown avatar Apr 28 '24 11:04 FlyAndNotDown

Hi!

Could you please provide steps that you are using for defragmentation? Generally speaking, after starting defragmentation all resources that are selected to move (listed in DEFRAGMENTATION_PASS_MOVE_INFO passed into DefragmentationContext::BeginPass()) are needed to be recreated in new places so older resources will be released and their ID3D12Resource pointers will be invalid. These steps are also described in here. Since you are using multiple threads for that, you must ensure that after committing single defragmentation pass (calling DefragmentationContext::EndPass()) all your own routines are using correct pointers to new resources. This will require some synchronization to ensure no hazards. You can also look at the tests methods for more examples on how to use defragmentation.

medranSolus avatar May 06 '24 11:05 medranSolus

Sorry about I forget to update this issue. I have checked if use many locks and some hard code to limit all the thing executed in a strict order, the library work correctly. Now what confuse me is the document specific that user need flush all graphics commands to make sure old allocation's data copy to the new one before DefragmentationContext::EndPass(), which is OK when wait fence every frame, because after flush we can update resource pointers and recreate resource views easily, when record graphics commands next time, everything is ready, no outdate resources are used. Unfortunately our in-house engine use inflight commit model, which means there are many command list executing inflight. Inflight model can brings some annoying problems, e.g. DefragmentationContext::EndPass() will release allocation after swap defragment src and dest, but at most time the resource is still used in inflight command list, so we can only defer the release process, which made lifecycle management bad.

For some reason, I can not show you code. So for inflight model, any suggestions ? Your reply is helpful for me, thanks!

FlyAndNotDown avatar May 06 '24 11:05 FlyAndNotDown

Hi, currently there is no mechanism that would allow for synchronization of such case where there might be several overlapping command lists that use resources used for defragmentation. Solution I can think of would be to set selected resources that are in flight to ignore them in current defragmentation pass or postponing executing such pass by few frames, which will result in delay in calling EndPass(). Some other mechanism that I can think of but would require changes inside the library would be to add possibility to postpone deletion of resources by few frames while switching to newly created ones for further frames. Would that sound like a desired solution?

medranSolus avatar May 06 '24 13:05 medranSolus

Sounds feasible, I will try those mechanisms later, thanks you.

FlyAndNotDown avatar May 07 '24 01:05 FlyAndNotDown