diff --git a/docs/features.txt b/docs/features.txt index 105f3527b5f..b25f96f0d41 100644 --- a/docs/features.txt +++ b/docs/features.txt @@ -660,7 +660,7 @@ Khronos extensions that are not part of any Vulkan version: VK_EXT_line_rasterization DONE (anv, hasvk, hk, kk, nvk, panvk, pvr, lvp, radv, tu, v3dv, vn) VK_EXT_load_store_op_none DONE (anv, hk, kk, lvp, nvk, panvk, radv, tu, v3dv, vn) VK_EXT_map_memory_placed DONE (anv, hk, nvk, panvk, pvr, radv, tu, vn) - VK_EXT_memory_budget DONE (anv, hasvk, lvp, nvk, panvk, radv, tu, v3dv, vn) + VK_EXT_memory_budget DONE (anv, hasvk, kk, lvp, nvk, panvk, radv, tu, v3dv, vn) VK_EXT_memory_priority DONE (lvp, radv) VK_EXT_mesh_shader DONE (anv/gfx12.5+, lvp, radv, vn) VK_EXT_multi_draw DONE (anv, hasvk, hk, kk, lvp, nvk, radv, tu, vn, v3dv) diff --git a/src/kosmickrisp/bridge/mtl_device.h b/src/kosmickrisp/bridge/mtl_device.h index 9a42b614e28..1e8be397e4f 100644 --- a/src/kosmickrisp/bridge/mtl_device.h +++ b/src/kosmickrisp/bridge/mtl_device.h @@ -32,6 +32,8 @@ bool mtl_device_supports_sample_count(mtl_device *dev, uint32_t sample_count); struct mtl_size mtl_device_max_threads_per_threadgroup(mtl_device *dev); uint32_t mtl_device_max_threadgroup_memory_length(mtl_device *dev); uint64_t mtl_device_max_buffer_length(mtl_device *dev); +uint64_t mtl_device_recommended_max_working_set_size(mtl_device *dev); +uint64_t mtl_device_current_allocated_size(mtl_device *dev); /* Timestamp query */ uint64_t mtl_device_get_gpu_timestamp(mtl_device *dev); diff --git a/src/kosmickrisp/bridge/mtl_device.m b/src/kosmickrisp/bridge/mtl_device.m index f607d2f18cc..ff6ccc35953 100644 --- a/src/kosmickrisp/bridge/mtl_device.m +++ b/src/kosmickrisp/bridge/mtl_device.m @@ -156,6 +156,24 @@ mtl_device_max_buffer_length(mtl_device *dev) } } +uint64_t +mtl_device_recommended_max_working_set_size(mtl_device *dev) +{ + @autoreleasepool { + id device = (id)dev; + return device.recommendedMaxWorkingSetSize; + } +} + +uint64_t +mtl_device_current_allocated_size(mtl_device *dev) +{ + @autoreleasepool { + id device = (id)dev; + return device.currentAllocatedSize; + } +} + /* Timestamp query */ uint64_t mtl_device_get_gpu_timestamp(mtl_device *dev) diff --git a/src/kosmickrisp/bridge/stubs/mtl_device.c b/src/kosmickrisp/bridge/stubs/mtl_device.c index da9accf7285..08c65680e8a 100644 --- a/src/kosmickrisp/bridge/stubs/mtl_device.c +++ b/src/kosmickrisp/bridge/stubs/mtl_device.c @@ -77,6 +77,18 @@ mtl_device_max_buffer_length(mtl_device *dev) return 0u; } +uint64_t +mtl_device_recommended_max_working_set_size(mtl_device *dev) +{ + return 0u; +} + +uint64_t +mtl_device_current_allocated_size(mtl_device *dev) +{ + return 0u; +} + /* Timestamp query */ uint64_t mtl_device_get_gpu_timestamp(mtl_device *dev) diff --git a/src/kosmickrisp/vulkan/kk_device_memory.c b/src/kosmickrisp/vulkan/kk_device_memory.c index 13e1dcc6595..9c11386dc1f 100644 --- a/src/kosmickrisp/vulkan/kk_device_memory.c +++ b/src/kosmickrisp/vulkan/kk_device_memory.c @@ -60,12 +60,10 @@ kk_AllocateMemory(VkDevice device, const VkMemoryAllocateInfo *pAllocateInfo, const VkAllocationCallbacks *pAllocator, VkDeviceMemory *pMem) { VK_FROM_HANDLE(kk_device, dev, device); - struct kk_physical_device *pdev = kk_device_physical(dev); struct kk_device_memory *mem; VkResult result = VK_SUCCESS; const VkImportMemoryMetalHandleInfoEXT *metal_info = vk_find_struct_const( pAllocateInfo->pNext, IMPORT_MEMORY_METAL_HANDLE_INFO_EXT); - const VkMemoryType *type = &pdev->mem_types[pAllocateInfo->memoryTypeIndex]; // TODO_KOSMICKRISP Do the actual memory allocation with alignment requirements uint32_t alignment = (1ULL << 12); @@ -102,9 +100,6 @@ kk_AllocateMemory(VkDevice device, const VkMemoryAllocateInfo *pAllocateInfo, goto fail_alloc; } - struct kk_memory_heap *heap = &pdev->mem_heaps[type->heapIndex]; - p_atomic_add(&heap->used, mem->bo->size_B); - *pMem = kk_device_memory_to_handle(mem); return VK_SUCCESS; @@ -120,15 +115,10 @@ kk_FreeMemory(VkDevice device, VkDeviceMemory _mem, { VK_FROM_HANDLE(kk_device, dev, device); VK_FROM_HANDLE(kk_device_memory, mem, _mem); - struct kk_physical_device *pdev = kk_device_physical(dev); if (!mem) return; - const VkMemoryType *type = &pdev->mem_types[mem->vk.memory_type_index]; - struct kk_memory_heap *heap = &pdev->mem_heaps[type->heapIndex]; - p_atomic_add(&heap->used, -((int64_t)mem->bo->size_B)); - kk_destroy_bo(dev, mem->bo); vk_device_memory_destroy(&dev->vk, pAllocator, &mem->vk); diff --git a/src/kosmickrisp/vulkan/kk_physical_device.c b/src/kosmickrisp/vulkan/kk_physical_device.c index a836bec2c6c..42fbfaa0a04 100644 --- a/src/kosmickrisp/vulkan/kk_physical_device.c +++ b/src/kosmickrisp/vulkan/kk_physical_device.c @@ -149,6 +149,7 @@ kk_get_device_extensions(const struct kk_instance *instance, .EXT_external_memory_metal = true, .EXT_image_2d_view_of_3d = true, .EXT_load_store_op_none = true, + .EXT_memory_budget = true, .EXT_multi_draw = true, .EXT_mutable_descriptor_type = true, .EXT_post_depth_coverage = true, @@ -806,25 +807,80 @@ kk_physical_device_free_disk_cache(struct kk_physical_device *pdev) static uint64_t kk_get_sysmem_heap_size(void) { + /* Report the total amount of system memory as the actual heap size */ uint64_t sysmem_size_B = 0; if (!os_get_total_physical_memory(&sysmem_size_B)) return 0; - /* Use 3/4 of total size to avoid swapping */ - return ROUND_DOWN_TO(sysmem_size_B * 3 / 4, 1 << 20); + return sysmem_size_B; } static uint64_t -kk_get_sysmem_heap_available(struct kk_physical_device *pdev) +kk_get_sysmem_heap_budget(struct kk_physical_device *pdev) { + /* From the Vulkan 1.3.278 spec: + * + * "heapBudget is an array of VK_MAX_MEMORY_HEAPS VkDeviceSize + * values in which memory budgets are returned, with one + * element for each memory heap. A heap’s budget is a rough + * estimate of how much memory the process can allocate from + * that heap before allocations may fail or cause performance + * degradation. The budget includes any currently allocated + * device memory." + * + * and + * + * "The heapBudget value must be less than or equal to + * VkMemoryHeap::size for each heap." + * + * From Metal documentation for recommendedMaxWorkingSetSize: + * + * An approximation of how much memory, in bytes, this GPU device can + * allocate without affecting its runtime performance. + * + * From Metal documentation for currentAllocatedSize: + * + * The total amount of memory, in bytes, the GPU device is using for all + * of its resources. + * + * First, determine the total and available system memory to calculate the + * amount of used memory. Then, subtract this from the Metal-defined budget, + * and add back the current used memory by this device. + */ uint64_t sysmem_size_B = 0; - if (!os_get_available_system_memory(&sysmem_size_B)) { - vk_loge(VK_LOG_OBJS(pdev), "Failed to query available system memory"); + uint64_t sysmem_available_B = 0; + if (!os_get_total_physical_memory(&sysmem_size_B) || + !os_get_available_system_memory(&sysmem_available_B)) return 0; - } - /* Use 3/4 of available to avoid swapping */ - return ROUND_DOWN_TO(sysmem_size_B * 3 / 4, 1 << 20); + uint64_t sysmem_used_B = sysmem_size_B - sysmem_available_B; + uint64_t sysmem_budget_B = + mtl_device_recommended_max_working_set_size(pdev->mtl_dev_handle); + uint64_t remaining_budget_B = sysmem_budget_B > sysmem_used_B ? + sysmem_budget_B - sysmem_used_B : 0u; + return remaining_budget_B + + mtl_device_current_allocated_size(pdev->mtl_dev_handle); +} + +static uint64_t +kk_get_sysmem_heap_used(struct kk_physical_device *pdev) +{ + /* From the Vulkan 1.3.278 spec: + * + * "heapUsage is an array of VK_MAX_MEMORY_HEAPS VkDeviceSize + * values in which memory usages are returned, with one element + * for each memory heap. A heap’s usage is an estimate of how + * much memory the process is currently using in that heap." + * + * From Metal documentation for currentAllocatedSize: + * + * The total amount of memory, in bytes, the GPU device is using for all + * of its resources. + * + * We can trivially report estimated heap usage using Metal's reported + * allocated size + */ + return mtl_device_current_allocated_size(pdev->mtl_dev_handle); } static void @@ -901,7 +957,8 @@ kk_enumerate_physical_devices(struct vk_instance *_instance) pdev->mem_heaps[sysmem_heap_idx] = (struct kk_memory_heap){ .size = sysmem_size_B, .flags = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT, - .available = kk_get_sysmem_heap_available, + .budget = kk_get_sysmem_heap_budget, + .used = kk_get_sysmem_heap_used, }; pdev->mem_types[pdev->mem_type_count++] = (VkMemoryType){ @@ -987,46 +1044,8 @@ kk_GetPhysicalDeviceMemoryProperties2( for (unsigned i = 0; i < pdev->mem_heap_count; i++) { const struct kk_memory_heap *heap = &pdev->mem_heaps[i]; - uint64_t used = p_atomic_read(&heap->used); - - /* From the Vulkan 1.3.278 spec: - * - * "heapUsage is an array of VK_MAX_MEMORY_HEAPS VkDeviceSize - * values in which memory usages are returned, with one element - * for each memory heap. A heap’s usage is an estimate of how - * much memory the process is currently using in that heap." - * - * TODO: Include internal allocations? - */ - p->heapUsage[i] = used; - - uint64_t available = heap->size; - if (heap->available) - available = heap->available(pdev); - - /* From the Vulkan 1.3.278 spec: - * - * "heapBudget is an array of VK_MAX_MEMORY_HEAPS VkDeviceSize - * values in which memory budgets are returned, with one - * element for each memory heap. A heap’s budget is a rough - * estimate of how much memory the process can allocate from - * that heap before allocations may fail or cause performance - * degradation. The budget includes any currently allocated - * device memory." - * - * and - * - * "The heapBudget value must be less than or equal to - * VkMemoryHeap::size for each heap." - * - * available (queried above) is the total amount free memory - * system-wide and does not include our allocations so we need - * to add that in. - */ - uint64_t budget = MIN2(available + used, heap->size); - - /* Set the budget at 90% of available to avoid thrashing */ - p->heapBudget[i] = ROUND_DOWN_TO(budget * 9 / 10, 1 << 20); + p->heapBudget[i] = heap->budget ? heap->budget(pdev) : 0; + p->heapUsage[i] = heap->used ? heap->used(pdev) : 0; } /* From the Vulkan 1.3.278 spec: diff --git a/src/kosmickrisp/vulkan/kk_physical_device.h b/src/kosmickrisp/vulkan/kk_physical_device.h index 91c053a8310..c9dd834185a 100644 --- a/src/kosmickrisp/vulkan/kk_physical_device.h +++ b/src/kosmickrisp/vulkan/kk_physical_device.h @@ -30,9 +30,9 @@ struct kk_queue_family { struct kk_memory_heap { uint64_t size; - uint64_t used; VkMemoryHeapFlags flags; - uint64_t (*available)(struct kk_physical_device *pdev); + uint64_t (*budget)(struct kk_physical_device *pdev); + uint64_t (*used)(struct kk_physical_device *pdev); }; struct kk_device_info {