1 /*
2 * Copyright © 2017 Google
3 * Copyright © 2019 Red Hat
4 * Copyright © 2024 Igalia S.L.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 * IN THE SOFTWARE.
24 */
25
26 #include <assert.h>
27 #include <errno.h>
28 #include <inttypes.h>
29 #include <limits.h>
30 #include <math.h>
31 #include <stddef.h>
32 #include <stdint.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <vulkan/vk_layer.h>
36 #include <vulkan/vulkan_core.h>
37
38 #include "util/bitscan.h"
39 #include "util/hash_table.h"
40 #include "util/macros.h"
41 #include "util/os_memory.h"
42 #include "util/os_misc.h"
43 #include "util/simple_mtx.h"
44 #include "util/u_memory.h"
45 #include "vk_dispatch_table.h"
46 #include "vk_enum_to_str.h"
47 #include "vk_util.h"
48
49 #define KiB(v) (UINT64_C(1024) * (v))
50 #define MiB(v) (UINT64_C(1024) * KiB(v))
51
52 #define VRAM_REPORT_LIMIT_DEBUG_LOG_TAG "VRAM-REPORT-LIMIT DEBUG: "
53 #define VRAM_REPORT_LIMIT_WARN_LOG_TAG "VRAM-REPORT-LIMIT WARNING: "
54 #define VRAM_REPORT_LIMIT_ERROR_LOG_TAG "VRAM-REPORT-LIMIT ERROR: "
55
56 struct vram_report_limit_instance_data {
57 struct vk_instance_dispatch_table vtable;
58 struct vk_physical_device_dispatch_table pd_vtable;
59 VkInstance instance;
60
61 /* Used to indicate that the heap size is unaffected. I.e. the layer will use
62 * the size reported by the underlying driver.
63 */
64 #define VRAM_REPORT_LIMIT_STATIC_HEAP_SIZE_DEFAULT (0)
65 uint64_t static_heap_size;
66
67 uint32_t active_pdevices_count;
68 struct vram_report_limit_pdevice_data {
69 VkPhysicalDevice pdevice;
70 /* Percentage to scale each device heap's reported budged.
71 * 1.0 is 100%.
72 */
73 long double per_heap_budget_percentage[VK_MAX_MEMORY_HEAPS];
74 } active_pdevices_array[];
75 };
76
77 #define HKEY(obj) ((uint64_t)(obj))
78 #define FIND(type, obj) ((type *)find_object_data(HKEY(obj)))
79
80 static struct hash_table_u64 *vk_object_to_data = NULL;
81 static simple_mtx_t vk_object_to_data_mutex = SIMPLE_MTX_INITIALIZER;
82
83 static inline void
ensure_vk_object_map(void)84 ensure_vk_object_map(void)
85 {
86 if (!vk_object_to_data) {
87 vk_object_to_data = _mesa_hash_table_u64_create(NULL);
88 }
89 }
90
91 static void *
find_object_data(uint64_t obj)92 find_object_data(uint64_t obj)
93 {
94 simple_mtx_lock(&vk_object_to_data_mutex);
95 ensure_vk_object_map();
96 void *data = _mesa_hash_table_u64_search(vk_object_to_data, obj);
97 simple_mtx_unlock(&vk_object_to_data_mutex);
98 return data;
99 }
100
101 static void
map_object(uint64_t obj,void * data)102 map_object(uint64_t obj, void *data)
103 {
104 simple_mtx_lock(&vk_object_to_data_mutex);
105 ensure_vk_object_map();
106 _mesa_hash_table_u64_insert(vk_object_to_data, obj, data);
107 simple_mtx_unlock(&vk_object_to_data_mutex);
108 }
109
110 static void
unmap_object(uint64_t obj)111 unmap_object(uint64_t obj)
112 {
113 simple_mtx_lock(&vk_object_to_data_mutex);
114 _mesa_hash_table_u64_remove(vk_object_to_data, obj);
115 simple_mtx_unlock(&vk_object_to_data_mutex);
116 }
117
118 #define VK_VRAM_REPORT_LIMIT_HEAP_SIZE_ENV_VAR_NAME \
119 "VK_VRAM_REPORT_LIMIT_HEAP_SIZE"
120
121 static uint64_t
vram_report_limit_env_get_static_heap_size_or_default()122 vram_report_limit_env_get_static_heap_size_or_default()
123 {
124 const char *const env_var_value_str =
125 os_get_option(VK_VRAM_REPORT_LIMIT_HEAP_SIZE_ENV_VAR_NAME);
126 if (!env_var_value_str) {
127 goto err_return;
128 }
129
130 const char *start_ptr = env_var_value_str;
131 char *end_ptr;
132
133 errno = 0;
134 const unsigned long long env_var_value =
135 strtoull(env_var_value_str, &end_ptr, 0);
136 if ((env_var_value == 0 && end_ptr == start_ptr) || errno == EINVAL ||
137 errno == ERANGE) {
138 goto err_return;
139 }
140
141 if (env_var_value == 0) {
142 return VRAM_REPORT_LIMIT_STATIC_HEAP_SIZE_DEFAULT;
143 }
144
145 return MiB(env_var_value);
146
147 err_return:
148 fprintf(
149 stderr,
150 VRAM_REPORT_LIMIT_ERROR_LOG_TAG VK_VRAM_REPORT_LIMIT_HEAP_SIZE_ENV_VAR_NAME
151 " is invalid or not set.\n");
152
153 return VRAM_REPORT_LIMIT_STATIC_HEAP_SIZE_DEFAULT;
154 }
155
156 #undef VK_VRAM_REPORT_LIMIT_HEAP_SIZE_ENV_VAR_NAME
157
158 #define VK_VRAM_REPORT_LIMIT_DEVICE_ID_ENV_VAR_NAME \
159 "VK_VRAM_REPORT_LIMIT_DEVICE_ID"
160
161 static bool
vram_report_limit_env_get_device_id(VkVendorId * vendor_id_out,uint32_t * device_id_out)162 vram_report_limit_env_get_device_id(VkVendorId *vendor_id_out,
163 uint32_t *device_id_out)
164 {
165 const char *const env_var_value_str =
166 os_get_option(VK_VRAM_REPORT_LIMIT_DEVICE_ID_ENV_VAR_NAME);
167 if (!env_var_value_str) {
168 goto err_return;
169 }
170
171 char *end_ptr;
172
173 errno = 0;
174 unsigned long val_0 = strtoul(env_var_value_str, &end_ptr, 0);
175 if (errno == EINVAL || errno == ERANGE || end_ptr == env_var_value_str) {
176 goto err_return;
177 }
178
179 char *start_ptr = end_ptr;
180
181 if (*start_ptr != ':') {
182 goto err_return;
183 }
184
185 start_ptr++;
186
187 errno = 0;
188 unsigned long val_1 = strtoul(start_ptr, &end_ptr, 0);
189 if (errno == EINVAL || errno == ERANGE || end_ptr == start_ptr)
190 return false;
191
192 *vendor_id_out = val_0;
193 *device_id_out = val_1;
194
195 return true;
196
197 err_return:
198 fprintf(
199 stderr,
200 VRAM_REPORT_LIMIT_ERROR_LOG_TAG VK_VRAM_REPORT_LIMIT_DEVICE_ID_ENV_VAR_NAME
201 " is invalid or not set.\n");
202 return false;
203 }
204
205 #undef VK_VRAM_REPORT_LIMIT_DEVICE_ID_ENV_VAR_NAME
206
207 static void
vram_report_limit_get_memory_heaps_with_device_property(VkPhysicalDeviceMemoryProperties * memory_properties,uint32_t * heaps_bitmask_out,VkMemoryHeap * heaps_out[static const VK_MAX_MEMORY_HEAPS])208 vram_report_limit_get_memory_heaps_with_device_property(
209 VkPhysicalDeviceMemoryProperties *memory_properties,
210 uint32_t *heaps_bitmask_out,
211 VkMemoryHeap *heaps_out[static const VK_MAX_MEMORY_HEAPS])
212 {
213 uint32_t heaps_bitmask = 0;
214
215 STATIC_ASSERT(sizeof(heaps_bitmask) * CHAR_BIT >= VK_MAX_MEMORY_HEAPS);
216
217 for (uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; i++) {
218 heaps_out[i] = NULL;
219 }
220
221 for (uint32_t i = 0; i < memory_properties->memoryTypeCount; i++) {
222 const VkMemoryType *const memory_type =
223 &memory_properties->memoryTypes[i];
224
225 #if !defined(NDEBUG)
226 const VkMemoryPropertyFlags handled_mem_flags =
227 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
228 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
229 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
230 VK_MEMORY_PROPERTY_HOST_CACHED_BIT |
231 VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT |
232 VK_MEMORY_PROPERTY_PROTECTED_BIT;
233
234 u_foreach_bit (mem_flag,
235 memory_type->propertyFlags & ~handled_mem_flags) {
236 fprintf(stderr,
237 VRAM_REPORT_LIMIT_WARN_LOG_TAG
238 "unhandled VkMemoryPropertyFlagBits: %s\n",
239 vk_MemoryPropertyFlagBits_to_str(mem_flag));
240 }
241 #endif
242
243 const VkMemoryPropertyFlags device_mem_flags =
244 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
245 VK_MEMORY_PROPERTY_PROTECTED_BIT |
246 VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
247
248 if (!(memory_type->propertyFlags & device_mem_flags)) {
249 continue;
250 }
251
252 const uint32_t heap_index = memory_type->heapIndex;
253
254 /* From the Vulkan spec:
255 *
256 * "More than one memory type may share each heap"
257 *
258 * So we don't accidentally want to get the same heap again.
259 */
260 if (heaps_bitmask & BITFIELD_BIT(heap_index)) {
261 continue;
262 }
263
264 heaps_bitmask |= BITFIELD_BIT(heap_index);
265 heaps_out[heap_index] = &memory_properties->memoryHeaps[heap_index];
266 }
267
268 *heaps_bitmask_out = heaps_bitmask;
269 }
270
271 static void
destroy_instance_data(struct vram_report_limit_instance_data * data)272 destroy_instance_data(struct vram_report_limit_instance_data *data)
273 {
274 unmap_object(HKEY(data->instance));
275 os_free_aligned(data);
276 }
277
278 static void
instance_data_unmap_physical_devices(struct vram_report_limit_instance_data * instance_data)279 instance_data_unmap_physical_devices(
280 struct vram_report_limit_instance_data *instance_data)
281 {
282 uint32_t physicalDeviceCount = 0;
283
284 instance_data->vtable.EnumeratePhysicalDevices(instance_data->instance,
285 &physicalDeviceCount, NULL);
286 if (physicalDeviceCount == 0) {
287 return;
288 }
289
290 VkPhysicalDevice *physicalDevices =
291 os_malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount);
292 if (physicalDevices == NULL) {
293 return;
294 }
295
296 instance_data->vtable.EnumeratePhysicalDevices(
297 instance_data->instance, &physicalDeviceCount, physicalDevices);
298 assert(physicalDeviceCount > 0);
299
300 for (uint32_t i = 0; i < physicalDeviceCount; i++) {
301 unmap_object(HKEY(physicalDevices[i]));
302 }
303
304 os_free(physicalDevices);
305 }
306
307 static VkLayerInstanceCreateInfo *
get_instance_chain_info(const VkInstanceCreateInfo * pCreateInfo)308 get_instance_chain_info(const VkInstanceCreateInfo *pCreateInfo)
309 {
310 vk_foreach_struct_const (item, pCreateInfo->pNext) {
311 if (item->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO &&
312 ((VkLayerInstanceCreateInfo *)item)->function == VK_LAYER_LINK_INFO)
313 return (VkLayerInstanceCreateInfo *)item;
314 }
315 unreachable("instance chain info not found");
316 return NULL;
317 }
318
319 static VkResult
vram_report_limit_CreateInstance(const VkInstanceCreateInfo * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkInstance * pInstance)320 vram_report_limit_CreateInstance(const VkInstanceCreateInfo *pCreateInfo,
321 const VkAllocationCallbacks *pAllocator,
322 VkInstance *pInstance)
323 {
324 VkResult result;
325
326 VkLayerInstanceCreateInfo *chain_info = get_instance_chain_info(pCreateInfo);
327
328 assert(chain_info->u.pLayerInfo);
329 PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr =
330 chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
331
332 #define DEFINE_VK_VOID_FUNC_PTR(proc_addr_func, instance, func_name) \
333 CONCAT2(PFN_vk, func_name) \
334 CONCAT2(fp, func_name) = \
335 (CONCAT2(PFN_vk, func_name))proc_addr_func(instance, "vk" #func_name)
336
337 DEFINE_VK_VOID_FUNC_PTR(fpGetInstanceProcAddr, NULL, CreateInstance);
338 if (fpCreateInstance == NULL) {
339 result = VK_ERROR_INITIALIZATION_FAILED;
340 goto err_return;
341 }
342
343 PFN_GetPhysicalDeviceProcAddr fpGetPhysicalDeviceProcAddr =
344 chain_info->u.pLayerInfo->pfnNextGetPhysicalDeviceProcAddr;
345 if (fpGetPhysicalDeviceProcAddr == NULL) {
346 result = VK_ERROR_INITIALIZATION_FAILED;
347 goto err_return;
348 }
349
350 /* Advance the link info for the next element on the chain */
351 chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
352
353 result = fpCreateInstance(pCreateInfo, pAllocator, pInstance);
354 if (result != VK_SUCCESS) {
355 goto err_return;
356 }
357
358 DEFINE_VK_VOID_FUNC_PTR(fpGetInstanceProcAddr, *pInstance, DestroyInstance);
359 if (fpDestroyInstance == NULL) {
360 result = VK_ERROR_INITIALIZATION_FAILED;
361 goto err_return;
362 }
363
364 DEFINE_VK_VOID_FUNC_PTR(fpGetInstanceProcAddr, *pInstance,
365 EnumeratePhysicalDevices);
366 if (fpEnumeratePhysicalDevices == NULL) {
367 result = VK_ERROR_INITIALIZATION_FAILED;
368 goto err_destroy_instance;
369 }
370
371 DEFINE_VK_VOID_FUNC_PTR(fpGetPhysicalDeviceProcAddr, *pInstance,
372 GetPhysicalDeviceProperties);
373 if (fpGetPhysicalDeviceProperties == NULL) {
374 result = VK_ERROR_INITIALIZATION_FAILED;
375 goto err_destroy_instance;
376 }
377
378 #undef DEFINE_VK_VOID_FUNC_PTR
379
380 const uint64_t static_heap_size =
381 vram_report_limit_env_get_static_heap_size_or_default();
382
383 VkVendorId vendor_id = ~0;
384 uint32_t device_id = ~0;
385 const bool device_id_is_valid =
386 vram_report_limit_env_get_device_id(&vendor_id, &device_id);
387
388 uint32_t pdevice_count = 0;
389 fpEnumeratePhysicalDevices(*pInstance, &pdevice_count, NULL);
390
391 VkPhysicalDevice *pdevices_array = NULL;
392 bool *is_pdevice_active_array = NULL;
393 if (pdevice_count > 0) {
394 pdevices_array = os_malloc(sizeof(VkPhysicalDevice) * pdevice_count);
395 if (pdevices_array == NULL) {
396 result = VK_ERROR_OUT_OF_HOST_MEMORY;
397 goto err_destroy_instance;
398 }
399
400 fpEnumeratePhysicalDevices(*pInstance, &pdevice_count, pdevices_array);
401
402 is_pdevice_active_array =
403 (bool *)os_calloc(pdevice_count, sizeof(*is_pdevice_active_array));
404 if (is_pdevice_active_array == NULL) {
405 result = VK_ERROR_OUT_OF_HOST_MEMORY;
406 goto err_free_pdevices_array;
407 }
408 }
409
410 uint32_t active_pdevices_count = 0;
411 if (device_id_is_valid &&
412 static_heap_size != VRAM_REPORT_LIMIT_STATIC_HEAP_SIZE_DEFAULT) {
413 for (uint32_t i = 0; i < pdevice_count; i++) {
414 VkPhysicalDevice pdevice = pdevices_array[i];
415 VkPhysicalDeviceProperties properties;
416
417 is_pdevice_active_array[i] = false;
418
419 fpGetPhysicalDeviceProperties(pdevice, &properties);
420
421 if (properties.vendorID != vendor_id) {
422 continue;
423 }
424
425 if (properties.deviceID != device_id) {
426 continue;
427 }
428
429 #if defined(DEBUG)
430 printf(VRAM_REPORT_LIMIT_DEBUG_LOG_TAG "Active device: %s\n",
431 properties.deviceName);
432 printf(VRAM_REPORT_LIMIT_DEBUG_LOG_TAG "Static Heap size: %lu MiB\n",
433 static_heap_size / MiB(1));
434 #endif
435
436 is_pdevice_active_array[i] = true;
437 active_pdevices_count++;
438 }
439 }
440
441 if (active_pdevices_count == 0 &&
442 static_heap_size != VRAM_REPORT_LIMIT_STATIC_HEAP_SIZE_DEFAULT) {
443 fprintf(stderr, VRAM_REPORT_LIMIT_WARN_LOG_TAG
444 "No device found to apply the limit to.\n");
445 }
446
447 struct vram_report_limit_instance_data *instance_data = os_malloc_aligned(
448 sizeof(*instance_data) + sizeof(instance_data->active_pdevices_array[0]) *
449 active_pdevices_count,
450 CACHE_LINE_SIZE);
451 if (instance_data == NULL) {
452 result = VK_ERROR_OUT_OF_HOST_MEMORY;
453 goto err_free_is_pdevice_active_array;
454 }
455
456 vk_instance_dispatch_table_load(&instance_data->vtable,
457 fpGetInstanceProcAddr, *pInstance);
458 vk_physical_device_dispatch_table_load(&instance_data->pd_vtable,
459 fpGetInstanceProcAddr, *pInstance);
460
461 instance_data->instance = *pInstance;
462 instance_data->static_heap_size = static_heap_size;
463
464 instance_data->active_pdevices_count = 0;
465 for (uint32_t i = 0; i < pdevice_count; i++) {
466 VkPhysicalDevice pdevice = pdevices_array[i];
467 struct vram_report_limit_pdevice_data *pdevice_data;
468
469 /* Even though multiple physical devices have the same vendor id and
470 * device id, they might not have the same heap arrangements due to
471 * potentially differing drivers. So we have to maintain per pdevice
472 * budged percentages and not just calculate it once to be used with
473 * all.
474 */
475 if (!is_pdevice_active_array[i]) {
476 continue;
477 }
478
479 /* No device should be active if the default size is set. */
480 assert(static_heap_size != VRAM_REPORT_LIMIT_STATIC_HEAP_SIZE_DEFAULT);
481
482 pdevice_data =
483 &instance_data
484 ->active_pdevices_array[instance_data->active_pdevices_count];
485 instance_data->active_pdevices_count++;
486
487 pdevice_data->pdevice = pdevice;
488
489 if (instance_data->pd_vtable.GetPhysicalDeviceMemoryProperties2 == NULL) {
490 #if defined(DEBUG)
491 for (uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; i++)
492 pdevice_data->per_heap_budget_percentage[i] = NAN;
493 #endif
494
495 continue;
496 }
497
498 /* For each active device we need to setup a budget percentage to scale
499 * down the reported budget to keep it under the new heap size.
500 */
501 VkPhysicalDeviceMemoryProperties2 memory_properties = {
502 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2,
503 };
504
505 instance_data->pd_vtable.GetPhysicalDeviceMemoryProperties2(
506 pdevice, &memory_properties);
507
508 VkMemoryHeap *heaps_array[VK_MAX_MEMORY_HEAPS];
509 uint32_t heaps_array_bitmask;
510
511 vram_report_limit_get_memory_heaps_with_device_property(
512 &memory_properties.memoryProperties, &heaps_array_bitmask,
513 heaps_array);
514
515 STATIC_ASSERT(ARRAY_SIZE(instance_data->active_pdevices_array[0]
516 .per_heap_budget_percentage) ==
517 VK_MAX_MEMORY_HEAPS);
518 for (uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; i++) {
519 const VkMemoryHeap *const heap = heaps_array[i];
520
521 if (!(BITFIELD_BIT(i) & heaps_array_bitmask)) {
522 pdevice_data->per_heap_budget_percentage[i] = 1.0;
523 continue;
524 }
525
526 assert(static_heap_size != VRAM_REPORT_LIMIT_STATIC_HEAP_SIZE_DEFAULT);
527 const long double ratio =
528 (long double)instance_data->static_heap_size / heap->size;
529
530 pdevice_data->per_heap_budget_percentage[i] = ratio;
531 }
532 }
533
534 map_object(HKEY(instance_data->instance), instance_data);
535
536 for (uint32_t i = 0; i < pdevice_count; i++) {
537 map_object(HKEY(pdevices_array[i]), instance_data);
538 }
539
540 if (is_pdevice_active_array) {
541 os_free(is_pdevice_active_array);
542 }
543
544 if (pdevices_array) {
545 os_free(pdevices_array);
546 }
547
548 return VK_SUCCESS;
549
550 err_free_is_pdevice_active_array:
551 if (is_pdevice_active_array) {
552 os_free(is_pdevice_active_array);
553 }
554
555 err_free_pdevices_array:
556 if (pdevices_array) {
557 os_free(pdevices_array);
558 }
559
560 err_destroy_instance:
561 fpDestroyInstance(*pInstance, NULL);
562
563 err_return:
564 return result;
565 }
566
567 static void
vram_report_limit_DestroyInstance(VkInstance instance,const VkAllocationCallbacks * pAllocator)568 vram_report_limit_DestroyInstance(VkInstance instance,
569 const VkAllocationCallbacks *pAllocator)
570 {
571 struct vram_report_limit_instance_data *const instance_data =
572 FIND(struct vram_report_limit_instance_data, instance);
573
574 instance_data_unmap_physical_devices(instance_data);
575 instance_data->vtable.DestroyInstance(instance, pAllocator);
576
577 destroy_instance_data(instance_data);
578 }
579
580 static inline void
vram_report_limit_apply_budget_percentage(long double percentage,VkDeviceSize * const size_in_out)581 vram_report_limit_apply_budget_percentage(long double percentage,
582 VkDeviceSize *const size_in_out)
583 {
584 const VkDeviceSize old_size = *size_in_out;
585 const VkDeviceSize new_size = (VkDeviceSize)(old_size * percentage);
586
587 #if defined(DEBUG)
588 if (percentage != 1.0) {
589 printf(VRAM_REPORT_LIMIT_DEBUG_LOG_TAG
590 "tweaking budget size to %0.2Lf %%, %" PRIu64 " MiB -> %" PRIu64
591 " MiB\n",
592 percentage * 100, old_size / MiB(1), new_size / MiB(1));
593 }
594 #endif
595
596 *size_in_out = new_size;
597 }
598
599 static void
vram_report_limit_tweak_memory_properties(const struct vram_report_limit_instance_data * instance_data,VkPhysicalDevice pdevice,VkPhysicalDeviceMemoryProperties * memory_properties,VkPhysicalDeviceMemoryBudgetPropertiesEXT * memory_budget_optional)600 vram_report_limit_tweak_memory_properties(
601 const struct vram_report_limit_instance_data *instance_data,
602 VkPhysicalDevice pdevice,
603 VkPhysicalDeviceMemoryProperties *memory_properties,
604 VkPhysicalDeviceMemoryBudgetPropertiesEXT *memory_budget_optional)
605 {
606 if (instance_data->static_heap_size ==
607 VRAM_REPORT_LIMIT_STATIC_HEAP_SIZE_DEFAULT) {
608 return;
609 }
610
611 const struct vram_report_limit_pdevice_data *pdevice_data = NULL;
612 for (uint32_t i = 0; i < instance_data->active_pdevices_count; i++) {
613 if (instance_data->active_pdevices_array[i].pdevice != pdevice) {
614 continue;
615 }
616
617 pdevice_data = &instance_data->active_pdevices_array[i];
618 }
619
620 if (pdevice_data == NULL) {
621 /* The device wasn't selected by the user so don't tweak any values. */
622 return;
623 }
624
625 for (uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; i++) {
626 if (i > memory_properties->memoryHeapCount) {
627 break;
628 }
629
630 assert(instance_data->static_heap_size !=
631 VRAM_REPORT_LIMIT_STATIC_HEAP_SIZE_DEFAULT);
632 memory_properties->memoryHeaps[i].size = instance_data->static_heap_size;
633
634 if (memory_budget_optional) {
635 const long double percentage =
636 pdevice_data->per_heap_budget_percentage[i];
637
638 vram_report_limit_apply_budget_percentage(
639 percentage, &memory_budget_optional->heapBudget[i]);
640
641 assert(memory_budget_optional->heapBudget[i] <=
642 memory_properties->memoryHeaps[i].size);
643 }
644 }
645 }
646
647 static VKAPI_ATTR void VKAPI_CALL
vram_report_limit_GetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice,VkPhysicalDeviceMemoryProperties * pMemoryProperties)648 vram_report_limit_GetPhysicalDeviceMemoryProperties(
649 VkPhysicalDevice physicalDevice,
650 VkPhysicalDeviceMemoryProperties *pMemoryProperties)
651 {
652 struct vram_report_limit_instance_data *instance_data =
653 FIND(struct vram_report_limit_instance_data, physicalDevice);
654
655 instance_data->pd_vtable.GetPhysicalDeviceMemoryProperties(
656 physicalDevice, pMemoryProperties);
657
658 vram_report_limit_tweak_memory_properties(instance_data, physicalDevice,
659 pMemoryProperties, NULL);
660 }
661
662 static VKAPI_ATTR void VKAPI_CALL
vram_report_limit_GetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice,VkPhysicalDeviceMemoryProperties2 * pMemoryProperties)663 vram_report_limit_GetPhysicalDeviceMemoryProperties2(
664 VkPhysicalDevice physicalDevice,
665 VkPhysicalDeviceMemoryProperties2 *pMemoryProperties)
666 {
667 struct vram_report_limit_instance_data *instance_data =
668 FIND(struct vram_report_limit_instance_data, physicalDevice);
669
670 instance_data->pd_vtable.GetPhysicalDeviceMemoryProperties2(
671 physicalDevice, pMemoryProperties);
672
673 struct VkPhysicalDeviceMemoryBudgetPropertiesEXT *budget_properties =
674 vk_find_struct(pMemoryProperties->pNext,
675 PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT);
676
677 vram_report_limit_tweak_memory_properties(
678 instance_data, physicalDevice, &pMemoryProperties->memoryProperties,
679 budget_properties);
680 }
681
682 static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
683 vram_report_limit_GetInstanceProcAddr(VkInstance instance,
684 const char *funcName);
685
686 static void *
find_ptr(const char * name)687 find_ptr(const char *name)
688 {
689 static const struct {
690 const char *name;
691 void *ptr;
692 } name_to_funcptr_map[] = {
693 {"vkGetInstanceProcAddr", (void *)vram_report_limit_GetInstanceProcAddr},
694 #define ADD_HOOK(fn) {"vk" #fn, (void *)vram_report_limit_##fn}
695 #define ADD_ALIAS_HOOK(alias, fn) \
696 { \
697 "vk" #alias, (void *)vram_report_limit_##fn \
698 }
699 ADD_HOOK(GetPhysicalDeviceMemoryProperties),
700 ADD_HOOK(GetPhysicalDeviceMemoryProperties2),
701 ADD_ALIAS_HOOK(GetPhysicalDeviceMemoryProperties2KHR,
702 GetPhysicalDeviceMemoryProperties2),
703
704 ADD_HOOK(CreateInstance),
705 ADD_HOOK(DestroyInstance),
706 #undef ADD_HOOK
707 #undef ADD_ALIAS_HOOK
708 };
709
710 for (uint32_t i = 0; i < ARRAY_SIZE(name_to_funcptr_map); i++) {
711 if (strcmp(name, name_to_funcptr_map[i].name) == 0) {
712 return name_to_funcptr_map[i].ptr;
713 }
714 }
715
716 return NULL;
717 }
718
719 static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
vram_report_limit_GetInstanceProcAddr(VkInstance instance,const char * funcName)720 vram_report_limit_GetInstanceProcAddr(VkInstance instance, const char *funcName)
721 {
722 void *ptr = find_ptr(funcName);
723 if (ptr) {
724 return (PFN_vkVoidFunction)(ptr);
725 }
726
727 if (instance == NULL) {
728 return NULL;
729 }
730
731 struct vram_report_limit_instance_data *instance_data =
732 FIND(struct vram_report_limit_instance_data, instance);
733 if (instance_data->vtable.GetInstanceProcAddr == NULL) {
734 return NULL;
735 }
736
737 return instance_data->vtable.GetInstanceProcAddr(instance, funcName);
738 }
739
740 PUBLIC VkResult
vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface * pVersionStruct)741 vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface *pVersionStruct)
742 {
743 if (pVersionStruct->loaderLayerInterfaceVersion < 2)
744 return VK_ERROR_INITIALIZATION_FAILED;
745
746 pVersionStruct->loaderLayerInterfaceVersion = 2;
747 pVersionStruct->pfnGetInstanceProcAddr =
748 vram_report_limit_GetInstanceProcAddr;
749
750 return VK_SUCCESS;
751 }
752