From 2d22317f31dda671dc12505c691a44179242ca0c Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Thu, 27 Aug 2020 12:31:04 +0200 Subject: [PATCH] vkd3d: Never attempt to use PCI-pinned memory types. These memory types might end up being used as fallback memory types, which is problematic due to their tiny sizes, and unexpected performance behavior. Generally, when we want to fallback, we should cleanly fall back to system memory rather than a different device local type. Signed-off-by: Hans-Kristian Arntzen --- libs/vkd3d/resource.c | 103 +++++++++++++++++++++++++++++++++++++ libs/vkd3d/vkd3d_private.h | 1 + 2 files changed, 104 insertions(+) diff --git a/libs/vkd3d/resource.c b/libs/vkd3d/resource.c index fb0db4d0..cd58a1cb 100644 --- a/libs/vkd3d/resource.c +++ b/libs/vkd3d/resource.c @@ -123,6 +123,10 @@ static HRESULT vkd3d_try_allocate_memory(struct d3d12_device *device, VkResult vr; uint32_t i; + /* buffer_mask / sampled_mask etc will generally take care of this, but for certain fallback scenarios + * where we select other memory types, we need to mask here as well. */ + type_mask &= device->memory_info.global_mask; + allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocate_info.pNext = pNext; allocate_info.allocationSize = size; @@ -6084,6 +6088,99 @@ void vkd3d_destroy_null_resources(struct vkd3d_null_resources *null_resources, memset(null_resources, 0, sizeof(*null_resources)); } +static uint32_t vkd3d_memory_info_find_global_mask(struct d3d12_device *device) +{ + /* Never allow memory types from any PCI-pinned heap. + * If we allow it, it might end up being used as a fallback memory type, which will cause severe instabilities. + * These types should only be used in a controlled fashion. */ + VkDeviceSize largest_device_local_heap_size = 0; + VkDeviceSize largest_host_only_heap_size = 0; + uint32_t largest_device_local_heap_index = 0; + uint32_t largest_host_only_heap_index = 0; + uint32_t device_local_heap_count = 0; + uint32_t host_only_heap_count = 0; + bool exists_device_only_type; + VkMemoryPropertyFlags flags; + bool exists_host_only_type; + VkDeviceSize heap_size; + uint32_t heap_index; + uint32_t i, mask; + + for (i = 0; i < device->memory_properties.memoryHeapCount; i++) + { + heap_size = device->memory_properties.memoryHeaps[i].size; + if (device->memory_properties.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) + { + if (heap_size > largest_device_local_heap_size) + { + largest_device_local_heap_index = i; + largest_device_local_heap_size = heap_size; + } + device_local_heap_count++; + } + else + { + if (heap_size > largest_host_only_heap_size) + { + largest_host_only_heap_index = i; + largest_host_only_heap_size = heap_size; + } + host_only_heap_count++; + } + } + + /* If we only have one device local heap, or no host-only heaps, there is nothing to do. */ + if (device_local_heap_count <= 1 || host_only_heap_count == 0) + return UINT32_MAX; + + /* Verify that there exists a DEVICE_LOCAL type that is not HOST_VISIBLE on this device + * which maps to the largest device local heap. That way, it is safe to mask out all memory types which are + * DEVICE_LOCAL | HOST_VISIBLE. + * Similarly, there must exist a host-only type. */ + exists_device_only_type = false; + exists_host_only_type = false; + for (i = 0; i < device->memory_properties.memoryTypeCount; i++) + { + flags = device->memory_properties.memoryTypes[i].propertyFlags; + heap_index = device->memory_properties.memoryTypes[i].heapIndex; + + if (heap_index == largest_device_local_heap_index && + (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 && + (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) + { + exists_device_only_type = true; + } + else if (heap_index == largest_host_only_heap_index && + (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0 && + (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) + { + exists_host_only_type = true; + } + } + + if (!exists_device_only_type || !exists_host_only_type) + return UINT32_MAX; + + /* Mask out any memory types which are deemed problematic. */ + for (i = 0, mask = 0; i < device->memory_properties.memoryTypeCount; i++) + { + flags = device->memory_properties.memoryTypes[i].propertyFlags; + heap_index = device->memory_properties.memoryTypes[i].propertyFlags; + const VkMemoryPropertyFlags pinned_mask = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + + if (heap_index != largest_device_local_heap_index && + heap_index != largest_host_only_heap_index && + (flags & pinned_mask) == pinned_mask) + { + mask |= 1u << i; + WARN("Blocking memory type %u for use (PCI-pinned memory).\n", i); + } + } + + return ~mask; +} + HRESULT vkd3d_memory_info_init(struct vkd3d_memory_info *info, struct d3d12_device *device) { @@ -6095,6 +6192,8 @@ HRESULT vkd3d_memory_info_init(struct vkd3d_memory_info *info, VkImage image; VkResult vr; + info->global_mask = vkd3d_memory_info_find_global_mask(device); + memset(&buffer_info, 0, sizeof(buffer_info)); buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buffer_info.size = 65536; @@ -6179,6 +6278,10 @@ HRESULT vkd3d_memory_info_init(struct vkd3d_memory_info *info, VK_CALL(vkDestroyImage(device->vk_device, image, NULL)); info->rt_ds_type_mask &= memory_requirements.memoryTypeBits; + info->buffer_type_mask &= info->global_mask; + info->sampled_type_mask &= info->global_mask; + info->rt_ds_type_mask &= info->global_mask; + TRACE("Device supports buffers on memory types 0x%#x.\n", info->buffer_type_mask); TRACE("Device supports textures on memory types 0x%#x.\n", info->sampled_type_mask); TRACE("Device supports render targets on memory types 0x%#x.\n", info->rt_ds_type_mask); diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h index 86497af0..c6f15472 100644 --- a/libs/vkd3d/vkd3d_private.h +++ b/libs/vkd3d/vkd3d_private.h @@ -1549,6 +1549,7 @@ struct vkd3d_format_compatibility_list struct vkd3d_memory_info { + uint32_t global_mask; uint32_t buffer_type_mask; uint32_t sampled_type_mask; uint32_t rt_ds_type_mask;