[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