vkd3d: Add a memory budget per memory type.

For resizable BAR, we don't want to endlessly promote UPLOAD heaps to
BAR since VRAM is precious. The aim is to set a fixed budget where we
can keep allocating until full, at which point we fall back to plain HOST.

Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
This commit is contained in:
Hans-Kristian Arntzen 2021-07-14 15:12:59 +02:00
parent e0451bb541
commit abdaeb136d
4 changed files with 64 additions and 4 deletions

View File

@ -2529,6 +2529,7 @@ static void d3d12_device_destroy(struct d3d12_device *device)
vkd3d_private_store_destroy(&device->private_store);
vkd3d_cleanup_format_info(device);
vkd3d_memory_info_cleanup(&device->memory_info, device);
vkd3d_shader_debug_ring_cleanup(&device->debug_ring, device);
d3d12_device_global_pipeline_cache_cleanup(device);
vkd3d_sampler_state_cleanup(&device->sampler_state, device);
@ -5229,7 +5230,7 @@ static HRESULT d3d12_device_init(struct d3d12_device *device,
goto out_cleanup_format_info;
if (FAILED(hr = vkd3d_bindless_state_init(&device->bindless_state, device)))
goto out_cleanup_format_info;
goto out_cleanup_memory_info;
if (FAILED(hr = vkd3d_view_map_init(&device->sampler_map)))
goto out_cleanup_bindless_state;
@ -5273,6 +5274,8 @@ out_cleanup_view_map:
vkd3d_view_map_destroy(&device->sampler_map, device);
out_cleanup_bindless_state:
vkd3d_bindless_state_cleanup(&device->bindless_state, device);
out_cleanup_memory_info:
vkd3d_memory_info_cleanup(&device->memory_info, device);
out_cleanup_format_info:
vkd3d_cleanup_format_info(device);
out_free_memory_allocator:

View File

@ -143,17 +143,34 @@ static HRESULT vkd3d_create_global_buffer(struct d3d12_device *device, VkDeviceS
void vkd3d_free_device_memory(struct d3d12_device *device, const struct vkd3d_device_memory_allocation *allocation)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkDeviceSize *type_current;
bool budget_sensitive;
VK_CALL(vkFreeMemory(device->vk_device, allocation->vk_memory, NULL));
budget_sensitive = !!(device->memory_info.budget_sensitive_mask & (1u << allocation->vk_memory_type));
if (budget_sensitive)
{
type_current = &device->memory_info.type_current[allocation->vk_memory_type];
pthread_mutex_lock(&device->memory_info.budget_lock);
assert(*type_current >= allocation->size);
*type_current -= allocation->size;
pthread_mutex_unlock(&device->memory_info.budget_lock);
}
}
static HRESULT vkd3d_try_allocate_device_memory(struct d3d12_device *device,
VkDeviceSize size, VkMemoryPropertyFlags type_flags, uint32_t type_mask,
void *pNext, struct vkd3d_device_memory_allocation *allocation)
{
const VkPhysicalDeviceMemoryProperties *memory_props = &device->memory_properties;
const VkMemoryPropertyFlags optional_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
const VkPhysicalDeviceMemoryProperties *memory_info = &device->memory_properties;
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
struct vkd3d_memory_info *memory_info = &device->memory_info;
VkMemoryAllocateInfo allocate_info;
VkDeviceSize *type_current;
VkDeviceSize *type_budget;
bool budget_sensitive;
VkResult vr;
/* buffer_mask / sampled_mask etc will generally take care of this,
* but for certain fallback scenarios where we select other memory
@ -168,12 +185,36 @@ static HRESULT vkd3d_try_allocate_device_memory(struct d3d12_device *device,
{
uint32_t type_index = vkd3d_bitmask_iter32(&type_mask);
if ((memory_info->memoryTypes[type_index].propertyFlags & type_flags) != type_flags)
if ((memory_props->memoryTypes[type_index].propertyFlags & type_flags) != type_flags)
continue;
allocate_info.memoryTypeIndex = type_index;
if (VK_CALL(vkAllocateMemory(device->vk_device, &allocate_info, NULL, &allocation->vk_memory)) == VK_SUCCESS)
budget_sensitive = !!(device->memory_info.budget_sensitive_mask & (1u << type_index));
if (budget_sensitive)
{
type_budget = &memory_info->type_budget[type_index];
type_current = &memory_info->type_current[type_index];
pthread_mutex_lock(&memory_info->budget_lock);
if (*type_current + size > *type_budget)
{
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;
}
}
vr = VK_CALL(vkAllocateMemory(device->vk_device, &allocate_info, NULL, &allocation->vk_memory));
if (budget_sensitive)
{
if (vr == VK_SUCCESS)
*type_current += size;
pthread_mutex_unlock(&memory_info->budget_lock);
}
if (vr == VK_SUCCESS)
{
allocation->vk_memory_type = type_index;
allocation->size = size;

View File

@ -5971,6 +5971,12 @@ static uint32_t vkd3d_memory_info_find_global_mask(struct d3d12_device *device)
return ~mask;
}
void vkd3d_memory_info_cleanup(struct vkd3d_memory_info *info,
struct d3d12_device *device)
{
pthread_mutex_destroy(&info->budget_lock);
}
HRESULT vkd3d_memory_info_init(struct vkd3d_memory_info *info,
struct d3d12_device *device)
{
@ -5991,6 +5997,9 @@ HRESULT vkd3d_memory_info_init(struct vkd3d_memory_info *info,
info->global_mask = vkd3d_memory_info_find_global_mask(device);
if (pthread_mutex_init(&info->budget_lock, NULL) != 0)
return E_OUTOFMEMORY;
memset(&buffer_info, 0, sizeof(buffer_info));
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buffer_info.size = 65536;

View File

@ -2226,6 +2226,11 @@ struct vkd3d_memory_info_domain
uint32_t buffer_type_mask;
uint32_t sampled_type_mask;
uint32_t rt_ds_type_mask;
uint32_t budget_sensitive_mask;
VkDeviceSize type_budget[VK_MAX_MEMORY_TYPES];
VkDeviceSize type_current[VK_MAX_MEMORY_TYPES];
pthread_mutex_t budget_lock;
};
struct vkd3d_memory_info
@ -2245,6 +2250,8 @@ struct vkd3d_memory_info
HRESULT vkd3d_memory_info_init(struct vkd3d_memory_info *info,
struct d3d12_device *device);
void vkd3d_memory_info_cleanup(struct vkd3d_memory_info *info,
struct d3d12_device *device);
/* meta operations */
struct vkd3d_clear_uav_args