diff --git a/libs/vkd3d/memory.c b/libs/vkd3d/memory.c index c2f0a04a..65098215 100644 --- a/libs/vkd3d/memory.c +++ b/libs/vkd3d/memory.c @@ -201,7 +201,12 @@ static HRESULT vkd3d_try_allocate_device_memory(struct d3d12_device *device, WARN("Attempting to allocate from memory type %u, but exceeding fixed budget: %"PRIu64" + %"PRIu64" > %"PRIu64".\n", type_index, *type_current, size, *type_budget); pthread_mutex_unlock(&memory_info->budget_lock); - continue; + + /* If we're out of DEVICE budget, don't try other types. */ + if (type_flags & optional_flags) + return E_OUTOFMEMORY; + else + continue; } } diff --git a/libs/vkd3d/resource.c b/libs/vkd3d/resource.c index 3fd3cf92..4d781280 100644 --- a/libs/vkd3d/resource.c +++ b/libs/vkd3d/resource.c @@ -5983,6 +5983,60 @@ static uint32_t vkd3d_memory_info_find_global_mask(const struct vkd3d_memory_top return ~mask; } +static void vkd3d_memory_info_init_budgets(struct vkd3d_memory_info *info, + const struct vkd3d_memory_topology *topology, + struct d3d12_device *device) +{ + bool heap_index_needs_budget; + VkMemoryPropertyFlags flags; + uint32_t heap_index; + uint32_t i; + + info->budget_sensitive_mask = 0; + + /* Nothing to do if we don't have separate heaps. */ + if (topology->device_local_heap_count == 0 || topology->host_only_heap_count == 0) + return; + if (!topology->exists_device_only_type || !topology->exists_host_only_type) + return; + + for (i = 0; i < device->memory_properties.memoryTypeCount; i++) + { + const VkMemoryPropertyFlags pinned_mask = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + + flags = device->memory_properties.memoryTypes[i].propertyFlags; + heap_index = device->memory_properties.memoryTypes[i].heapIndex; + + /* Work around a driver workaround on NV drivers which targets certain + * older DXVK versions (use of DXVK DXGI is likely what impacts us here), + * since we don't see this behavior in native builds. + * Even with resizable BAR, we might observe two different heaps, + * with very slightly different heap sizes. + * It's straight forward to be universally robust against these kinds of scenarios, + * so just go for that. + * If we're within 75% of the actual VRAM size, assume we've hit this scenario. + * This should exclude small BAR from explicit budget, since that's just 256 MB. */ + heap_index_needs_budget = + (device->memory_properties.memoryHeaps[heap_index].size > + 3 * device->memory_properties.memoryHeaps[topology->largest_device_local_heap_index].size / 4) && + (device->memory_properties.memoryHeaps[heap_index].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT); + + if ((flags & pinned_mask) == pinned_mask && heap_index_needs_budget) + { + /* Limit this type. This limit is a pure heuristic and we might need further tuning here. + * If there's a separate heap type for PCI-e BAR, + * don't bother limiting it since the size is already going to be tiny. + * The driver will limit us naturally. */ + info->budget_sensitive_mask |= 1u << i; + info->type_budget[i] = device->memory_properties.memoryHeaps[heap_index].size / 16; + info->type_current[i] = 0; + } + } + + INFO("Applying resizable BAR budget to memory types: 0x%x.\n", info->budget_sensitive_mask); +} + void vkd3d_memory_info_cleanup(struct vkd3d_memory_info *info, struct d3d12_device *device) { @@ -6010,6 +6064,7 @@ HRESULT vkd3d_memory_info_init(struct vkd3d_memory_info *info, vkd3d_memory_info_get_topology(&topology, device); info->global_mask = vkd3d_memory_info_find_global_mask(&topology, device); + vkd3d_memory_info_init_budgets(info, &topology, device); if (pthread_mutex_init(&info->budget_lock, NULL) != 0) return E_OUTOFMEMORY;