1bf215546Sopenharmony_ci/* 2bf215546Sopenharmony_ci * Copyright © Microsoft Corporation 3bf215546Sopenharmony_ci * 4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation 7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10bf215546Sopenharmony_ci * 11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next 12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13bf215546Sopenharmony_ci * Software. 14bf215546Sopenharmony_ci * 15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20bf215546Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21bf215546Sopenharmony_ci * IN THE SOFTWARE. 22bf215546Sopenharmony_ci */ 23bf215546Sopenharmony_ci 24bf215546Sopenharmony_ci#include "d3d12_batch.h" 25bf215546Sopenharmony_ci#include "d3d12_bufmgr.h" 26bf215546Sopenharmony_ci#include "d3d12_residency.h" 27bf215546Sopenharmony_ci#include "d3d12_resource.h" 28bf215546Sopenharmony_ci#include "d3d12_screen.h" 29bf215546Sopenharmony_ci 30bf215546Sopenharmony_ci#include "util/os_time.h" 31bf215546Sopenharmony_ci 32bf215546Sopenharmony_ci#include <dxguids/dxguids.h> 33bf215546Sopenharmony_ci 34bf215546Sopenharmony_cistatic constexpr unsigned residency_batch_size = 128; 35bf215546Sopenharmony_ci 36bf215546Sopenharmony_cistatic void 37bf215546Sopenharmony_cievict_aged_allocations(struct d3d12_screen *screen, uint64_t completed_fence, int64_t time, int64_t grace_period) 38bf215546Sopenharmony_ci{ 39bf215546Sopenharmony_ci ID3D12Pageable *to_evict[residency_batch_size]; 40bf215546Sopenharmony_ci unsigned num_pending_evictions = 0; 41bf215546Sopenharmony_ci 42bf215546Sopenharmony_ci list_for_each_entry_safe(struct d3d12_bo, bo, &screen->residency_list, residency_list_entry) { 43bf215546Sopenharmony_ci /* This residency list should all be base bos, not suballocated ones */ 44bf215546Sopenharmony_ci assert(bo->res); 45bf215546Sopenharmony_ci 46bf215546Sopenharmony_ci if (bo->last_used_fence > completed_fence || 47bf215546Sopenharmony_ci time - bo->last_used_timestamp <= grace_period) { 48bf215546Sopenharmony_ci /* List is LRU-sorted, this bo is still in use, so we're done */ 49bf215546Sopenharmony_ci break; 50bf215546Sopenharmony_ci } 51bf215546Sopenharmony_ci 52bf215546Sopenharmony_ci if (bo->residency_status == d3d12_permanently_resident) 53bf215546Sopenharmony_ci continue; 54bf215546Sopenharmony_ci 55bf215546Sopenharmony_ci to_evict[num_pending_evictions++] = bo->res; 56bf215546Sopenharmony_ci bo->residency_status = d3d12_evicted; 57bf215546Sopenharmony_ci list_del(&bo->residency_list_entry); 58bf215546Sopenharmony_ci 59bf215546Sopenharmony_ci if (num_pending_evictions == residency_batch_size) { 60bf215546Sopenharmony_ci screen->dev->Evict(num_pending_evictions, to_evict); 61bf215546Sopenharmony_ci num_pending_evictions = 0; 62bf215546Sopenharmony_ci } 63bf215546Sopenharmony_ci } 64bf215546Sopenharmony_ci 65bf215546Sopenharmony_ci if (num_pending_evictions) 66bf215546Sopenharmony_ci screen->dev->Evict(num_pending_evictions, to_evict); 67bf215546Sopenharmony_ci} 68bf215546Sopenharmony_ci 69bf215546Sopenharmony_cistatic void 70bf215546Sopenharmony_cievict_to_fence_or_budget(struct d3d12_screen *screen, uint64_t target_fence, uint64_t current_usage, uint64_t target_budget) 71bf215546Sopenharmony_ci{ 72bf215546Sopenharmony_ci screen->fence->SetEventOnCompletion(target_fence, nullptr); 73bf215546Sopenharmony_ci 74bf215546Sopenharmony_ci ID3D12Pageable *to_evict[residency_batch_size]; 75bf215546Sopenharmony_ci unsigned num_pending_evictions = 0; 76bf215546Sopenharmony_ci 77bf215546Sopenharmony_ci list_for_each_entry_safe(struct d3d12_bo, bo, &screen->residency_list, residency_list_entry) { 78bf215546Sopenharmony_ci /* This residency list should all be base bos, not suballocated ones */ 79bf215546Sopenharmony_ci assert(bo->res); 80bf215546Sopenharmony_ci 81bf215546Sopenharmony_ci if (bo->last_used_fence > target_fence || current_usage < target_budget) { 82bf215546Sopenharmony_ci break; 83bf215546Sopenharmony_ci } 84bf215546Sopenharmony_ci 85bf215546Sopenharmony_ci if (bo->residency_status == d3d12_permanently_resident) 86bf215546Sopenharmony_ci continue; 87bf215546Sopenharmony_ci 88bf215546Sopenharmony_ci to_evict[num_pending_evictions++] = bo->res; 89bf215546Sopenharmony_ci bo->residency_status = d3d12_evicted; 90bf215546Sopenharmony_ci list_del(&bo->residency_list_entry); 91bf215546Sopenharmony_ci 92bf215546Sopenharmony_ci current_usage -= bo->estimated_size; 93bf215546Sopenharmony_ci 94bf215546Sopenharmony_ci if (num_pending_evictions == residency_batch_size) { 95bf215546Sopenharmony_ci screen->dev->Evict(num_pending_evictions, to_evict); 96bf215546Sopenharmony_ci num_pending_evictions = 0; 97bf215546Sopenharmony_ci } 98bf215546Sopenharmony_ci } 99bf215546Sopenharmony_ci 100bf215546Sopenharmony_ci if (num_pending_evictions) 101bf215546Sopenharmony_ci screen->dev->Evict(num_pending_evictions, to_evict); 102bf215546Sopenharmony_ci} 103bf215546Sopenharmony_ci 104bf215546Sopenharmony_cistatic constexpr int64_t eviction_grace_period_seconds_min = 1; 105bf215546Sopenharmony_cistatic constexpr int64_t eviction_grace_period_seconds_max = 60; 106bf215546Sopenharmony_cistatic constexpr int64_t microseconds_per_second = 1000000; 107bf215546Sopenharmony_cistatic constexpr int64_t eviction_grace_period_microseconds_min = 108bf215546Sopenharmony_ci eviction_grace_period_seconds_min * microseconds_per_second; 109bf215546Sopenharmony_cistatic constexpr int64_t eviction_grace_period_microseconds_max = 110bf215546Sopenharmony_ci eviction_grace_period_seconds_max * microseconds_per_second; 111bf215546Sopenharmony_cistatic constexpr double trim_percentage_usage_threshold = 0.7; 112bf215546Sopenharmony_ci 113bf215546Sopenharmony_cistatic int64_t 114bf215546Sopenharmony_ciget_eviction_grace_period(struct d3d12_memory_info *mem_info) 115bf215546Sopenharmony_ci{ 116bf215546Sopenharmony_ci double pressure = double(mem_info->usage) / double(mem_info->budget); 117bf215546Sopenharmony_ci pressure = MIN2(pressure, 1.0); 118bf215546Sopenharmony_ci 119bf215546Sopenharmony_ci if (pressure > trim_percentage_usage_threshold) { 120bf215546Sopenharmony_ci /* Normalize pressure for the range [0, threshold] */ 121bf215546Sopenharmony_ci pressure = (pressure - trim_percentage_usage_threshold) / (1.0 - trim_percentage_usage_threshold); 122bf215546Sopenharmony_ci /* Linearly interpolate between min and max period based on pressure */ 123bf215546Sopenharmony_ci return (int64_t)((eviction_grace_period_microseconds_max - eviction_grace_period_microseconds_min) * 124bf215546Sopenharmony_ci (1.0 - pressure)) + eviction_grace_period_microseconds_min; 125bf215546Sopenharmony_ci } 126bf215546Sopenharmony_ci 127bf215546Sopenharmony_ci /* Unlimited grace period, essentially don't trim at all */ 128bf215546Sopenharmony_ci return INT64_MAX; 129bf215546Sopenharmony_ci} 130bf215546Sopenharmony_ci 131bf215546Sopenharmony_civoid 132bf215546Sopenharmony_cid3d12_process_batch_residency(struct d3d12_screen *screen, struct d3d12_batch *batch) 133bf215546Sopenharmony_ci{ 134bf215546Sopenharmony_ci d3d12_memory_info mem_info; 135bf215546Sopenharmony_ci screen->get_memory_info(screen, &mem_info); 136bf215546Sopenharmony_ci 137bf215546Sopenharmony_ci uint64_t completed_fence_value = screen->fence->GetCompletedValue(); 138bf215546Sopenharmony_ci uint64_t pending_fence_value = screen->fence_value + 1; 139bf215546Sopenharmony_ci int64_t current_time = os_time_get(); 140bf215546Sopenharmony_ci int64_t grace_period = get_eviction_grace_period(&mem_info); 141bf215546Sopenharmony_ci 142bf215546Sopenharmony_ci /* Gather base bos for the batch */ 143bf215546Sopenharmony_ci uint64_t size_to_make_resident = 0; 144bf215546Sopenharmony_ci set *base_bo_set = _mesa_pointer_set_create(nullptr); 145bf215546Sopenharmony_ci hash_table_foreach(batch->bos, entry) { 146bf215546Sopenharmony_ci struct d3d12_bo *bo = (struct d3d12_bo *)entry->key; 147bf215546Sopenharmony_ci uint64_t offset; 148bf215546Sopenharmony_ci struct d3d12_bo *base_bo = d3d12_bo_get_base(bo, &offset); 149bf215546Sopenharmony_ci 150bf215546Sopenharmony_ci if (base_bo->residency_status == d3d12_evicted) { 151bf215546Sopenharmony_ci bool added = false; 152bf215546Sopenharmony_ci _mesa_set_search_or_add(base_bo_set, base_bo, &added); 153bf215546Sopenharmony_ci assert(!added); 154bf215546Sopenharmony_ci 155bf215546Sopenharmony_ci base_bo->residency_status = d3d12_resident; 156bf215546Sopenharmony_ci size_to_make_resident += base_bo->estimated_size; 157bf215546Sopenharmony_ci list_addtail(&base_bo->residency_list_entry, &screen->residency_list); 158bf215546Sopenharmony_ci } else if (base_bo->last_used_fence != pending_fence_value) { 159bf215546Sopenharmony_ci /* First time seeing this already-resident base bo in this batch */ 160bf215546Sopenharmony_ci list_del(&base_bo->residency_list_entry); 161bf215546Sopenharmony_ci list_addtail(&base_bo->residency_list_entry, &screen->residency_list); 162bf215546Sopenharmony_ci } 163bf215546Sopenharmony_ci 164bf215546Sopenharmony_ci base_bo->last_used_fence = pending_fence_value; 165bf215546Sopenharmony_ci base_bo->last_used_timestamp = current_time; 166bf215546Sopenharmony_ci } 167bf215546Sopenharmony_ci 168bf215546Sopenharmony_ci /* Now that bos referenced by this batch are moved to the end of the LRU, trim it */ 169bf215546Sopenharmony_ci evict_aged_allocations(screen, completed_fence_value, current_time, grace_period); 170bf215546Sopenharmony_ci 171bf215546Sopenharmony_ci /* If there's nothing needing to be made newly resident, we're done once we've trimmed */ 172bf215546Sopenharmony_ci if (base_bo_set->entries == 0) { 173bf215546Sopenharmony_ci _mesa_set_destroy(base_bo_set, nullptr); 174bf215546Sopenharmony_ci return; 175bf215546Sopenharmony_ci } 176bf215546Sopenharmony_ci 177bf215546Sopenharmony_ci uint64_t residency_fence_value_snapshot = screen->residency_fence_value; 178bf215546Sopenharmony_ci 179bf215546Sopenharmony_ci struct set_entry *entry = _mesa_set_next_entry(base_bo_set, nullptr); 180bf215546Sopenharmony_ci uint64_t batch_memory_size = 0; 181bf215546Sopenharmony_ci unsigned batch_count = 0; 182bf215546Sopenharmony_ci ID3D12Pageable *to_make_resident[residency_batch_size]; 183bf215546Sopenharmony_ci while (true) { 184bf215546Sopenharmony_ci /* Refresh memory stats */ 185bf215546Sopenharmony_ci screen->get_memory_info(screen, &mem_info); 186bf215546Sopenharmony_ci 187bf215546Sopenharmony_ci int64_t available_memory = (int64_t)mem_info.budget - (int64_t)mem_info.usage; 188bf215546Sopenharmony_ci 189bf215546Sopenharmony_ci assert(!list_is_empty(&screen->residency_list)); 190bf215546Sopenharmony_ci struct d3d12_bo *oldest_resident_bo = 191bf215546Sopenharmony_ci list_first_entry(&screen->residency_list, struct d3d12_bo, residency_list_entry); 192bf215546Sopenharmony_ci bool anything_to_wait_for = oldest_resident_bo->last_used_fence < pending_fence_value; 193bf215546Sopenharmony_ci 194bf215546Sopenharmony_ci /* We've got some room, or we can't free up any more room, make some resources resident */ 195bf215546Sopenharmony_ci HRESULT hr = S_OK; 196bf215546Sopenharmony_ci if ((available_memory || !anything_to_wait_for) && batch_count < residency_batch_size) { 197bf215546Sopenharmony_ci for (; entry; entry = _mesa_set_next_entry(base_bo_set, entry)) { 198bf215546Sopenharmony_ci struct d3d12_bo *bo = (struct d3d12_bo *)entry->key; 199bf215546Sopenharmony_ci if (anything_to_wait_for && 200bf215546Sopenharmony_ci (int64_t)(batch_memory_size + bo->estimated_size) > available_memory) 201bf215546Sopenharmony_ci break; 202bf215546Sopenharmony_ci 203bf215546Sopenharmony_ci batch_memory_size += bo->estimated_size; 204bf215546Sopenharmony_ci to_make_resident[batch_count++] = bo->res; 205bf215546Sopenharmony_ci if (batch_count == residency_batch_size) 206bf215546Sopenharmony_ci break; 207bf215546Sopenharmony_ci } 208bf215546Sopenharmony_ci 209bf215546Sopenharmony_ci if (batch_count) { 210bf215546Sopenharmony_ci hr = screen->dev->EnqueueMakeResident(D3D12_RESIDENCY_FLAG_NONE, batch_count, to_make_resident, 211bf215546Sopenharmony_ci screen->residency_fence, screen->residency_fence_value + 1); 212bf215546Sopenharmony_ci if (SUCCEEDED(hr)) 213bf215546Sopenharmony_ci ++screen->residency_fence_value; 214bf215546Sopenharmony_ci } 215bf215546Sopenharmony_ci 216bf215546Sopenharmony_ci if (SUCCEEDED(hr) && batch_count == residency_batch_size) { 217bf215546Sopenharmony_ci batch_count = 0; 218bf215546Sopenharmony_ci size_to_make_resident -= batch_memory_size; 219bf215546Sopenharmony_ci continue; 220bf215546Sopenharmony_ci } 221bf215546Sopenharmony_ci } 222bf215546Sopenharmony_ci 223bf215546Sopenharmony_ci /* We need to free up some space, either we broke early from the resource loop, 224bf215546Sopenharmony_ci * or the MakeResident call itself failed. 225bf215546Sopenharmony_ci */ 226bf215546Sopenharmony_ci if (FAILED(hr) || entry) { 227bf215546Sopenharmony_ci if (!anything_to_wait_for) { 228bf215546Sopenharmony_ci assert(false); 229bf215546Sopenharmony_ci break; 230bf215546Sopenharmony_ci } 231bf215546Sopenharmony_ci 232bf215546Sopenharmony_ci evict_to_fence_or_budget(screen, oldest_resident_bo->last_used_fence, mem_info.usage + size_to_make_resident, mem_info.budget); 233bf215546Sopenharmony_ci continue; 234bf215546Sopenharmony_ci } 235bf215546Sopenharmony_ci 236bf215546Sopenharmony_ci /* Made it to the end without explicitly needing to loop, so we're done */ 237bf215546Sopenharmony_ci break; 238bf215546Sopenharmony_ci } 239bf215546Sopenharmony_ci _mesa_set_destroy(base_bo_set, nullptr); 240bf215546Sopenharmony_ci 241bf215546Sopenharmony_ci /* The GPU needs to wait for these resources to be made resident */ 242bf215546Sopenharmony_ci if (residency_fence_value_snapshot != screen->residency_fence_value) 243bf215546Sopenharmony_ci screen->cmdqueue->Wait(screen->residency_fence, screen->residency_fence_value); 244bf215546Sopenharmony_ci} 245bf215546Sopenharmony_ci 246bf215546Sopenharmony_cibool 247bf215546Sopenharmony_cid3d12_init_residency(struct d3d12_screen *screen) 248bf215546Sopenharmony_ci{ 249bf215546Sopenharmony_ci list_inithead(&screen->residency_list); 250bf215546Sopenharmony_ci if (FAILED(screen->dev->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&screen->residency_fence)))) 251bf215546Sopenharmony_ci return false; 252bf215546Sopenharmony_ci 253bf215546Sopenharmony_ci return true; 254bf215546Sopenharmony_ci} 255bf215546Sopenharmony_ci 256bf215546Sopenharmony_civoid 257bf215546Sopenharmony_cid3d12_deinit_residency(struct d3d12_screen *screen) 258bf215546Sopenharmony_ci{ 259bf215546Sopenharmony_ci if (screen->residency_fence) { 260bf215546Sopenharmony_ci screen->residency_fence->Release(); 261bf215546Sopenharmony_ci screen->residency_fence = nullptr; 262bf215546Sopenharmony_ci } 263bf215546Sopenharmony_ci} 264bf215546Sopenharmony_ci 265bf215546Sopenharmony_civoid 266bf215546Sopenharmony_cid3d12_promote_to_permanent_residency(struct d3d12_screen *screen, struct d3d12_resource* resource) 267bf215546Sopenharmony_ci{ 268bf215546Sopenharmony_ci mtx_lock(&screen->submit_mutex); 269bf215546Sopenharmony_ci uint64_t offset; 270bf215546Sopenharmony_ci struct d3d12_bo *base_bo = d3d12_bo_get_base(resource->bo, &offset); 271bf215546Sopenharmony_ci 272bf215546Sopenharmony_ci /* Promote non-permanent resident resources to permanent residency*/ 273bf215546Sopenharmony_ci if(base_bo->residency_status != d3d12_permanently_resident) { 274bf215546Sopenharmony_ci 275bf215546Sopenharmony_ci /* Mark as permanently resident*/ 276bf215546Sopenharmony_ci base_bo->residency_status = d3d12_permanently_resident; 277bf215546Sopenharmony_ci 278bf215546Sopenharmony_ci /* If it wasn't made resident before, make it*/ 279bf215546Sopenharmony_ci bool was_made_resident = (base_bo->residency_status == d3d12_resident); 280bf215546Sopenharmony_ci if(!was_made_resident) { 281bf215546Sopenharmony_ci list_addtail(&base_bo->residency_list_entry, &screen->residency_list); 282bf215546Sopenharmony_ci ID3D12Pageable *pageable = base_bo->res; 283bf215546Sopenharmony_ci HRESULT hr = screen->dev->MakeResident(1, &pageable); 284bf215546Sopenharmony_ci assert(SUCCEEDED(hr)); 285bf215546Sopenharmony_ci } 286bf215546Sopenharmony_ci } 287bf215546Sopenharmony_ci mtx_unlock(&screen->submit_mutex); 288bf215546Sopenharmony_ci}