From afb85c79cd83d73258d15aa51ac2c3d3869dbe43 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Tue, 24 Nov 2020 12:59:15 +0100 Subject: [PATCH] vkd3d: Add code to create, destroy and recycle scratch buffers. Command lists may need to allocate temporary device memory for certain operations. In order to avoid frequent alloc/free calls, we'll recycle these scratch buffers until a certain threshold. Signed-off-by: Philip Rebohle --- libs/vkd3d/command.c | 70 +++++++++++++++++++++++++++ libs/vkd3d/device.c | 96 ++++++++++++++++++++++++++++++++++++++ libs/vkd3d/vkd3d_private.h | 22 +++++++++ 3 files changed, 188 insertions(+) diff --git a/libs/vkd3d/command.c b/libs/vkd3d/command.c index b158e990..f11e856c 100644 --- a/libs/vkd3d/command.c +++ b/libs/vkd3d/command.c @@ -1492,6 +1492,10 @@ static ULONG STDMETHODCALLTYPE d3d12_command_allocator_Release(ID3D12CommandAllo vkd3d_free(allocator->command_buffers); VK_CALL(vkDestroyCommandPool(device->vk_device, allocator->vk_command_pool, NULL)); + for (i = 0; i < allocator->scratch_buffer_count; i++) + d3d12_device_return_scratch_buffer(device, &allocator->scratch_buffers[i]); + + vkd3d_free(allocator->scratch_buffers); vkd3d_free(allocator); d3d12_device_release(device); @@ -1557,6 +1561,7 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_allocator_Reset(ID3D12CommandAllo struct d3d12_device *device; LONG pending; VkResult vr; + size_t i; TRACE("iface %p.\n", iface); @@ -1606,6 +1611,11 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_allocator_Reset(ID3D12CommandAllo return hresult_from_vk_result(vr); } + /* Return scratch buffers to the device */ + for (i = 0; i < allocator->scratch_buffer_count; i++) + d3d12_device_return_scratch_buffer(device, &allocator->scratch_buffers[i]); + + allocator->scratch_buffer_count = 0; return S_OK; } @@ -1711,6 +1721,10 @@ static HRESULT d3d12_command_allocator_init(struct d3d12_command_allocator *allo allocator->command_buffers_size = 0; allocator->command_buffer_count = 0; + allocator->scratch_buffers = NULL; + allocator->scratch_buffers_size = 0; + allocator->scratch_buffer_count = 0; + allocator->current_command_list = NULL; d3d12_device_add_ref(allocator->device = device); @@ -1746,6 +1760,62 @@ HRESULT d3d12_command_allocator_create(struct d3d12_device *device, return S_OK; } +struct vkd3d_scratch_allocation +{ + VkBuffer buffer; + VkDeviceSize offset; + VkDeviceAddress va; +}; + +static bool d3d12_command_allocator_allocate_scratch_memory(struct d3d12_command_allocator *allocator, + VkDeviceSize size, VkDeviceSize alignment, struct vkd3d_scratch_allocation *allocation) +{ + VkDeviceSize aligned_offset, aligned_size; + struct vkd3d_scratch_buffer *scratch; + unsigned int i; + + aligned_size = align(size, alignment); + + /* Probe last block first since the others are likely full */ + for (i = allocator->scratch_buffer_count; i; i--) + { + scratch = &allocator->scratch_buffers[i - 1]; + aligned_offset = align(scratch->offset, alignment); + + if (aligned_offset + aligned_size <= scratch->size) + { + scratch->offset = aligned_offset + aligned_size; + + allocation->buffer = scratch->vk_buffer; + allocation->offset = aligned_offset; + allocation->va = scratch->va + aligned_offset; + return true; + } + } + + if (!vkd3d_array_reserve((void**)&allocator->scratch_buffers, &allocator->scratch_buffers_size, + allocator->scratch_buffer_count + 1, sizeof(*allocator->scratch_buffers))) + { + ERR("Failed to allocate scratch buffer.\n"); + return false; + } + + scratch = &allocator->scratch_buffers[allocator->scratch_buffer_count]; + if (FAILED(d3d12_device_get_scratch_buffer(allocator->device, aligned_size, scratch))) + { + ERR("Failed to create scratch buffer.\n"); + return false; + } + + allocator->scratch_buffer_count += 1; + scratch->offset = aligned_size; + + allocation->buffer = scratch->vk_buffer; + allocation->offset = 0; + allocation->va = scratch->va; + return true; +} + /* ID3D12CommandList */ static inline struct d3d12_command_list *impl_from_ID3D12GraphicsCommandList(d3d12_command_list_iface *iface) { diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c index 1a668c5d..301194ae 100644 --- a/libs/vkd3d/device.c +++ b/libs/vkd3d/device.c @@ -2256,6 +2256,98 @@ static void d3d12_remove_device_singleton(LUID luid) } } +static HRESULT d3d12_device_create_scratch_buffer(struct d3d12_device *device, VkDeviceSize size, struct vkd3d_scratch_buffer *scratch) +{ + const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; + VkDeviceMemory vk_memory = VK_NULL_HANDLE; + VkBuffer vk_buffer = VK_NULL_HANDLE; + D3D12_RESOURCE_DESC resource_desc; + D3D12_HEAP_PROPERTIES heap_desc; + HRESULT hr; + + TRACE("device %p, size %llu, scratch %p.\n", device, size, scratch); + + memset(&heap_desc, 0, sizeof(heap_desc)); + heap_desc.Type = D3D12_HEAP_TYPE_DEFAULT; + + resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resource_desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; + resource_desc.Width = size; + resource_desc.Height = 1; + resource_desc.DepthOrArraySize = 1; + resource_desc.MipLevels = 1; + resource_desc.Format = DXGI_FORMAT_UNKNOWN; + resource_desc.SampleDesc.Count = 1; + resource_desc.SampleDesc.Quality = 0; + resource_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + + if (FAILED(hr = vkd3d_create_buffer(device, &heap_desc, D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS, &resource_desc, &vk_buffer))) + return hr; + + if (FAILED(hr = vkd3d_allocate_buffer_memory(device, vk_buffer, NULL, + &heap_desc, D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS, &vk_memory, NULL, NULL))) + { + VK_CALL(vkDestroyBuffer(device->vk_device, vk_buffer, NULL)); + return hr; + } + + scratch->vk_buffer = vk_buffer; + scratch->vk_memory = vk_memory; + scratch->size = size; + scratch->offset = 0; + scratch->va = device->device_info.buffer_device_address_features.bufferDeviceAddress + ? vkd3d_get_buffer_device_address(device, vk_buffer) : 0ull; + return S_OK; +} + +static void d3d12_device_destroy_scratch_buffer(struct d3d12_device *device, const struct vkd3d_scratch_buffer *scratch) +{ + const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; + + TRACE("device %p, scratch %p.\n", device, scratch); + + VK_CALL(vkFreeMemory(device->vk_device, scratch->vk_memory, NULL)); + VK_CALL(vkDestroyBuffer(device->vk_device, scratch->vk_buffer, NULL)); +} + +HRESULT d3d12_device_get_scratch_buffer(struct d3d12_device *device, VkDeviceSize min_size, struct vkd3d_scratch_buffer *scratch) +{ + if (min_size > VKD3D_SCRATCH_BUFFER_SIZE) + return d3d12_device_create_scratch_buffer(device, min_size, scratch); + + pthread_mutex_lock(&device->mutex); + + if (device->scratch_buffer_count) + { + *scratch = device->scratch_buffers[--device->scratch_buffer_count]; + scratch->offset = 0; + pthread_mutex_unlock(&device->mutex); + return S_OK; + } + else + { + pthread_mutex_unlock(&device->mutex); + return d3d12_device_create_scratch_buffer(device, VKD3D_SCRATCH_BUFFER_SIZE, scratch); + } +} + +void d3d12_device_return_scratch_buffer(struct d3d12_device *device, const struct vkd3d_scratch_buffer *scratch) +{ + pthread_mutex_lock(&device->mutex); + + if (scratch->size == VKD3D_SCRATCH_BUFFER_SIZE && device->scratch_buffer_count < VKD3D_SCRATCH_BUFFER_COUNT) + { + device->scratch_buffers[device->scratch_buffer_count++] = *scratch; + pthread_mutex_unlock(&device->mutex); + } + else + { + pthread_mutex_unlock(&device->mutex); + d3d12_device_destroy_scratch_buffer(device, scratch); + } +} + /* ID3D12Device */ static inline struct d3d12_device *impl_from_ID3D12Device(d3d12_device_iface *iface) { @@ -2301,6 +2393,10 @@ static ULONG STDMETHODCALLTYPE d3d12_device_AddRef(d3d12_device_iface *iface) static void d3d12_device_destroy(struct d3d12_device *device) { const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; + size_t i; + + for (i = 0; i < device->scratch_buffer_count; i++) + d3d12_device_destroy_scratch_buffer(device, &device->scratch_buffers[i]); vkd3d_private_store_destroy(&device->private_store); diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h index 8a97f0bc..0246633c 100644 --- a/libs/vkd3d/vkd3d_private.h +++ b/libs/vkd3d/vkd3d_private.h @@ -1108,6 +1108,18 @@ enum vkd3d_descriptor_pool_types VKD3D_DESCRIPTOR_POOL_TYPE_COUNT }; +#define VKD3D_SCRATCH_BUFFER_SIZE (1ull << 20) +#define VKD3D_SCRATCH_BUFFER_COUNT (32u) + +struct vkd3d_scratch_buffer +{ + VkBuffer vk_buffer; + VkDeviceMemory vk_memory; + VkDeviceSize size; + VkDeviceSize offset; + VkDeviceAddress va; +}; + /* ID3D12CommandAllocator */ struct d3d12_command_allocator { @@ -1141,6 +1153,10 @@ struct d3d12_command_allocator size_t command_buffers_size; size_t command_buffer_count; + struct vkd3d_scratch_buffer *scratch_buffers; + size_t scratch_buffers_size; + size_t scratch_buffer_count; + LONG outstanding_submissions_count; struct d3d12_command_list *current_command_list; @@ -1955,6 +1971,9 @@ struct d3d12_device struct vkd3d_private_store private_store; struct d3d12_caps d3d12_caps; + struct vkd3d_scratch_buffer scratch_buffers[VKD3D_SCRATCH_BUFFER_COUNT]; + size_t scratch_buffer_count; + HRESULT removed_reason; const struct vkd3d_format *depth_stencil_formats; @@ -1978,6 +1997,9 @@ void d3d12_device_mark_as_removed(struct d3d12_device *device, HRESULT reason, const char *message, ...) VKD3D_PRINTF_FUNC(3, 4); struct d3d12_device *unsafe_impl_from_ID3D12Device(d3d12_device_iface *iface); +HRESULT d3d12_device_get_scratch_buffer(struct d3d12_device *device, VkDeviceSize min_size, struct vkd3d_scratch_buffer *scratch); +void d3d12_device_return_scratch_buffer(struct d3d12_device *device, const struct vkd3d_scratch_buffer *scratch); + static inline HRESULT d3d12_device_query_interface(struct d3d12_device *device, REFIID iid, void **object) { return ID3D12Device6_QueryInterface(&device->ID3D12Device_iface, iid, object);