Added documentation chapter about defragmentation

This commit is contained in:
Adam Sawicki 2022-03-15 11:32:07 +01:00
parent 2fd8ea323a
commit d0cc54c5ad
4 changed files with 258 additions and 56 deletions

View File

@ -69,7 +69,122 @@ $(function() {
<div class="headertitle"><div class="title">Defragmentation </div></div> <div class="headertitle"><div class="title">Defragmentation </div></div>
</div><!--header--> </div><!--header-->
<div class="contents"> <div class="contents">
<div class="textblock"><p >TBD... </p> <div class="textblock"><p >Interleaved allocations and deallocations of many objects of varying size can cause fragmentation over time, which can lead to a situation where the library is unable to find a continuous range of free memory for a new allocation despite there is enough free space, just scattered across many small free ranges between existing allocations.</p>
<p >To mitigate this problem, you can use defragmentation feature. It doesn't happen automatically though and needs your cooperation, because D3D12MA is a low level library that only allocates memory. It cannot recreate buffers and textures in a new place as it doesn't remember the contents of <code>D3D12_RESOURCE_DESC</code> structure. It cannot copy their contents as it doesn't record any commands to a command list.</p>
<p >Example:</p>
<div class="fragment"><div class="line"><a class="code hl_struct" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___d_e_s_c.html">D3D12MA::DEFRAGMENTATION_DESC</a> defragDesc = {};</div>
<div class="line">defragDesc.<a class="code hl_variable" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___d_e_s_c.html#aac9a357e679a0afa9ab84cd06057630b">Flags</a> = <a class="code hl_enumvalue" href="namespace_d3_d12_m_a.html#ab433989212ee6736bf9d63c5bc565df4a9d0301a81136ca79e3ba52542c6d2e13">D3D12MA::DEFRAGMENTATION_FLAG_ALGORITHM_FAST</a>;</div>
<div class="line"> </div>
<div class="line"><a class="code hl_class" href="class_d3_d12_m_a_1_1_defragmentation_context.html">D3D12MA::DefragmentationContext</a>* defragCtx;</div>
<div class="line">allocator-&gt;BeginDefragmentation(&amp;defragDesc, &amp;defragCtx);</div>
<div class="line"> </div>
<div class="line"><span class="keywordflow">for</span>(;;)</div>
<div class="line">{</div>
<div class="line"> <a class="code hl_struct" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___p_a_s_s___m_o_v_e___i_n_f_o.html">D3D12MA::DEFRAGMENTATION_PASS_MOVE_INFO</a> pass;</div>
<div class="line"> HRESULT hr = defragCtx-&gt;<a class="code hl_function" href="class_d3_d12_m_a_1_1_defragmentation_context.html#a1606c015d02edc094bb246986159d592">BeginPass</a>(&amp;pass);</div>
<div class="line"> <span class="keywordflow">if</span>(hr == S_OK)</div>
<div class="line"> <span class="keywordflow">break</span>;</div>
<div class="line"> <span class="keywordflow">else</span> <span class="keywordflow">if</span>(hr != S_FALSE)</div>
<div class="line"> <span class="comment">// Handle error...</span></div>
<div class="line"> </div>
<div class="line"> <span class="keywordflow">for</span>(UINT i = 0; i &lt; pass.<a class="code hl_variable" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___p_a_s_s___m_o_v_e___i_n_f_o.html#a8df22d990c318d82fe9fcc5f04132c04">MoveCount</a>; ++i)</div>
<div class="line"> {</div>
<div class="line"> <span class="comment">// Inspect pass.pMoves[i].pSrcAllocation, identify what buffer/texture it represents.</span></div>
<div class="line"> MyEngineResourceData* resData = (MyEngineResourceData*)pMoves[i].pSrcAllocation-&gt;GetPrivateData();</div>
<div class="line"> </div>
<div class="line"> <span class="comment">// Recreate this buffer/texture as placed at pass.pMoves[i].pDstTmpAllocation.</span></div>
<div class="line"> D3D12_RESOURCE_DESC resDesc = ...</div>
<div class="line"> ID3D12Resource* newRes;</div>
<div class="line"> hr = device-&gt;CreatePlacedResource(</div>
<div class="line"> pass.<a class="code hl_variable" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___p_a_s_s___m_o_v_e___i_n_f_o.html#a719fbdaae54251759605c41baeb24dc4">pMoves</a>[i].<a class="code hl_variable" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___m_o_v_e.html#aec0c7f51ccc870c6a88af7c7390d8eda">pDstTmpAllocation</a>-&gt;<a class="code hl_function" href="class_d3_d12_m_a_1_1_allocation.html#adca8d5a82bed492fe7265fcda6e53da2">GetHeap</a>(),</div>
<div class="line"> pass.<a class="code hl_variable" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___p_a_s_s___m_o_v_e___i_n_f_o.html#a719fbdaae54251759605c41baeb24dc4">pMoves</a>[i].<a class="code hl_variable" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___m_o_v_e.html#aec0c7f51ccc870c6a88af7c7390d8eda">pDstTmpAllocation</a>-&gt;<a class="code hl_function" href="class_d3_d12_m_a_1_1_allocation.html#a47be9557d441797b65de177a3d5cdf60">GetOffset</a>(), &amp;resDesc,</div>
<div class="line"> D3D12_RESOURCE_STATE_COPY_DEST, NULL, IID_PPV_ARGS(&amp;newRes));</div>
<div class="line"> <span class="comment">// Check hr...</span></div>
<div class="line"> </div>
<div class="line"> <span class="comment">// Store new resource in the pDstTmpAllocation.</span></div>
<div class="line"> pass.<a class="code hl_variable" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___p_a_s_s___m_o_v_e___i_n_f_o.html#a719fbdaae54251759605c41baeb24dc4">pMoves</a>[i].<a class="code hl_variable" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___m_o_v_e.html#aec0c7f51ccc870c6a88af7c7390d8eda">pDstTmpAllocation</a>-&gt;<a class="code hl_function" href="class_d3_d12_m_a_1_1_allocation.html#a414a088c22bae0f29b1038f5f9346d14">SetResource</a>(newRes);</div>
<div class="line"> </div>
<div class="line"> <span class="comment">// Copy its content to the new place.</span></div>
<div class="line"> cmdList-&gt;CopyResource(</div>
<div class="line"> pass.<a class="code hl_variable" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___p_a_s_s___m_o_v_e___i_n_f_o.html#a719fbdaae54251759605c41baeb24dc4">pMoves</a>[i].<a class="code hl_variable" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___m_o_v_e.html#aec0c7f51ccc870c6a88af7c7390d8eda">pDstTmpAllocation</a>-&gt;<a class="code hl_function" href="class_d3_d12_m_a_1_1_allocation.html#ad00308118252f82d8f803c623c67bf18">GetResource</a>(),</div>
<div class="line"> pass.<a class="code hl_variable" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___p_a_s_s___m_o_v_e___i_n_f_o.html#a719fbdaae54251759605c41baeb24dc4">pMoves</a>[i].<a class="code hl_variable" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___m_o_v_e.html#a4946b874c958a71c21fac25b515cf5f7">pSrcAllocation</a>-&gt;<a class="code hl_function" href="class_d3_d12_m_a_1_1_allocation.html#ad00308118252f82d8f803c623c67bf18">GetResource</a>());</div>
<div class="line"> }</div>
<div class="line"> </div>
<div class="line"> <span class="comment">// Make sure the copy commands finished executing.</span></div>
<div class="line"> cmdQueue-&gt;ExecuteCommandLists(...);</div>
<div class="line"> <span class="comment">// ...</span></div>
<div class="line"> WaitForSingleObject(fenceEvent, INFINITE);</div>
<div class="line"> </div>
<div class="line"> <span class="comment">// Update appropriate descriptors to point to the new places...</span></div>
<div class="line"> </div>
<div class="line"> hr = defragCtx-&gt;<a class="code hl_function" href="class_d3_d12_m_a_1_1_defragmentation_context.html#aad502ba70d6dadaeee37703fd8bf90ae">EndPass</a>(&amp;pass);</div>
<div class="line"> <span class="keywordflow">if</span>(hr == S_OK)</div>
<div class="line"> <span class="keywordflow">break</span>;</div>
<div class="line"> <span class="keywordflow">else</span> <span class="keywordflow">if</span>(hr != S_FALSE)</div>
<div class="line"> <span class="comment">// Handle error...</span></div>
<div class="line">}</div>
<div class="line"> </div>
<div class="line">defragCtx-&gt;Release();</div>
<div class="ttc" id="aclass_d3_d12_m_a_1_1_allocation_html_a414a088c22bae0f29b1038f5f9346d14"><div class="ttname"><a href="class_d3_d12_m_a_1_1_allocation.html#a414a088c22bae0f29b1038f5f9346d14">D3D12MA::Allocation::SetResource</a></div><div class="ttdeci">void SetResource(ID3D12Resource *pResource)</div><div class="ttdoc">Releases the resource currently pointed by the allocation (if any), sets it to new one,...</div></div>
<div class="ttc" id="aclass_d3_d12_m_a_1_1_allocation_html_a47be9557d441797b65de177a3d5cdf60"><div class="ttname"><a href="class_d3_d12_m_a_1_1_allocation.html#a47be9557d441797b65de177a3d5cdf60">D3D12MA::Allocation::GetOffset</a></div><div class="ttdeci">UINT64 GetOffset() const</div><div class="ttdoc">Returns offset in bytes from the start of memory heap.</div></div>
<div class="ttc" id="aclass_d3_d12_m_a_1_1_allocation_html_ad00308118252f82d8f803c623c67bf18"><div class="ttname"><a href="class_d3_d12_m_a_1_1_allocation.html#ad00308118252f82d8f803c623c67bf18">D3D12MA::Allocation::GetResource</a></div><div class="ttdeci">ID3D12Resource * GetResource() const</div><div class="ttdoc">Returns D3D12 resource associated with this object.</div><div class="ttdef"><b>Definition:</b> D3D12MemAlloc.h:493</div></div>
<div class="ttc" id="aclass_d3_d12_m_a_1_1_allocation_html_adca8d5a82bed492fe7265fcda6e53da2"><div class="ttname"><a href="class_d3_d12_m_a_1_1_allocation.html#adca8d5a82bed492fe7265fcda6e53da2">D3D12MA::Allocation::GetHeap</a></div><div class="ttdeci">ID3D12Heap * GetHeap() const</div><div class="ttdoc">Returns memory heap that the resource is created in.</div></div>
<div class="ttc" id="aclass_d3_d12_m_a_1_1_defragmentation_context_html"><div class="ttname"><a href="class_d3_d12_m_a_1_1_defragmentation_context.html">D3D12MA::DefragmentationContext</a></div><div class="ttdoc">Represents defragmentation process in progress.</div><div class="ttdef"><b>Definition:</b> D3D12MemAlloc.h:773</div></div>
<div class="ttc" id="aclass_d3_d12_m_a_1_1_defragmentation_context_html_a1606c015d02edc094bb246986159d592"><div class="ttname"><a href="class_d3_d12_m_a_1_1_defragmentation_context.html#a1606c015d02edc094bb246986159d592">D3D12MA::DefragmentationContext::BeginPass</a></div><div class="ttdeci">HRESULT BeginPass(DEFRAGMENTATION_PASS_MOVE_INFO *pPassInfo)</div><div class="ttdoc">Starts single defragmentation pass.</div></div>
<div class="ttc" id="aclass_d3_d12_m_a_1_1_defragmentation_context_html_aad502ba70d6dadaeee37703fd8bf90ae"><div class="ttname"><a href="class_d3_d12_m_a_1_1_defragmentation_context.html#aad502ba70d6dadaeee37703fd8bf90ae">D3D12MA::DefragmentationContext::EndPass</a></div><div class="ttdeci">HRESULT EndPass(DEFRAGMENTATION_PASS_MOVE_INFO *pPassInfo)</div><div class="ttdoc">Ends single defragmentation pass.</div></div>
<div class="ttc" id="anamespace_d3_d12_m_a_html_ab433989212ee6736bf9d63c5bc565df4a9d0301a81136ca79e3ba52542c6d2e13"><div class="ttname"><a href="namespace_d3_d12_m_a.html#ab433989212ee6736bf9d63c5bc565df4a9d0301a81136ca79e3ba52542c6d2e13">D3D12MA::DEFRAGMENTATION_FLAG_ALGORITHM_FAST</a></div><div class="ttdeci">@ DEFRAGMENTATION_FLAG_ALGORITHM_FAST</div><div class="ttdef"><b>Definition:</b> D3D12MemAlloc.h:653</div></div>
<div class="ttc" id="astruct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___d_e_s_c_html"><div class="ttname"><a href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___d_e_s_c.html">D3D12MA::DEFRAGMENTATION_DESC</a></div><div class="ttdoc">Parameters for defragmentation.</div><div class="ttdef"><b>Definition:</b> D3D12MemAlloc.h:675</div></div>
<div class="ttc" id="astruct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___d_e_s_c_html_aac9a357e679a0afa9ab84cd06057630b"><div class="ttname"><a href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___d_e_s_c.html#aac9a357e679a0afa9ab84cd06057630b">D3D12MA::DEFRAGMENTATION_DESC::Flags</a></div><div class="ttdeci">DEFRAGMENTATION_FLAGS Flags</div><div class="ttdoc">Flags.</div><div class="ttdef"><b>Definition:</b> D3D12MemAlloc.h:677</div></div>
<div class="ttc" id="astruct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___m_o_v_e_html_a4946b874c958a71c21fac25b515cf5f7"><div class="ttname"><a href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___m_o_v_e.html#a4946b874c958a71c21fac25b515cf5f7">D3D12MA::DEFRAGMENTATION_MOVE::pSrcAllocation</a></div><div class="ttdeci">Allocation * pSrcAllocation</div><div class="ttdoc">Allocation that should be moved.</div><div class="ttdef"><b>Definition:</b> D3D12MemAlloc.h:711</div></div>
<div class="ttc" id="astruct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___m_o_v_e_html_aec0c7f51ccc870c6a88af7c7390d8eda"><div class="ttname"><a href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___m_o_v_e.html#aec0c7f51ccc870c6a88af7c7390d8eda">D3D12MA::DEFRAGMENTATION_MOVE::pDstTmpAllocation</a></div><div class="ttdeci">Allocation * pDstTmpAllocation</div><div class="ttdoc">Temporary allocation pointing to destination memory that will replace pSrcAllocation.</div><div class="ttdef"><b>Definition:</b> D3D12MemAlloc.h:719</div></div>
<div class="ttc" id="astruct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___p_a_s_s___m_o_v_e___i_n_f_o_html"><div class="ttname"><a href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___p_a_s_s___m_o_v_e___i_n_f_o.html">D3D12MA::DEFRAGMENTATION_PASS_MOVE_INFO</a></div><div class="ttdoc">Parameters for incremental defragmentation steps.</div><div class="ttdef"><b>Definition:</b> D3D12MemAlloc.h:727</div></div>
<div class="ttc" id="astruct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___p_a_s_s___m_o_v_e___i_n_f_o_html_a719fbdaae54251759605c41baeb24dc4"><div class="ttname"><a href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___p_a_s_s___m_o_v_e___i_n_f_o.html#a719fbdaae54251759605c41baeb24dc4">D3D12MA::DEFRAGMENTATION_PASS_MOVE_INFO::pMoves</a></div><div class="ttdeci">DEFRAGMENTATION_MOVE * pMoves</div><div class="ttdoc">Array of moves to be performed by the user in the current defragmentation pass.</div><div class="ttdef"><b>Definition:</b> D3D12MemAlloc.h:751</div></div>
<div class="ttc" id="astruct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___p_a_s_s___m_o_v_e___i_n_f_o_html_a8df22d990c318d82fe9fcc5f04132c04"><div class="ttname"><a href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___p_a_s_s___m_o_v_e___i_n_f_o.html#a8df22d990c318d82fe9fcc5f04132c04">D3D12MA::DEFRAGMENTATION_PASS_MOVE_INFO::MoveCount</a></div><div class="ttdeci">UINT32 MoveCount</div><div class="ttdoc">Number of elements in the pMoves array.</div><div class="ttdef"><b>Definition:</b> D3D12MemAlloc.h:729</div></div>
</div><!-- fragment --><p >Although functions like <a class="el" href="class_d3_d12_m_a_1_1_allocator.html#aa37d6b9fe8ea0864f7a35b9d68e8345a" title="Allocates memory and creates a D3D12 resource (buffer or texture). This is the main allocation functi...">D3D12MA::Allocator::CreateResource()</a> create an allocation and a buffer/texture at once, these are just a shortcut for allocating memory and creating a placed resource. Defragmentation works on memory allocations only. You must handle the rest manually. Defragmentation is an iterative process that should repreat "passes" as long as related functions return <code>S_FALSE</code> not <code>S_OK</code>. In each pass:</p>
<ol type="1">
<li><a class="el" href="class_d3_d12_m_a_1_1_defragmentation_context.html#a1606c015d02edc094bb246986159d592" title="Starts single defragmentation pass.">D3D12MA::DefragmentationContext::BeginPass()</a> function call:<ul>
<li>Calculates and returns the list of allocations to be moved in this pass. Note this can be a time-consuming process.</li>
<li>Reserves destination memory for them by creating temporary destination allocations that you can query for their <code>ID3D12Heap</code> + offset using methods like <a class="el" href="class_d3_d12_m_a_1_1_allocation.html#adca8d5a82bed492fe7265fcda6e53da2" title="Returns memory heap that the resource is created in.">D3D12MA::Allocation::GetHeap()</a>.</li>
</ul>
</li>
<li>Inside the pass, <b>you should</b>:<ul>
<li>Inspect the returned list of allocations to be moved.</li>
<li>Create new buffers/textures as placed at the returned destination temporary allocations.</li>
<li>Copy data from source to destination resources if necessary.</li>
<li>Store the pointer to the new resource in the temporary destination allocation.</li>
</ul>
</li>
<li><a class="el" href="class_d3_d12_m_a_1_1_defragmentation_context.html#aad502ba70d6dadaeee37703fd8bf90ae" title="Ends single defragmentation pass.">D3D12MA::DefragmentationContext::EndPass()</a> function call:<ul>
<li>Frees the source memory reserved for the allocations that are moved.</li>
<li>Modifies source <a class="el" href="class_d3_d12_m_a_1_1_allocation.html" title="Represents single memory allocation.">D3D12MA::Allocation</a> objects that are moved to point to the destination reserved memory and destination resource, while source resource is released.</li>
<li>Frees <code>ID3D12Heap</code> blocks that became empty.</li>
</ul>
</li>
</ol>
<p >Defragmentation algorithm tries to move all suitable allocations. You can, however, refuse to move some of them inside a defragmentation pass, by setting <code>pass.pMoves[i].Operation</code> to <a class="el" href="namespace_d3_d12_m_a.html#a82bb787a69699a877b4166789a30e602abefe270a1803998dda3f8e01ec3a4ad6" title="Set this value if you cannot move the allocation. New place reserved at pDstTmpAllocation will be fre...">D3D12MA::DEFRAGMENTATION_MOVE_OPERATION_IGNORE</a>. This is not recommended and may result in suboptimal packing of the allocations after defragmentation. If you cannot ensure any allocation can be moved, it is better to keep movable allocations separate in a custom pool.</p>
<p >Inside a pass, for each allocation that should be moved:</p>
<ul>
<li>You should copy its data from the source to the destination place by calling e.g. <code>CopyResource()</code>.<ul>
<li>You need to make sure these commands finished executing before the source buffers/textures are released by <a class="el" href="class_d3_d12_m_a_1_1_defragmentation_context.html#aad502ba70d6dadaeee37703fd8bf90ae" title="Ends single defragmentation pass.">D3D12MA::DefragmentationContext::EndPass()</a>.</li>
</ul>
</li>
<li>If a resource doesn't contain any meaningful data, e.g. it is a transient render-target texture to be cleared, filled, and used temporarily in each rendering frame, you can just recreate this texture without copying its data.</li>
<li>If the resource is in <code>D3D12_HEAP_TYPE_READBACK</code> memory, you can copy its data on the CPU using <code>memcpy()</code>.</li>
<li>If you cannot move the allocation, you can set <code>pass.pMoves[i].Operation</code> to <a class="el" href="namespace_d3_d12_m_a.html#a82bb787a69699a877b4166789a30e602abefe270a1803998dda3f8e01ec3a4ad6" title="Set this value if you cannot move the allocation. New place reserved at pDstTmpAllocation will be fre...">D3D12MA::DEFRAGMENTATION_MOVE_OPERATION_IGNORE</a>. This will cancel the move.<ul>
<li><a class="el" href="class_d3_d12_m_a_1_1_defragmentation_context.html#aad502ba70d6dadaeee37703fd8bf90ae" title="Ends single defragmentation pass.">D3D12MA::DefragmentationContext::EndPass()</a> will then free the destination memory not the source memory of the allocation, leaving it unchanged.</li>
</ul>
</li>
<li>If you decide the allocation is unimportant and can be destroyed instead of moved (e.g. it wasn't used for long time), you can set <code>pass.pMoves[i].Operation</code> to <a class="el" href="namespace_d3_d12_m_a.html#a82bb787a69699a877b4166789a30e602aa2143507d723de458c2ed94e143ac242" title="Set this value if you decide to abandon the allocation and you destroyed the resource....">D3D12MA::DEFRAGMENTATION_MOVE_OPERATION_DESTROY</a>.<ul>
<li><a class="el" href="class_d3_d12_m_a_1_1_defragmentation_context.html#aad502ba70d6dadaeee37703fd8bf90ae" title="Ends single defragmentation pass.">D3D12MA::DefragmentationContext::EndPass()</a> will then free both source and destination memory, and will destroy the source <a class="el" href="class_d3_d12_m_a_1_1_allocation.html" title="Represents single memory allocation.">D3D12MA::Allocation</a> object.</li>
</ul>
</li>
</ul>
<p >You can defragment a specific custom pool by calling <a class="el" href="class_d3_d12_m_a_1_1_pool.html#adc87bb49c192de8f5a9ca0484c499575" title="Begins defragmentation process of the current pool.">D3D12MA::Pool::BeginDefragmentation</a> or all the default pools by calling <a class="el" href="class_d3_d12_m_a_1_1_allocator.html#a08e1468f1dbb63ce3bf6680e592b2143" title="Begins defragmentation process of the default pools.">D3D12MA::Allocator::BeginDefragmentation</a> (like in the example above).</p>
<p >Defragmentation is always performed in each pool separately. Allocations are never moved between different heap types. The size of the destination memory reserved for a moved allocation is the same as the original one. Alignment of an allocation as it was determined using <code>GetResourceAllocationInfo()</code> is also respected after defragmentation. Buffers/textures should be recreated with the same <code>D3D12_RESOURCE_DESC</code> parameters as the original ones.</p>
<p >You can perform the defragmentation incrementally to limit the number of allocations and bytes to be moved in each pass, e.g. to call it in sync with render frames and not to experience too big hitches. See members: <a class="el" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___d_e_s_c.html#ad8d224e0687a35898970d0a5688c6343" title="Maximum numbers of bytes that can be copied during single pass, while moving allocations to different...">D3D12MA::DEFRAGMENTATION_DESC::MaxBytesPerPass</a>, <a class="el" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___d_e_s_c.html#a83bfb404f387863eafdd6703483aed89" title="Maximum number of allocations that can be moved during single pass to a different place.">D3D12MA::DEFRAGMENTATION_DESC::MaxAllocationsPerPass</a>.</p>
<p >It is also safe to perform the defragmentation asynchronously to render frames and other Direct3D 12 and D3D12MA usage, possibly from multiple threads, with the exception that allocations returned in <a class="el" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___p_a_s_s___m_o_v_e___i_n_f_o.html#a719fbdaae54251759605c41baeb24dc4" title="Array of moves to be performed by the user in the current defragmentation pass.">D3D12MA::DEFRAGMENTATION_PASS_MOVE_INFO::pMoves</a> shouldn't be released until the defragmentation pass is ended.</p>
<p ><b>Mapping</b> is out of scope of this library and so it is not preserved after an allocation is moved during defragmentation. You need to map the new resource yourself if needed.</p>
<dl class="section note"><dt>Note</dt><dd>Defragmentation is not supported in custom pools created with <a class="el" href="namespace_d3_d12_m_a.html#a919d8545365d6b7209a964f2b99936d1aa37a0103f511954ea42a1d0bba286b6a" title="Enables alternative, linear allocation algorithm in this pool.">D3D12MA::POOL_FLAG_ALGORITHM_LINEAR</a>. </dd></dl>
</div></div><!-- contents --> </div></div><!-- contents -->
</div><!-- PageDoc --> </div><!-- PageDoc -->
<!-- start footer part --> <!-- start footer part -->

View File

@ -405,56 +405,6 @@ Functions</h2></td></tr>
</td></tr> </td></tr>
</table> </table>
</div>
</div>
<a id="ab433989212ee6736bf9d63c5bc565df4" name="ab433989212ee6736bf9d63c5bc565df4"></a>
<h2 class="memtitle"><span class="permalink"><a href="#ab433989212ee6736bf9d63c5bc565df4">&#9670;&nbsp;</a></span>DEFRAGMENTATION_FLAGS</h2>
<div class="memitem">
<div class="memproto">
<table class="memname">
<tr>
<td class="memname">enum <a class="el" href="namespace_d3_d12_m_a.html#ab433989212ee6736bf9d63c5bc565df4">D3D12MA::DEFRAGMENTATION_FLAGS</a></td>
</tr>
</table>
</div><div class="memdoc">
<p>Flags to be passed as <a class="el" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___d_e_s_c.html#aac9a357e679a0afa9ab84cd06057630b" title="Flags.">DEFRAGMENTATION_DESC::Flags</a>. </p>
<table class="fieldtable">
<tr><th colspan="2">Enumerator</th></tr><tr><td class="fieldname"><a id="ab433989212ee6736bf9d63c5bc565df4a9d0301a81136ca79e3ba52542c6d2e13" name="ab433989212ee6736bf9d63c5bc565df4a9d0301a81136ca79e3ba52542c6d2e13"></a>DEFRAGMENTATION_FLAG_ALGORITHM_FAST&#160;</td><td class="fielddoc"><p >Use simple but fast algorithm for defragmentation. May not achieve best results but will require least time to compute and least allocations to copy. </p>
</td></tr>
<tr><td class="fieldname"><a id="ab433989212ee6736bf9d63c5bc565df4ace3a84861ed6a1b8066fa9c12c7e89b9" name="ab433989212ee6736bf9d63c5bc565df4ace3a84861ed6a1b8066fa9c12c7e89b9"></a>DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED&#160;</td><td class="fielddoc"><p >Default defragmentation algorithm, applied also when no <code>ALGORITHM</code> flag is specified. Offers a balance between defragmentation quality and the amount of allocations and bytes that need to be moved. </p>
</td></tr>
<tr><td class="fieldname"><a id="ab433989212ee6736bf9d63c5bc565df4af205647f08a5c2cddced83ed66892467" name="ab433989212ee6736bf9d63c5bc565df4af205647f08a5c2cddced83ed66892467"></a>DEFRAGMENTATION_FLAG_ALGORITHM_FULL&#160;</td><td class="fielddoc"><p >Perform full defragmentation of memory. Can result in notably more time to compute and allocations to copy, but will achieve best memory packing. </p>
</td></tr>
<tr><td class="fieldname"><a id="ab433989212ee6736bf9d63c5bc565df4a567e0fe890cc07d8dd6576584e8d9f2d" name="ab433989212ee6736bf9d63c5bc565df4a567e0fe890cc07d8dd6576584e8d9f2d"></a>DEFRAGMENTATION_FLAG_ALGORITHM_MASK&#160;</td><td class="fielddoc"><p >A bit mask to extract only <code>ALGORITHM</code> bits from entire set of flags. </p>
</td></tr>
</table>
</div>
</div>
<a id="a82bb787a69699a877b4166789a30e602" name="a82bb787a69699a877b4166789a30e602"></a>
<h2 class="memtitle"><span class="permalink"><a href="#a82bb787a69699a877b4166789a30e602">&#9670;&nbsp;</a></span>DEFRAGMENTATION_MOVE_OPERATION</h2>
<div class="memitem">
<div class="memproto">
<table class="memname">
<tr>
<td class="memname">enum <a class="el" href="namespace_d3_d12_m_a.html#a82bb787a69699a877b4166789a30e602">D3D12MA::DEFRAGMENTATION_MOVE_OPERATION</a></td>
</tr>
</table>
</div><div class="memdoc">
<p>Operation performed on single defragmentation move. </p>
<table class="fieldtable">
<tr><th colspan="2">Enumerator</th></tr><tr><td class="fieldname"><a id="a82bb787a69699a877b4166789a30e602a29a5c20322e633f6c34ddebd16bc61ac" name="a82bb787a69699a877b4166789a30e602a29a5c20322e633f6c34ddebd16bc61ac"></a>DEFRAGMENTATION_MOVE_OPERATION_COPY&#160;</td><td class="fielddoc"><p >Resource has been recreated at <code>pDstTmpAllocation</code>, data has been copied, old resource has been destroyed. <code>pSrcAllocation</code> will be changed to point to the new place. This is the default value set by <a class="el" href="class_d3_d12_m_a_1_1_defragmentation_context.html#a1606c015d02edc094bb246986159d592" title="Starts single defragmentation pass.">DefragmentationContext::BeginPass()</a>. </p>
</td></tr>
<tr><td class="fieldname"><a id="a82bb787a69699a877b4166789a30e602abefe270a1803998dda3f8e01ec3a4ad6" name="a82bb787a69699a877b4166789a30e602abefe270a1803998dda3f8e01ec3a4ad6"></a>DEFRAGMENTATION_MOVE_OPERATION_IGNORE&#160;</td><td class="fielddoc"><p >Set this value if you cannot move the allocation. New place reserved at <code>pDstTmpAllocation</code> will be freed. <code>pSrcAllocation</code> will remain unchanged. </p>
</td></tr>
<tr><td class="fieldname"><a id="a82bb787a69699a877b4166789a30e602aa2143507d723de458c2ed94e143ac242" name="a82bb787a69699a877b4166789a30e602aa2143507d723de458c2ed94e143ac242"></a>DEFRAGMENTATION_MOVE_OPERATION_DESTROY&#160;</td><td class="fielddoc"><p >Set this value if you decide to abandon the allocation and you destroyed the resource. New place reserved <code>pDstTmpAllocation</code> will be freed, along with <code>pSrcAllocation</code>. </p>
</td></tr>
</table>
</div> </div>
</div> </div>
<a id="a919d8545365d6b7209a964f2b99936d1" name="a919d8545365d6b7209a964f2b99936d1"></a> <a id="a919d8545365d6b7209a964f2b99936d1" name="a919d8545365d6b7209a964f2b99936d1"></a>

View File

@ -129,8 +129,8 @@ Public Attributes</h2></td></tr>
<li>Make sure these commands finished executing on the GPU.</li> <li>Make sure these commands finished executing on the GPU.</li>
</ol> </ol>
<p >Only then you can finish defragmentation pass by calling <a class="el" href="class_d3_d12_m_a_1_1_defragmentation_context.html#aad502ba70d6dadaeee37703fd8bf90ae" title="Ends single defragmentation pass.">DefragmentationContext::EndPass()</a>. After this call, the allocation will point to the new place in memory.</p> <p >Only then you can finish defragmentation pass by calling <a class="el" href="class_d3_d12_m_a_1_1_defragmentation_context.html#aad502ba70d6dadaeee37703fd8bf90ae" title="Ends single defragmentation pass.">DefragmentationContext::EndPass()</a>. After this call, the allocation will point to the new place in memory.</p>
<p >Alternatively, if you cannot move specific allocation, you can set <a class="el" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___m_o_v_e.html#a5ba8d6894267ae59df4efb9972af5d81" title="Operation to be performed on the allocation by DefragmentationContext::EndPass(). Default value is DE...">DEFRAGMENTATION_MOVE::Operation</a> to <a class="el" href="namespace_d3_d12_m_a.html#a82bb787a69699a877b4166789a30e602abefe270a1803998dda3f8e01ec3a4ad6" title="Set this value if you cannot move the allocation. New place reserved at pDstTmpAllocation will be fre...">DEFRAGMENTATION_MOVE_OPERATION_IGNORE</a>.</p> <p >Alternatively, if you cannot move specific allocation, you can set <a class="el" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___m_o_v_e.html#a5ba8d6894267ae59df4efb9972af5d81" title="Operation to be performed on the allocation by DefragmentationContext::EndPass(). Default value is DE...">DEFRAGMENTATION_MOVE::Operation</a> to <a class="el" href="namespace_d3_d12_m_a.html#a82bb787a69699a877b4166789a30e602abefe270a1803998dda3f8e01ec3a4ad6" title="Set this value if you cannot move the allocation. New place reserved at pDstTmpAllocation will be fre...">D3D12MA::DEFRAGMENTATION_MOVE_OPERATION_IGNORE</a>.</p>
<p >Alternatively, if you decide you want to completely remove the allocation, set <a class="el" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___m_o_v_e.html#a5ba8d6894267ae59df4efb9972af5d81" title="Operation to be performed on the allocation by DefragmentationContext::EndPass(). Default value is DE...">DEFRAGMENTATION_MOVE::Operation</a> to <a class="el" href="namespace_d3_d12_m_a.html#a82bb787a69699a877b4166789a30e602aa2143507d723de458c2ed94e143ac242" title="Set this value if you decide to abandon the allocation and you destroyed the resource....">DEFRAGMENTATION_MOVE_OPERATION_DESTROY</a>. Then, after <a class="el" href="class_d3_d12_m_a_1_1_defragmentation_context.html#aad502ba70d6dadaeee37703fd8bf90ae" title="Ends single defragmentation pass.">DefragmentationContext::EndPass()</a> the allocation will be released. </p> <p >Alternatively, if you decide you want to completely remove the allocation, set <a class="el" href="struct_d3_d12_m_a_1_1_d_e_f_r_a_g_m_e_n_t_a_t_i_o_n___m_o_v_e.html#a5ba8d6894267ae59df4efb9972af5d81" title="Operation to be performed on the allocation by DefragmentationContext::EndPass(). Default value is DE...">DEFRAGMENTATION_MOVE::Operation</a> to <a class="el" href="namespace_d3_d12_m_a.html#a82bb787a69699a877b4166789a30e602aa2143507d723de458c2ed94e143ac242" title="Set this value if you decide to abandon the allocation and you destroyed the resource....">D3D12MA::DEFRAGMENTATION_MOVE_OPERATION_DESTROY</a>. Then, after <a class="el" href="class_d3_d12_m_a_1_1_defragmentation_context.html#aad502ba70d6dadaeee37703fd8bf90ae" title="Ends single defragmentation pass.">DefragmentationContext::EndPass()</a> the allocation will be released. </p>
</div> </div>
</div> </div>

View File

@ -742,10 +742,10 @@ struct DEFRAGMENTATION_PASS_MOVE_INFO
After this call, the allocation will point to the new place in memory. After this call, the allocation will point to the new place in memory.
Alternatively, if you cannot move specific allocation, Alternatively, if you cannot move specific allocation,
you can set DEFRAGMENTATION_MOVE::Operation to #DEFRAGMENTATION_MOVE_OPERATION_IGNORE. you can set DEFRAGMENTATION_MOVE::Operation to D3D12MA::DEFRAGMENTATION_MOVE_OPERATION_IGNORE.
Alternatively, if you decide you want to completely remove the allocation, Alternatively, if you decide you want to completely remove the allocation,
set DEFRAGMENTATION_MOVE::Operation to #DEFRAGMENTATION_MOVE_OPERATION_DESTROY. set DEFRAGMENTATION_MOVE::Operation to D3D12MA::DEFRAGMENTATION_MOVE_OPERATION_DESTROY.
Then, after DefragmentationContext::EndPass() the allocation will be released. Then, after DefragmentationContext::EndPass() the allocation will be released.
*/ */
DEFRAGMENTATION_MOVE* pMoves; DEFRAGMENTATION_MOVE* pMoves;
@ -1762,7 +1762,144 @@ extended allocation parameters, like custom `D3D12_HEAP_PROPERTIES`, which are a
\page defragmentation Defragmentation \page defragmentation Defragmentation
TBD... Interleaved allocations and deallocations of many objects of varying size can
cause fragmentation over time, which can lead to a situation where the library is unable
to find a continuous range of free memory for a new allocation despite there is
enough free space, just scattered across many small free ranges between existing
allocations.
To mitigate this problem, you can use defragmentation feature.
It doesn't happen automatically though and needs your cooperation,
because %D3D12MA is a low level library that only allocates memory.
It cannot recreate buffers and textures in a new place as it doesn't remember the contents of `D3D12_RESOURCE_DESC` structure.
It cannot copy their contents as it doesn't record any commands to a command list.
Example:
\code
D3D12MA::DEFRAGMENTATION_DESC defragDesc = {};
defragDesc.Flags = D3D12MA::DEFRAGMENTATION_FLAG_ALGORITHM_FAST;
D3D12MA::DefragmentationContext* defragCtx;
allocator->BeginDefragmentation(&defragDesc, &defragCtx);
for(;;)
{
D3D12MA::DEFRAGMENTATION_PASS_MOVE_INFO pass;
HRESULT hr = defragCtx->BeginPass(&pass);
if(hr == S_OK)
break;
else if(hr != S_FALSE)
// Handle error...
for(UINT i = 0; i < pass.MoveCount; ++i)
{
// Inspect pass.pMoves[i].pSrcAllocation, identify what buffer/texture it represents.
MyEngineResourceData* resData = (MyEngineResourceData*)pMoves[i].pSrcAllocation->GetPrivateData();
// Recreate this buffer/texture as placed at pass.pMoves[i].pDstTmpAllocation.
D3D12_RESOURCE_DESC resDesc = ...
ID3D12Resource* newRes;
hr = device->CreatePlacedResource(
pass.pMoves[i].pDstTmpAllocation->GetHeap(),
pass.pMoves[i].pDstTmpAllocation->GetOffset(), &resDesc,
D3D12_RESOURCE_STATE_COPY_DEST, NULL, IID_PPV_ARGS(&newRes));
// Check hr...
// Store new resource in the pDstTmpAllocation.
pass.pMoves[i].pDstTmpAllocation->SetResource(newRes);
// Copy its content to the new place.
cmdList->CopyResource(
pass.pMoves[i].pDstTmpAllocation->GetResource(),
pass.pMoves[i].pSrcAllocation->GetResource());
}
// Make sure the copy commands finished executing.
cmdQueue->ExecuteCommandLists(...);
// ...
WaitForSingleObject(fenceEvent, INFINITE);
// Update appropriate descriptors to point to the new places...
hr = defragCtx->EndPass(&pass);
if(hr == S_OK)
break;
else if(hr != S_FALSE)
// Handle error...
}
defragCtx->Release();
\endcode
Although functions like D3D12MA::Allocator::CreateResource()
create an allocation and a buffer/texture at once, these are just a shortcut for
allocating memory and creating a placed resource.
Defragmentation works on memory allocations only. You must handle the rest manually.
Defragmentation is an iterative process that should repreat "passes" as long as related functions
return `S_FALSE` not `S_OK`.
In each pass:
1. D3D12MA::DefragmentationContext::BeginPass() function call:
- Calculates and returns the list of allocations to be moved in this pass.
Note this can be a time-consuming process.
- Reserves destination memory for them by creating temporary destination allocations
that you can query for their `ID3D12Heap` + offset using methods like D3D12MA::Allocation::GetHeap().
2. Inside the pass, **you should**:
- Inspect the returned list of allocations to be moved.
- Create new buffers/textures as placed at the returned destination temporary allocations.
- Copy data from source to destination resources if necessary.
- Store the pointer to the new resource in the temporary destination allocation.
3. D3D12MA::DefragmentationContext::EndPass() function call:
- Frees the source memory reserved for the allocations that are moved.
- Modifies source D3D12MA::Allocation objects that are moved to point to the destination reserved memory
and destination resource, while source resource is released.
- Frees `ID3D12Heap` blocks that became empty.
Defragmentation algorithm tries to move all suitable allocations.
You can, however, refuse to move some of them inside a defragmentation pass, by setting
`pass.pMoves[i].Operation` to D3D12MA::DEFRAGMENTATION_MOVE_OPERATION_IGNORE.
This is not recommended and may result in suboptimal packing of the allocations after defragmentation.
If you cannot ensure any allocation can be moved, it is better to keep movable allocations separate in a custom pool.
Inside a pass, for each allocation that should be moved:
- You should copy its data from the source to the destination place by calling e.g. `CopyResource()`.
- You need to make sure these commands finished executing before the source buffers/textures are released by D3D12MA::DefragmentationContext::EndPass().
- If a resource doesn't contain any meaningful data, e.g. it is a transient render-target texture to be cleared,
filled, and used temporarily in each rendering frame, you can just recreate this texture
without copying its data.
- If the resource is in `D3D12_HEAP_TYPE_READBACK` memory, you can copy its data on the CPU
using `memcpy()`.
- If you cannot move the allocation, you can set `pass.pMoves[i].Operation` to D3D12MA::DEFRAGMENTATION_MOVE_OPERATION_IGNORE.
This will cancel the move.
- D3D12MA::DefragmentationContext::EndPass() will then free the destination memory
not the source memory of the allocation, leaving it unchanged.
- If you decide the allocation is unimportant and can be destroyed instead of moved (e.g. it wasn't used for long time),
you can set `pass.pMoves[i].Operation` to D3D12MA::DEFRAGMENTATION_MOVE_OPERATION_DESTROY.
- D3D12MA::DefragmentationContext::EndPass() will then free both source and destination memory, and will destroy the source D3D12MA::Allocation object.
You can defragment a specific custom pool by calling D3D12MA::Pool::BeginDefragmentation
or all the default pools by calling D3D12MA::Allocator::BeginDefragmentation (like in the example above).
Defragmentation is always performed in each pool separately.
Allocations are never moved between different heap types.
The size of the destination memory reserved for a moved allocation is the same as the original one.
Alignment of an allocation as it was determined using `GetResourceAllocationInfo()` is also respected after defragmentation.
Buffers/textures should be recreated with the same `D3D12_RESOURCE_DESC` parameters as the original ones.
You can perform the defragmentation incrementally to limit the number of allocations and bytes to be moved
in each pass, e.g. to call it in sync with render frames and not to experience too big hitches.
See members: D3D12MA::DEFRAGMENTATION_DESC::MaxBytesPerPass, D3D12MA::DEFRAGMENTATION_DESC::MaxAllocationsPerPass.
It is also safe to perform the defragmentation asynchronously to render frames and other Direct3D 12 and %D3D12MA
usage, possibly from multiple threads, with the exception that allocations
returned in D3D12MA::DEFRAGMENTATION_PASS_MOVE_INFO::pMoves shouldn't be released until the defragmentation pass is ended.
<b>Mapping</b> is out of scope of this library and so it is not preserved after an allocation is moved during defragmentation.
You need to map the new resource yourself if needed.
\note Defragmentation is not supported in custom pools created with D3D12MA::POOL_FLAG_ALGORITHM_LINEAR.
\page statistics Statistics \page statistics Statistics