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 <philip.rebohle@tu-dortmund.de>
This commit is contained in:
Philip Rebohle 2020-11-24 12:59:15 +01:00 committed by Hans-Kristian Arntzen
parent c0b34fdb7b
commit afb85c79cd
3 changed files with 188 additions and 0 deletions

View File

@ -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)
{

View File

@ -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);

View File

@ -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);