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}