[PATCH 03/15] wined3d: Implement a Vulkan device memory allocator.
Henri Verbeet
hverbeet at codeweavers.com
Fri Apr 17 10:51:10 CDT 2020
Note that the constants like WINED3D_ALLOCATOR_CHUNK_SIZE and
WINED3D_ALLOCATOR_MIN_BLOCK_SIZE are somewhat arbitrary, rather than the
result of careful tuning. That's mostly because we have a couple of
known stalls in e.g. the command stream that would largely invalidate
such tuning.
Signed-off-by: Henri Verbeet <hverbeet at codeweavers.com>
---
dlls/wined3d/adapter_vk.c | 121 +++++++++++++++++--
dlls/wined3d/buffer.c | 2 +-
dlls/wined3d/context_vk.c | 137 +++++++++++++++++++--
dlls/wined3d/utils.c | 215 +++++++++++++++++++++++++++++++++
dlls/wined3d/wined3d_private.h | 81 +++++++++++++
5 files changed, 537 insertions(+), 19 deletions(-)
diff --git a/dlls/wined3d/adapter_vk.c b/dlls/wined3d/adapter_vk.c
index e51eeccc02c..1bca53ac7f9 100644
--- a/dlls/wined3d/adapter_vk.c
+++ b/dlls/wined3d/adapter_vk.c
@@ -176,6 +176,57 @@ static void wined3d_disable_vulkan_features(VkPhysicalDeviceFeatures *features)
features->inheritedQueries = VK_FALSE;
}
+static struct wined3d_allocator_chunk *wined3d_allocator_vk_create_chunk(struct wined3d_allocator *allocator,
+ struct wined3d_context *context, unsigned int memory_type, size_t chunk_size)
+{
+ struct wined3d_context_vk *context_vk = wined3d_context_vk(context);
+ struct wined3d_allocator_chunk_vk *chunk_vk;
+
+ if (!(chunk_vk = heap_alloc(sizeof(*chunk_vk))))
+ return NULL;
+
+ if (!wined3d_allocator_chunk_init(&chunk_vk->c, allocator))
+ {
+ heap_free(chunk_vk);
+ return NULL;
+ }
+
+ if (!(chunk_vk->vk_memory = wined3d_context_vk_allocate_vram_chunk_memory(context_vk, memory_type, chunk_size)))
+ {
+ wined3d_allocator_chunk_cleanup(&chunk_vk->c);
+ heap_free(chunk_vk);
+ return NULL;
+ }
+ list_add_head(&allocator->pools[memory_type].chunks, &chunk_vk->c.entry);
+
+ return &chunk_vk->c;
+}
+
+static void wined3d_allocator_vk_destroy_chunk(struct wined3d_allocator_chunk *chunk)
+{
+ struct wined3d_allocator_chunk_vk *chunk_vk = wined3d_allocator_chunk_vk(chunk);
+ const struct wined3d_vk_info *vk_info;
+ struct wined3d_device_vk *device_vk;
+
+ TRACE("chunk %p.\n", chunk);
+
+ device_vk = CONTAINING_RECORD(chunk_vk->c.allocator, struct wined3d_device_vk, allocator);
+ vk_info = &device_vk->vk_info;
+
+ if (chunk_vk->c.map_ptr)
+ VK_CALL(vkUnmapMemory(device_vk->vk_device, chunk_vk->vk_memory));
+ VK_CALL(vkFreeMemory(device_vk->vk_device, chunk_vk->vk_memory, NULL));
+ TRACE("Freed memory 0x%s.\n", wine_dbgstr_longlong(chunk_vk->vk_memory));
+ wined3d_allocator_chunk_cleanup(&chunk_vk->c);
+ heap_free(chunk_vk);
+}
+
+static const struct wined3d_allocator_ops wined3d_allocator_vk_ops =
+{
+ .allocator_create_chunk = wined3d_allocator_vk_create_chunk,
+ .allocator_destroy_chunk = wined3d_allocator_vk_destroy_chunk,
+};
+
static HRESULT adapter_vk_create_device(struct wined3d *wined3d, const struct wined3d_adapter *adapter,
enum wined3d_device_type device_type, HWND focus_window, unsigned int flags, BYTE surface_alignment,
const enum wined3d_feature_level *levels, unsigned int level_count,
@@ -247,10 +298,19 @@ static HRESULT adapter_vk_create_device(struct wined3d *wined3d, const struct wi
VK_DEVICE_FUNCS()
#undef VK_DEVICE_PFN
+ if (!wined3d_allocator_init(&device_vk->allocator,
+ adapter_vk->memory_properties.memoryTypeCount, &wined3d_allocator_vk_ops))
+ {
+ WARN("Failed to initialise allocator.\n");
+ hr = E_FAIL;
+ goto fail;
+ }
+
if (FAILED(hr = wined3d_device_init(&device_vk->d, wined3d, adapter->ordinal, device_type,
focus_window, flags, surface_alignment, levels, level_count, device_parent)))
{
WARN("Failed to initialize device, hr %#x.\n", hr);
+ wined3d_allocator_cleanup(&device_vk->allocator);
goto fail;
}
@@ -270,6 +330,7 @@ static void adapter_vk_destroy_device(struct wined3d_device *device)
const struct wined3d_vk_info *vk_info = &device_vk->vk_info;
wined3d_device_cleanup(&device_vk->d);
+ wined3d_allocator_cleanup(&device_vk->allocator);
VK_CALL(vkDestroyDevice(device_vk->vk_device, NULL));
heap_free(device_vk);
}
@@ -488,6 +549,49 @@ static void adapter_vk_uninit_3d(struct wined3d_device *device)
wined3d_context_vk_cleanup(context_vk);
}
+static void *wined3d_bo_vk_map(struct wined3d_bo_vk *bo, struct wined3d_context_vk *context_vk)
+{
+ const struct wined3d_vk_info *vk_info;
+ struct wined3d_device_vk *device_vk;
+ void *map_ptr;
+ VkResult vr;
+
+ vk_info = context_vk->vk_info;
+ device_vk = wined3d_device_vk(context_vk->c.device);
+
+ if (bo->memory)
+ {
+ struct wined3d_allocator_chunk_vk *chunk_vk = wined3d_allocator_chunk_vk(bo->memory->chunk);
+
+ if (!(map_ptr = wined3d_allocator_chunk_vk_map(chunk_vk, context_vk)))
+ {
+ ERR("Failed to map chunk.\n");
+ return NULL;
+ }
+ }
+ else if ((vr = VK_CALL(vkMapMemory(device_vk->vk_device, bo->vk_memory, 0, VK_WHOLE_SIZE, 0, &map_ptr))) < 0)
+ {
+ ERR("Failed to map memory, vr %s.\n", wined3d_debug_vkresult(vr));
+ return NULL;
+ }
+
+ return map_ptr;
+}
+
+static void wined3d_bo_vk_unmap(struct wined3d_bo_vk *bo, struct wined3d_context_vk *context_vk)
+{
+ const struct wined3d_vk_info *vk_info;
+ struct wined3d_device_vk *device_vk;
+
+ vk_info = context_vk->vk_info;
+ device_vk = wined3d_device_vk(context_vk->c.device);
+
+ if (bo->memory)
+ wined3d_allocator_chunk_vk_unmap(wined3d_allocator_chunk_vk(bo->memory->chunk), context_vk);
+ else
+ VK_CALL(vkUnmapMemory(device_vk->vk_device, bo->vk_memory));
+}
+
static void *adapter_vk_map_bo_address(struct wined3d_context *context,
const struct wined3d_bo_address *data, size_t size, uint32_t bind_flags, uint32_t map_flags)
{
@@ -499,7 +603,6 @@ static void *adapter_vk_map_bo_address(struct wined3d_context *context,
VkMappedMemoryRange range;
struct wined3d_bo_vk *bo;
void *map_ptr;
- VkResult vr;
if (!(bo = (struct wined3d_bo_vk *)data->buffer_object))
return data->addr;
@@ -532,7 +635,7 @@ static void *adapter_vk_map_bo_address(struct wined3d_context *context,
range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
range.pNext = NULL;
range.memory = bo->vk_memory;
- range.offset = (uintptr_t)data->addr;
+ range.offset = bo->memory_offset + (uintptr_t)data->addr;
range.size = size;
VK_CALL(vkInvalidateMappedMemoryRanges(device_vk->vk_device, 1, &range));
}
@@ -544,19 +647,19 @@ static void *adapter_vk_map_bo_address(struct wined3d_context *context,
wined3d_context_vk_submit_command_buffer(context_vk, 0, NULL, NULL, 0, NULL);
wined3d_context_vk_wait_command_buffer(context_vk, bo->command_buffer_id);
- if ((vr = VK_CALL(vkMapMemory(device_vk->vk_device, bo->vk_memory,
- (uintptr_t)data->addr, size, 0, &map_ptr))) < 0)
+ if (!(map_ptr = wined3d_bo_vk_map(bo, context_vk)))
{
- ERR("Failed to map buffer, vr %s.\n", wined3d_debug_vkresult(vr));
+ ERR("Failed to map bo.\n");
return NULL;
}
- return map_ptr;
+ return (uint8_t *)map_ptr + bo->memory_offset + (uintptr_t)data->addr;
}
static void adapter_vk_unmap_bo_address(struct wined3d_context *context, const struct wined3d_bo_address *data,
uint32_t bind_flags, unsigned int range_count, const struct wined3d_range *ranges)
{
+ struct wined3d_context_vk *context_vk = wined3d_context_vk(context);
const struct wined3d_vk_info *vk_info;
struct wined3d_device_vk *device_vk;
VkMappedMemoryRange range;
@@ -566,7 +669,7 @@ static void adapter_vk_unmap_bo_address(struct wined3d_context *context, const s
if (!(bo = (struct wined3d_bo_vk *)data->buffer_object))
return;
- vk_info = wined3d_context_vk(context)->vk_info;
+ vk_info = context_vk->vk_info;
device_vk = wined3d_device_vk(context->device);
if (!(bo->memory_type & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
@@ -577,13 +680,13 @@ static void adapter_vk_unmap_bo_address(struct wined3d_context *context, const s
for (i = 0; i < range_count; ++i)
{
- range.offset = ranges[i].offset;
+ range.offset = bo->memory_offset + ranges[i].offset;
range.size = ranges[i].size;
VK_CALL(vkFlushMappedMemoryRanges(device_vk->vk_device, 1, &range));
}
}
- VK_CALL(vkUnmapMemory(device_vk->vk_device, bo->vk_memory));
+ wined3d_bo_vk_unmap(bo, context_vk);
}
static void adapter_vk_copy_bo_address(struct wined3d_context *context,
diff --git a/dlls/wined3d/buffer.c b/dlls/wined3d/buffer.c
index 2e2ae239210..c45ddae7e59 100644
--- a/dlls/wined3d/buffer.c
+++ b/dlls/wined3d/buffer.c
@@ -1636,7 +1636,7 @@ static void wined3d_buffer_vk_unload_location(struct wined3d_buffer *buffer,
case WINED3D_LOCATION_BUFFER:
wined3d_context_vk_destroy_bo(context_vk, &buffer_vk->bo);
buffer_vk->bo.vk_buffer = VK_NULL_HANDLE;
- buffer_vk->bo.vk_memory = VK_NULL_HANDLE;
+ buffer_vk->bo.memory = NULL;
buffer_vk->b.buffer_object = 0u;
break;
diff --git a/dlls/wined3d/context_vk.c b/dlls/wined3d/context_vk.c
index 3fa3ce8207f..b3239a4f9ee 100644
--- a/dlls/wined3d/context_vk.c
+++ b/dlls/wined3d/context_vk.c
@@ -29,6 +29,87 @@
WINE_DEFAULT_DEBUG_CHANNEL(d3d);
+void *wined3d_allocator_chunk_vk_map(struct wined3d_allocator_chunk_vk *chunk_vk,
+ struct wined3d_context_vk *context_vk)
+{
+ struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device);
+ const struct wined3d_vk_info *vk_info = context_vk->vk_info;
+ VkResult vr;
+
+ TRACE("chunk %p, memory 0x%s, map_ptr %p.\n", chunk_vk,
+ wine_dbgstr_longlong(chunk_vk->vk_memory), chunk_vk->c.map_ptr);
+
+ if (!chunk_vk->c.map_ptr && (vr = VK_CALL(vkMapMemory(device_vk->vk_device,
+ chunk_vk->vk_memory, 0, VK_WHOLE_SIZE, 0, &chunk_vk->c.map_ptr))) < 0)
+ {
+ ERR("Failed to map chunk memory, vr %s.\n", wined3d_debug_vkresult(vr));
+ return NULL;
+ }
+
+ ++chunk_vk->c.map_count;
+
+ return chunk_vk->c.map_ptr;
+}
+
+void wined3d_allocator_chunk_vk_unmap(struct wined3d_allocator_chunk_vk *chunk_vk,
+ struct wined3d_context_vk *context_vk)
+{
+ struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device);
+ const struct wined3d_vk_info *vk_info = context_vk->vk_info;
+
+ TRACE("chunk_vk %p, context_vk %p.\n", chunk_vk, context_vk);
+
+ if (!--chunk_vk->c.map_count)
+ VK_CALL(vkUnmapMemory(device_vk->vk_device, chunk_vk->vk_memory));
+ chunk_vk->c.map_ptr = NULL;
+}
+
+VkDeviceMemory wined3d_context_vk_allocate_vram_chunk_memory(struct wined3d_context_vk *context_vk,
+ unsigned int pool, size_t size)
+{
+ struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device);
+ const struct wined3d_vk_info *vk_info = context_vk->vk_info;
+ VkMemoryAllocateInfo allocate_info;
+ VkDeviceMemory vk_memory;
+ VkResult vr;
+
+ allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ allocate_info.pNext = NULL;
+ allocate_info.allocationSize = size;
+ allocate_info.memoryTypeIndex = pool;
+ if ((vr = VK_CALL(vkAllocateMemory(device_vk->vk_device, &allocate_info, NULL, &vk_memory))) < 0)
+ {
+ ERR("Failed to allocate memory, vr %s.\n", wined3d_debug_vkresult(vr));
+ return VK_NULL_HANDLE;
+ }
+
+ return vk_memory;
+}
+
+static struct wined3d_allocator_block *wined3d_context_vk_allocate_memory(struct wined3d_context_vk *context_vk,
+ unsigned int memory_type, VkDeviceSize size, VkDeviceMemory *vk_memory)
+{
+ struct wined3d_device_vk *device_vk = wined3d_device_vk(context_vk->c.device);
+ struct wined3d_allocator *allocator = &device_vk->allocator;
+ struct wined3d_allocator_block *block;
+
+ if (size > WINED3D_ALLOCATOR_CHUNK_SIZE / 2)
+ {
+ *vk_memory = wined3d_context_vk_allocate_vram_chunk_memory(context_vk, memory_type, size);
+ return NULL;
+ }
+
+ if (!(block = wined3d_allocator_allocate(allocator, &context_vk->c, memory_type, size)))
+ {
+ *vk_memory = VK_NULL_HANDLE;
+ return NULL;
+ }
+
+ *vk_memory = wined3d_allocator_chunk_vk(block->chunk)->vk_memory;
+
+ return block;
+}
+
BOOL wined3d_context_vk_create_bo(struct wined3d_context_vk *context_vk, VkDeviceSize size,
VkBufferUsageFlags usage, VkMemoryPropertyFlags memory_type, struct wined3d_bo_vk *bo)
{
@@ -36,7 +117,6 @@ BOOL wined3d_context_vk_create_bo(struct wined3d_context_vk *context_vk, VkDevic
const struct wined3d_vk_info *vk_info = context_vk->vk_info;
VkMemoryRequirements memory_requirements;
struct wined3d_adapter_vk *adapter_vk;
- VkMemoryAllocateInfo allocate_info;
VkBufferCreateInfo create_info;
unsigned int memory_type_idx;
VkResult vr;
@@ -60,9 +140,6 @@ BOOL wined3d_context_vk_create_bo(struct wined3d_context_vk *context_vk, VkDevic
VK_CALL(vkGetBufferMemoryRequirements(device_vk->vk_device, bo->vk_buffer, &memory_requirements));
- allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
- allocate_info.pNext = NULL;
- allocate_info.allocationSize = memory_requirements.size;
memory_type_idx = wined3d_adapter_vk_get_memory_type_index(adapter_vk,
memory_requirements.memoryTypeBits, memory_type);
if (memory_type_idx == ~0u)
@@ -71,18 +148,24 @@ BOOL wined3d_context_vk_create_bo(struct wined3d_context_vk *context_vk, VkDevic
VK_CALL(vkDestroyBuffer(device_vk->vk_device, bo->vk_buffer, NULL));
return FALSE;
}
- allocate_info.memoryTypeIndex = memory_type_idx;
- if ((vr = VK_CALL(vkAllocateMemory(device_vk->vk_device, &allocate_info, NULL, &bo->vk_memory))) < 0)
+ bo->memory = wined3d_context_vk_allocate_memory(context_vk,
+ memory_type_idx, memory_requirements.size, &bo->vk_memory);
+ if (!bo->vk_memory)
{
- ERR("Failed to allocate buffer memory, vr %s.\n", wined3d_debug_vkresult(vr));
+ ERR("Failed to allocate buffer memory.\n");
VK_CALL(vkDestroyBuffer(device_vk->vk_device, bo->vk_buffer, NULL));
return FALSE;
}
+ bo->memory_offset = bo->memory ? bo->memory->offset : 0;
- if ((vr = VK_CALL(vkBindBufferMemory(device_vk->vk_device, bo->vk_buffer, bo->vk_memory, 0))) < 0)
+ if ((vr = VK_CALL(vkBindBufferMemory(device_vk->vk_device, bo->vk_buffer,
+ bo->vk_memory, bo->memory_offset))) < 0)
{
ERR("Failed to bind buffer memory, vr %s.\n", wined3d_debug_vkresult(vr));
- VK_CALL(vkFreeMemory(device_vk->vk_device, bo->vk_memory, NULL));
+ if (bo->memory)
+ wined3d_allocator_block_free(bo->memory);
+ else
+ VK_CALL(vkFreeMemory(device_vk->vk_device, bo->vk_memory, NULL));
VK_CALL(vkDestroyBuffer(device_vk->vk_device, bo->vk_buffer, NULL));
return FALSE;
}
@@ -140,6 +223,29 @@ static void wined3d_context_vk_destroy_memory(struct wined3d_context_vk *context
o->command_buffer_id = command_buffer_id;
}
+static void wined3d_context_vk_destroy_allocator_block(struct wined3d_context_vk *context_vk,
+ struct wined3d_allocator_block *block, uint64_t command_buffer_id)
+{
+ struct wined3d_retired_object_vk *o;
+
+ if (context_vk->completed_command_buffer_id > command_buffer_id)
+ {
+ wined3d_allocator_block_free(block);
+ TRACE("Freed block %p.\n", block);
+ return;
+ }
+
+ if (!(o = wined3d_context_vk_get_retired_object_vk(context_vk)))
+ {
+ ERR("Leaking block %p.\n", block);
+ return;
+ }
+
+ o->type = WINED3D_RETIRED_ALLOCATOR_BLOCK_VK;
+ o->u.block = block;
+ o->command_buffer_id = command_buffer_id;
+}
+
static void wined3d_context_vk_destroy_buffer(struct wined3d_context_vk *context_vk,
VkBuffer vk_buffer, uint64_t command_buffer_id)
{
@@ -167,7 +273,15 @@ static void wined3d_context_vk_destroy_buffer(struct wined3d_context_vk *context
void wined3d_context_vk_destroy_bo(struct wined3d_context_vk *context_vk, const struct wined3d_bo_vk *bo)
{
+ TRACE("context_vk %p, bo %p.\n", context_vk, bo);
+
wined3d_context_vk_destroy_buffer(context_vk, bo->vk_buffer, bo->command_buffer_id);
+ if (bo->memory)
+ {
+ wined3d_context_vk_destroy_allocator_block(context_vk, bo->memory, bo->command_buffer_id);
+ return;
+ }
+
wined3d_context_vk_destroy_memory(context_vk, bo->vk_memory, bo->command_buffer_id);
}
@@ -221,6 +335,11 @@ static void wined3d_context_vk_cleanup_resources(struct wined3d_context_vk *cont
TRACE("Freed memory 0x%s.\n", wine_dbgstr_longlong(o->u.vk_memory));
break;
+ case WINED3D_RETIRED_ALLOCATOR_BLOCK_VK:
+ TRACE("Destroying block %p.\n", o->u.block);
+ wined3d_allocator_block_free(o->u.block);
+ break;
+
case WINED3D_RETIRED_BUFFER_VK:
VK_CALL(vkDestroyBuffer(device_vk->vk_device, o->u.vk_buffer, NULL));
TRACE("Destroyed buffer 0x%s.\n", wine_dbgstr_longlong(o->u.vk_buffer));
diff --git a/dlls/wined3d/utils.c b/dlls/wined3d/utils.c
index 211160ad4db..a2765774c8d 100644
--- a/dlls/wined3d/utils.c
+++ b/dlls/wined3d/utils.c
@@ -7061,3 +7061,218 @@ void compute_normal_matrix(float *normal_matrix, BOOL legacy_lighting,
for (j = 0; j < 3; ++j)
normal_matrix[i * 3 + j] = (&mv._11)[j * 4 + i];
}
+
+static void wined3d_allocator_release_block(struct wined3d_allocator *allocator,
+ struct wined3d_allocator_block *block)
+{
+ block->parent = allocator->free;
+ allocator->free = block;
+}
+
+static struct wined3d_allocator_block *wined3d_allocator_acquire_block(struct wined3d_allocator *allocator)
+{
+ struct wined3d_allocator_block *block;
+
+ if (!allocator->free)
+ return heap_alloc(sizeof(*block));
+
+ block = allocator->free;
+ allocator->free = block->parent;
+
+ return block;
+}
+
+void wined3d_allocator_block_free(struct wined3d_allocator_block *block)
+{
+ struct wined3d_allocator *allocator = block->chunk->allocator;
+ struct wined3d_allocator_block *parent;
+
+ while ((parent = block->parent) && block->sibling->free)
+ {
+ list_remove(&block->sibling->entry);
+ wined3d_allocator_release_block(allocator, block->sibling);
+ wined3d_allocator_release_block(allocator, block);
+ block = parent;
+ }
+
+ block->free = true;
+ list_add_head(&block->chunk->available[block->order], &block->entry);
+}
+
+static void wined3d_allocator_block_init(struct wined3d_allocator_block *block,
+ struct wined3d_allocator_chunk *chunk, struct wined3d_allocator_block *parent,
+ struct wined3d_allocator_block *sibling, unsigned int order, size_t offset, bool free)
+{
+ list_init(&block->entry);
+ block->chunk = chunk;
+ block->parent = parent;
+ block->sibling = sibling;
+ block->order = order;
+ block->offset = offset;
+ block->free = free;
+}
+
+void wined3d_allocator_chunk_cleanup(struct wined3d_allocator_chunk *chunk)
+{
+ struct wined3d_allocator_block *block;
+ size_t i;
+
+ if (list_empty(&chunk->available[0]))
+ {
+ ERR("Chunk %p is not empty.\n", chunk);
+ return;
+ }
+
+ for (i = 1; i < ARRAY_SIZE(chunk->available); ++i)
+ {
+ if (!list_empty(&chunk->available[i]))
+ {
+ ERR("Chunk %p is not empty.\n", chunk);
+ return;
+ }
+ }
+
+ block = LIST_ENTRY(list_head(&chunk->available[0]), struct wined3d_allocator_block, entry);
+ wined3d_allocator_release_block(chunk->allocator, block);
+}
+
+bool wined3d_allocator_chunk_init(struct wined3d_allocator_chunk *chunk, struct wined3d_allocator *allocator)
+{
+ struct wined3d_allocator_block *block;
+ unsigned int i;
+
+ if (!(block = wined3d_allocator_acquire_block(allocator)))
+ return false;
+ wined3d_allocator_block_init(block, chunk, NULL, NULL, 0, 0, true);
+
+ list_init(&chunk->entry);
+ for (i = 0; i < ARRAY_SIZE(chunk->available); ++i)
+ {
+ list_init(&chunk->available[i]);
+ }
+ list_add_head(&chunk->available[0], &block->entry);
+ chunk->allocator = allocator;
+ chunk->map_count = 0;
+ chunk->map_ptr = NULL;
+
+ return true;
+}
+
+void wined3d_allocator_cleanup(struct wined3d_allocator *allocator)
+{
+ struct wined3d_allocator_chunk *chunk, *chunk2;
+ struct wined3d_allocator_block *block, *next;
+ size_t i;
+
+ for (i = 0; i < allocator->pool_count; ++i)
+ {
+ LIST_FOR_EACH_ENTRY_SAFE(chunk, chunk2, &allocator->pools[i].chunks, struct wined3d_allocator_chunk, entry)
+ {
+ list_remove(&chunk->entry);
+ allocator->ops->allocator_destroy_chunk(chunk);
+ }
+ }
+ heap_free(allocator->pools);
+
+ next = allocator->free;
+ while ((block = next))
+ {
+ next = block->parent;
+ heap_free(block);
+ }
+}
+
+static struct wined3d_allocator_block *wined3d_allocator_chunk_allocate(struct wined3d_allocator_chunk *chunk,
+ unsigned int order)
+{
+ struct wined3d_allocator_block *block, *left, *right;
+ unsigned int i = order;
+
+ while (i)
+ {
+ if (!list_empty(&chunk->available[i]))
+ break;
+ --i;
+ }
+
+ if (list_empty(&chunk->available[i]))
+ return NULL;
+
+ block = LIST_ENTRY(list_head(&chunk->available[i]), struct wined3d_allocator_block, entry);
+ list_remove(&block->entry);
+ block->free = false;
+
+ while (i < order)
+ {
+ if (!(left = wined3d_allocator_acquire_block(chunk->allocator)))
+ {
+ ERR("Failed to allocate left.\n");
+ break;
+ }
+
+ if (!(right = wined3d_allocator_acquire_block(chunk->allocator)))
+ {
+ ERR("Failed to allocate right.\n");
+ wined3d_allocator_release_block(chunk->allocator, left);
+ break;
+ }
+
+ wined3d_allocator_block_init(left, chunk, block, right, block->order + 1, block->offset, false);
+ wined3d_allocator_block_init(right, chunk, block, left, block->order + 1,
+ block->offset + (WINED3D_ALLOCATOR_CHUNK_SIZE >> left->order), true);
+ list_add_head(&chunk->available[right->order], &right->entry);
+
+ block = left;
+ ++i;
+ }
+
+ return block;
+}
+
+struct wined3d_allocator_block *wined3d_allocator_allocate(struct wined3d_allocator *allocator,
+ struct wined3d_context *context, unsigned int memory_type, size_t size)
+{
+ struct wined3d_allocator_chunk *chunk;
+ struct wined3d_allocator_block *block;
+ unsigned int order;
+
+ if (size > WINED3D_ALLOCATOR_CHUNK_SIZE / 2)
+ return NULL;
+
+ if (size < WINED3D_ALLOCATOR_MIN_BLOCK_SIZE)
+ order = WINED3D_ALLOCATOR_CHUNK_ORDER_COUNT - 1;
+ else
+ order = wined3d_log2i(WINED3D_ALLOCATOR_CHUNK_SIZE / size);
+
+ LIST_FOR_EACH_ENTRY(chunk, &allocator->pools[memory_type].chunks, struct wined3d_allocator_chunk, entry)
+ {
+ if ((block = wined3d_allocator_chunk_allocate(chunk, order)))
+ return block;
+ }
+
+ if (!(chunk = allocator->ops->allocator_create_chunk(allocator,
+ context, memory_type, WINED3D_ALLOCATOR_CHUNK_SIZE)))
+ return NULL;
+
+ if (!(block = wined3d_allocator_chunk_allocate(chunk, order)))
+ return NULL;
+
+ return block;
+}
+
+bool wined3d_allocator_init(struct wined3d_allocator *allocator,
+ size_t pool_count, const struct wined3d_allocator_ops *allocator_ops)
+{
+ size_t i;
+
+ allocator->ops = allocator_ops;
+ allocator->pool_count = pool_count;
+ if (!(allocator->pools = heap_calloc(pool_count, sizeof(*allocator->pools))))
+ return false;
+ for (i = 0; i < pool_count; ++i)
+ {
+ list_init(&allocator->pools[i].chunks);
+ }
+
+ return true;
+}
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index fe44a6edb0c..ade1eae352c 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -33,6 +33,7 @@
#include <assert.h>
#include <stdarg.h>
+#include <stdbool.h>
#include <math.h>
#include <limits.h>
#include "ntstatus.h"
@@ -1520,8 +1521,11 @@ do { \
struct wined3d_bo_vk
{
VkBuffer vk_buffer;
+ struct wined3d_allocator_block *memory;
+
VkDeviceMemory vk_memory;
+ VkDeviceSize memory_offset;
VkMemoryPropertyFlags memory_type;
uint64_t command_buffer_id;
@@ -2188,6 +2192,7 @@ enum wined3d_retired_object_type_vk
{
WINED3D_RETIRED_FREE_VK,
WINED3D_RETIRED_MEMORY_VK,
+ WINED3D_RETIRED_ALLOCATOR_BLOCK_VK,
WINED3D_RETIRED_BUFFER_VK,
};
@@ -2198,6 +2203,7 @@ struct wined3d_retired_object_vk
{
struct wined3d_retired_object_vk *next;
VkDeviceMemory vk_memory;
+ struct wined3d_allocator_block *block;
VkBuffer vk_buffer;
} u;
uint64_t command_buffer_id;
@@ -2236,6 +2242,8 @@ static inline struct wined3d_context_vk *wined3d_context_vk(struct wined3d_conte
return CONTAINING_RECORD(context, struct wined3d_context_vk, c);
}
+VkDeviceMemory wined3d_context_vk_allocate_vram_chunk_memory(struct wined3d_context_vk *context_vk,
+ unsigned int pool, size_t size) DECLSPEC_HIDDEN;
void wined3d_context_vk_cleanup(struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN;
BOOL wined3d_context_vk_create_bo(struct wined3d_context_vk *context_vk, VkDeviceSize size,
VkBufferUsageFlags usage, VkMemoryPropertyFlags memory_type, struct wined3d_bo_vk *bo) DECLSPEC_HIDDEN;
@@ -3422,6 +3430,77 @@ static inline struct wined3d_device_gl *wined3d_device_gl(struct wined3d_device
return CONTAINING_RECORD(device, struct wined3d_device_gl, d);
}
+#define WINED3D_ALLOCATOR_CHUNK_SIZE (64 * 1024 * 1024)
+#define WINED3D_ALLOCATOR_CHUNK_ORDER_COUNT 15
+#define WINED3D_ALLOCATOR_MIN_BLOCK_SIZE (WINED3D_ALLOCATOR_CHUNK_SIZE >> (WINED3D_ALLOCATOR_CHUNK_ORDER_COUNT - 1))
+
+struct wined3d_allocator_chunk
+{
+ struct list entry;
+ struct list available[WINED3D_ALLOCATOR_CHUNK_ORDER_COUNT];
+ struct wined3d_allocator *allocator;
+ unsigned int map_count;
+ void *map_ptr;
+};
+
+void wined3d_allocator_chunk_cleanup(struct wined3d_allocator_chunk *chunk) DECLSPEC_HIDDEN;
+bool wined3d_allocator_chunk_init(struct wined3d_allocator_chunk *chunk,
+ struct wined3d_allocator *allocator) DECLSPEC_HIDDEN;
+
+struct wined3d_allocator_chunk_vk
+{
+ struct wined3d_allocator_chunk c;
+ VkDeviceMemory vk_memory;
+};
+
+static inline struct wined3d_allocator_chunk_vk *wined3d_allocator_chunk_vk(struct wined3d_allocator_chunk *chunk)
+{
+ return CONTAINING_RECORD(chunk, struct wined3d_allocator_chunk_vk, c);
+}
+
+void *wined3d_allocator_chunk_vk_map(struct wined3d_allocator_chunk_vk *chunk_vk,
+ struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN;
+void wined3d_allocator_chunk_vk_unmap(struct wined3d_allocator_chunk_vk *chunk_vk,
+ struct wined3d_context_vk *context_vk) DECLSPEC_HIDDEN;
+
+struct wined3d_allocator_block
+{
+ struct list entry;
+ struct wined3d_allocator_chunk *chunk;
+ struct wined3d_allocator_block *parent, *sibling;
+ unsigned int order;
+ size_t offset;
+ bool free;
+};
+
+void wined3d_allocator_block_free(struct wined3d_allocator_block *block) DECLSPEC_HIDDEN;
+
+struct wined3d_allocator_pool
+{
+ struct list chunks;
+};
+
+struct wined3d_allocator_ops
+{
+ struct wined3d_allocator_chunk *(*allocator_create_chunk)(struct wined3d_allocator *allocator,
+ struct wined3d_context *context, unsigned int memory_type, size_t chunk_size);
+ void (*allocator_destroy_chunk)(struct wined3d_allocator_chunk *chunk);
+};
+
+struct wined3d_allocator
+{
+ const struct wined3d_allocator_ops *ops;
+ struct wined3d_allocator_pool *pools;
+ size_t pool_count;
+ struct wined3d_allocator_block *free;
+};
+
+struct wined3d_allocator_block *wined3d_allocator_allocate(struct wined3d_allocator *allocator,
+ struct wined3d_context *context, unsigned int memory_type, size_t size) DECLSPEC_HIDDEN;
+void wined3d_allocator_cleanup(struct wined3d_allocator *allocator) DECLSPEC_HIDDEN;
+bool wined3d_allocator_init(struct wined3d_allocator *allocator,
+ size_t pool_count, const struct wined3d_allocator_ops *allocator_ops) DECLSPEC_HIDDEN;
+
struct wined3d_device_vk
{
struct wined3d_device d;
@@ -3433,6 +3512,8 @@ struct wined3d_device_vk
uint32_t vk_queue_family_index;
struct wined3d_vk_info vk_info;
+
+ struct wined3d_allocator allocator;
};
static inline struct wined3d_device_vk *wined3d_device_vk(struct wined3d_device *device)
--
2.20.1
More information about the wine-devel
mailing list