vkd3d: Recycle command pools.

Elden Ring in particular spam frees and allocates command pools despite
this being a very bad idea.

Add a simple 8-entry cache which seems to take care of it.

Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
This commit is contained in:
Hans-Kristian Arntzen 2022-02-25 11:50:53 +01:00
parent 4b07535909
commit 54fbadcc94
3 changed files with 67 additions and 6 deletions

View File

@ -1587,7 +1587,31 @@ static ULONG STDMETHODCALLTYPE d3d12_command_allocator_Release(ID3D12CommandAllo
vkd3d_free(allocator->framebuffers);
vkd3d_free(allocator->passes);
/* All command buffers are implicitly freed when a pool is destroyed. */
if (pthread_mutex_lock(&device->mutex) == 0)
{
if (device->cached_command_allocator_count < ARRAY_SIZE(device->cached_command_allocators))
{
/* Recycle the pool. Some games spam free/allocate pools,
* even if it completely goes against the point of the API. */
/* Have to free command buffers here if we're going to recycle,
* otherwise DestroyCommandPool takes care of it. */
VK_CALL(vkFreeCommandBuffers(device->vk_device, allocator->vk_command_pool,
allocator->command_buffer_count, allocator->command_buffers));
VK_CALL(vkResetCommandPool(device->vk_device, allocator->vk_command_pool, 0));
device->cached_command_allocators[device->cached_command_allocator_count].vk_command_pool =
allocator->vk_command_pool;
device->cached_command_allocators[device->cached_command_allocator_count].vk_family_index =
allocator->vk_family_index;
device->cached_command_allocator_count++;
allocator->vk_command_pool = VK_NULL_HANDLE;
}
pthread_mutex_unlock(&device->mutex);
}
/* Command buffers are implicitly freed when destroying the pool. */
vkd3d_free(allocator->command_buffers);
VK_CALL(vkDestroyCommandPool(device->vk_device, allocator->vk_command_pool, NULL));
@ -1796,6 +1820,7 @@ static HRESULT d3d12_command_allocator_init(struct d3d12_command_allocator *allo
VkCommandPoolCreateInfo command_pool_info;
VkResult vr;
HRESULT hr;
size_t i;
if (FAILED(hr = vkd3d_private_store_init(&allocator->private_store)))
return hr;
@ -1815,12 +1840,34 @@ static HRESULT d3d12_command_allocator_init(struct d3d12_command_allocator *allo
command_pool_info.flags = 0;
command_pool_info.queueFamilyIndex = queue_family->vk_family_index;
if ((vr = VK_CALL(vkCreateCommandPool(device->vk_device, &command_pool_info, NULL,
&allocator->vk_command_pool))) < 0)
allocator->vk_command_pool = VK_NULL_HANDLE;
allocator->vk_family_index = queue_family->vk_family_index;
/* Try to recycle command allocators. Some games spam free/allocate pools. */
if (pthread_mutex_lock(&device->mutex) == 0)
{
WARN("Failed to create Vulkan command pool, vr %d.\n", vr);
vkd3d_private_store_destroy(&allocator->private_store);
return hresult_from_vk_result(vr);
for (i = 0; i < device->cached_command_allocator_count; i++)
{
if (device->cached_command_allocators[i].vk_family_index == queue_family->vk_family_index)
{
allocator->vk_command_pool = device->cached_command_allocators[i].vk_command_pool;
device->cached_command_allocators[i] =
device->cached_command_allocators[--device->cached_command_allocator_count];
break;
}
}
pthread_mutex_unlock(&device->mutex);
}
if (allocator->vk_command_pool == VK_NULL_HANDLE)
{
if ((vr = VK_CALL(vkCreateCommandPool(device->vk_device, &command_pool_info, NULL,
&allocator->vk_command_pool))) < 0)
{
WARN("Failed to create Vulkan command pool, vr %d.\n", vr);
vkd3d_private_store_destroy(&allocator->private_store);
return hresult_from_vk_result(vr);
}
}
memset(allocator->descriptor_pool_caches, 0, sizeof(allocator->descriptor_pool_caches));

View File

@ -2717,6 +2717,9 @@ static void d3d12_device_destroy(struct d3d12_device *device)
for (i = 0; i < device->query_pool_count; i++)
d3d12_device_destroy_query_pool(device, &device->query_pools[i]);
for (i = 0; i < device->cached_command_allocator_count; i++)
VK_CALL(vkDestroyCommandPool(device->vk_device, device->cached_command_allocators[i].vk_command_pool, NULL));
vkd3d_free(device->descriptor_heap_gpu_vas);
vkd3d_private_store_destroy(&device->private_store);

View File

@ -1773,6 +1773,7 @@ struct d3d12_command_allocator
D3D12_COMMAND_LIST_TYPE type;
VkQueueFlags vk_queue_flags;
uint32_t vk_family_index;
VkCommandPool vk_command_pool;
@ -2881,6 +2882,13 @@ struct vkd3d_queue_family_info
VkQueueFlags vk_queue_flags;
};
#define VKD3D_CACHED_COMMAND_ALLOCATOR_COUNT 8
struct vkd3d_cached_command_allocator
{
VkCommandPool vk_command_pool;
uint32_t vk_family_index;
};
/* ID3D12Device */
typedef ID3D12Device9 d3d12_device_iface;
@ -2931,6 +2939,9 @@ struct d3d12_device
struct vkd3d_query_pool query_pools[VKD3D_VIRTUAL_QUERY_POOL_COUNT];
size_t query_pool_count;
struct vkd3d_cached_command_allocator cached_command_allocators[VKD3D_CACHED_COMMAND_ALLOCATOR_COUNT];
size_t cached_command_allocator_count;
uint32_t *descriptor_heap_gpu_vas;
size_t descriptor_heap_gpu_va_count;
size_t descriptor_heap_gpu_va_size;