summarylogtreecommitdiffstats
path: root/0008-wined3d-Implement-lazy-free-using-a-deferred-free-li.patch
diff options
context:
space:
mode:
authorStefan Schmidt2018-03-07 18:20:55 +0100
committerStefan Schmidt2018-03-07 18:20:55 +0100
commit9cc0c1987f64a4f538e747058b53e37f4548d8f4 (patch)
tree2bac1b3e58a4e2857e5cc32d30cbe1a3423b94d6 /0008-wined3d-Implement-lazy-free-using-a-deferred-free-li.patch
parent8f3ed8701334e289a8a98b275825044cecab9277 (diff)
downloadaur-9cc0c1987f64a4f538e747058b53e37f4548d8f4.tar.gz
Updated to wine-staging 3.3 and commit b33e274f894de751f9abe2e09aec5bc9d1e6b992
Diffstat (limited to '0008-wined3d-Implement-lazy-free-using-a-deferred-free-li.patch')
-rw-r--r--0008-wined3d-Implement-lazy-free-using-a-deferred-free-li.patch638
1 files changed, 0 insertions, 638 deletions
diff --git a/0008-wined3d-Implement-lazy-free-using-a-deferred-free-li.patch b/0008-wined3d-Implement-lazy-free-using-a-deferred-free-li.patch
deleted file mode 100644
index 1597c85366f..00000000000
--- a/0008-wined3d-Implement-lazy-free-using-a-deferred-free-li.patch
+++ /dev/null
@@ -1,638 +0,0 @@
-From 831d8bc7117ddb24507bd60f4c9c0df37b4b9699 Mon Sep 17 00:00:00 2001
-From: Andrew Comminos <andrew@comminos.com>
-Date: Wed, 28 Feb 2018 22:46:31 -0800
-Subject: [PATCH 8/8] wined3d: Implement lazy-free using a deferred free list.
-
----
- dlls/wined3d/buffer_heap.c | 325 ++++++++++++++++++++++++++++-------------
- dlls/wined3d/cs.c | 12 +-
- dlls/wined3d/device.c | 16 +-
- dlls/wined3d/wined3d_private.h | 22 ++-
- 4 files changed, 261 insertions(+), 114 deletions(-)
-
-diff --git a/dlls/wined3d/buffer_heap.c b/dlls/wined3d/buffer_heap.c
-index f4af1b93b9..4d90dcf861 100644
---- a/dlls/wined3d/buffer_heap.c
-+++ b/dlls/wined3d/buffer_heap.c
-@@ -27,24 +27,20 @@ WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
-
- struct wined3d_buffer_heap_element
- {
-- struct wine_rb_entry entry;
- struct wined3d_map_range range;
-
-+ // rbtree data
-+ struct wine_rb_entry entry;
-+ BOOL in_tree;
-+
- // Binned free list positions
- struct wined3d_buffer_heap_element *next;
- struct wined3d_buffer_heap_element *prev;
- };
-
--struct wined3d_buffer_heap_range
--{
-- struct wined3d_map_range range;
--
-- struct wined3d_buffer_heap_range *next;
--};
--
- struct wined3d_buffer_heap_fenced_element
- {
-- struct wined3d_buffer_heap_range *ranges;
-+ struct wined3d_buffer_heap_bin_set free_list;
- struct wined3d_fence *fence;
-
- struct wined3d_buffer_heap_fenced_element *next;
-@@ -53,13 +49,12 @@ struct wined3d_buffer_heap_fenced_element
- static struct wined3d_buffer_heap_element* element_new(GLsizei offset, GLsizei size)
- {
- struct wined3d_buffer_heap_element* elem;
-- elem = HeapAlloc(GetProcessHeap(), 0, sizeof(struct wined3d_buffer_heap_element));
-+ elem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct wined3d_buffer_heap_element));
- if (!elem)
- return NULL;
- elem->range.offset = offset;
- elem->range.size = size;
-- elem->prev = NULL;
-- elem->next = NULL;
-+ elem->in_tree = FALSE;
- return elem;
- }
-
-@@ -86,27 +81,41 @@ static int element_bin(struct wined3d_buffer_heap_element *elem)
- return min(WINED3D_BUFFER_HEAP_BINS - 1, bitwise_log2_floor(elem->range.size));
- }
-
--// Inserts and element into the free tree and its bin.
--// Does not coalesce.
--static void element_insert_free(struct wined3d_buffer_heap *heap, struct wined3d_buffer_heap_element *elem)
-+// Inserts an element into the appropriate free list bin.
-+static void element_insert_free_bin(struct wined3d_buffer_heap *heap, struct wined3d_buffer_heap_element *elem)
- {
- int bin = element_bin(elem);
-
- elem->prev = NULL;
-- elem->next = heap->free_bins[bin];
-- if (heap->free_bins[bin])
-- heap->free_bins[bin]->prev = elem;
-- heap->free_bins[bin] = elem;
-+ elem->next = heap->free_list.bins[bin].head;
-+ if (heap->free_list.bins[bin].head)
-+ heap->free_list.bins[bin].head->prev = elem;
-+ heap->free_list.bins[bin].head = elem;
-
-+ if (!heap->free_list.bins[bin].tail)
-+ heap->free_list.bins[bin].tail = elem;
-+
-+ TRACE("Inserted allocation at %p of size %lld into bin %d\n", elem->range.offset, elem->range.size, bin);
-+}
-+
-+// Inserts an elemnet into the free tree. Does not perform coalescing.
-+static void element_insert_free_tree(struct wined3d_buffer_heap *heap, struct wined3d_buffer_heap_element *elem)
-+{
-+ if (elem->in_tree)
-+ {
-+ FIXME("Element %p already in free tree, ignoring.\n", elem);
-+ return;
-+ }
- if (wine_rb_put(&heap->free_tree, &elem->range.offset, &elem->entry) == -1)
- {
- ERR("Failed to insert element into free tree.\n");
-+ return;
- }
--
-- TRACE("Inserted allocation at %p of size %lld into bin %d\n", elem->range.offset, elem->range.size, bin);
-+ TRACE("Inserted allocation at %p of size %lld into free tree\n", elem->range.offset, elem->range.size);
-+ elem->in_tree = TRUE;
- }
-
--// Removes an element from the free tree and its bin.
-+// Removes an element from the free tree, its bin, and the coalesce list.
- static void element_remove_free(struct wined3d_buffer_heap *heap, struct wined3d_buffer_heap_element *elem)
- {
- int bin = element_bin(elem);
-@@ -117,24 +126,31 @@ static void element_remove_free(struct wined3d_buffer_heap *heap, struct wined3d
- if (elem->next)
- elem->next->prev = elem->prev;
-
-- if (!elem->prev)
-- heap->free_bins[bin] = elem->next;
-+ if (elem == heap->free_list.bins[bin].head)
-+ heap->free_list.bins[bin].head = elem->next;
-+
-+ if (elem == heap->free_list.bins[bin].tail)
-+ heap->free_list.bins[bin].tail = elem->prev;
-
- elem->prev = NULL;
- elem->next = NULL;
-
-- wine_rb_remove(&heap->free_tree, &elem->entry);
-+ if (elem->in_tree)
-+ {
-+ wine_rb_remove(&heap->free_tree, &elem->entry);
-+ elem->in_tree = FALSE;
-+ }
-
- TRACE("Freed allocation at %p of size %lld from bin %d\n", elem->range.offset, elem->range.size, bin);
- }
-
--static struct wined3d_buffer_heap_fenced_element* fenced_element_new(struct wined3d_buffer_heap_range *ranges, struct wined3d_fence* fence)
-+static struct wined3d_buffer_heap_fenced_element* fenced_element_new(struct wined3d_buffer_heap_bin_set bins, struct wined3d_fence* fence)
- {
- struct wined3d_buffer_heap_fenced_element* elem;
-- elem = HeapAlloc(GetProcessHeap(), 0, sizeof(struct wined3d_buffer_heap_fenced_element));
-+ elem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct wined3d_buffer_heap_fenced_element));
- if (!elem)
- return NULL;
-- elem->ranges = ranges;
-+ elem->free_list = bins;
- elem->fence = fence;
- elem->next = NULL;
- return elem;
-@@ -163,6 +179,11 @@ HRESULT wined3d_buffer_heap_create(struct wined3d_context *context, GLsizeiptr s
-
- struct wined3d_buffer_heap *object;
-
-+ if ((alignment & (alignment - 1)) != 0)
-+ {
-+ return E_FAIL;
-+ }
-+
- if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
- {
- return E_OUTOFMEMORY;
-@@ -194,13 +215,13 @@ HRESULT wined3d_buffer_heap_create(struct wined3d_context *context, GLsizeiptr s
-
- object->fenced_head = object->fenced_tail = NULL;
- object->alignment = alignment;
-- object->pending_fenced_bytes = 0;
-- object->pending_fenced_head = NULL;
-- object->pending_fenced_threshold_bytes = size / 4; // FIXME(acomminos): make this externally declared
-+ // FIXME(acomminos): make this externally declared
-+ object->pending_fenced_threshold_bytes = 16 * 1024 * 1024;
- InitializeCriticalSection(&object->temp_lock);
-
- initial_elem = element_new(0, size);
-- element_insert_free(object, initial_elem);
-+ // Don't bother adding the initial allocation to the coalescing tree.
-+ element_insert_free_bin(object, initial_elem);
-
- *buffer_heap = object;
-
-@@ -217,21 +238,24 @@ HRESULT wined3d_buffer_heap_destroy(struct wined3d_buffer_heap *heap, struct win
- HRESULT wined3d_buffer_heap_alloc(struct wined3d_buffer_heap *heap, GLsizeiptr size, struct wined3d_map_range *out_range)
- {
- int initial_bin;
-+ int initial_size = size;
-+
- EnterCriticalSection(&heap->temp_lock);
-
-- // Round to the nearest power of two to reduce fragmentation.
-- size = 1ULL << bitwise_log2_ceil(size);
-+ // After alignment, reduce fragmentation by rounding to next power of two.
-+ // If the alignment is a power of two (which it should be), this should be
-+ // no problem.
-+ size = 1 << bitwise_log2_ceil(size);
-
-- // Round up the size to a multiple of the heap's alignment.
-- if (heap->alignment)
-+ // Align size values where possible.
-+ if (heap->alignment && (size % heap->alignment != 0))
- size += heap->alignment - (size % heap->alignment);
-
-- // TODO(acomminos): use bitwise arithmetic instead
- initial_bin = min(WINED3D_BUFFER_HEAP_BINS - 1, bitwise_log2_ceil(size));
-
- for (int i = initial_bin; i < WINED3D_BUFFER_HEAP_BINS; i++)
- {
-- struct wined3d_buffer_heap_element *elem = heap->free_bins[i];
-+ struct wined3d_buffer_heap_element *elem = heap->free_list.bins[i].head;
- if (elem)
- {
- struct wined3d_map_range remaining_range;
-@@ -241,13 +265,17 @@ HRESULT wined3d_buffer_heap_alloc(struct wined3d_buffer_heap *heap, GLsizeiptr s
- out_range->offset = elem->range.offset;
- out_range->size = size;
-
-+ TRACE_(d3d_perf)("Allocated %d (requested %d) at %p from bin %d (initial %d)\n", size, initial_size, elem->range.offset, i, initial_bin);
-+
- // Remove the element from its current free bin to move it to the correct list.
- element_remove_free(heap, elem);
-
- if (remaining_range.size > 0)
- {
-+ TRACE_(d3d_perf)("Imperfect fit allocated, fragmenting remainder of %lld at %p.\n", remaining_range.size, remaining_range.offset);
-+
- elem->range = remaining_range;
-- element_insert_free(heap, elem);
-+ element_insert_free_bin(heap, elem);
- }
- else
- {
-@@ -260,68 +288,33 @@ HRESULT wined3d_buffer_heap_alloc(struct wined3d_buffer_heap *heap, GLsizeiptr s
- }
-
- LeaveCriticalSection(&heap->temp_lock);
-+
-+ // Attempt to coalesce blocks until an allocation of the requested size is
-+ // available.
-+ GLsizei coalesced_size;
-+ while (SUCCEEDED(wined3d_buffer_heap_deferred_coalesce(heap, &coalesced_size)))
-+ {
-+ FIXME_(d3d_perf)("Forcing coalesce, not enough free space in buffer heap.\n");
-+ if (coalesced_size >= size)
-+ {
-+ return wined3d_buffer_heap_alloc(heap, size, out_range);
-+ }
-+ }
-+
- return WINED3DERR_OUTOFVIDEOMEMORY;
- }
-
- HRESULT wined3d_buffer_heap_free(struct wined3d_buffer_heap *heap, struct wined3d_map_range range)
- {
- struct wined3d_buffer_heap_element *elem = element_new(range.offset, range.size);
-- struct wined3d_map_range coalesced_range = range;
-- struct wine_rb_entry *entry;
-- HRESULT hr;
-
- if (!elem)
- return E_OUTOFMEMORY;
-
- EnterCriticalSection(&heap->temp_lock);
-
-- // TODO(acomminos): implement lower_bound, upper_bound.
-- // we don't have to allocate a new elem here, this sentry
-- // is just so I can get this proof of concept out the door.
--
-- if (wine_rb_put(&heap->free_tree, &elem->range.offset, &elem->entry) == -1)
-- {
-- LeaveCriticalSection(&heap->temp_lock);
-- HeapFree(GetProcessHeap(), 0, elem);
-- return E_FAIL;
-- }
--
-- // Coalesce left.
-- entry = wine_rb_prev(&elem->entry);
-- if (entry)
-- {
-- TRACE("Coalesced left.\n");
-- struct wined3d_buffer_heap_element *left_elem = WINE_RB_ENTRY_VALUE(entry, struct wined3d_buffer_heap_element, entry);
-- if (left_elem->range.offset + left_elem->range.size == coalesced_range.offset)
-- {
-- coalesced_range.offset = left_elem->range.offset;
-- coalesced_range.size = coalesced_range.size + left_elem->range.size;
--
-- element_remove_free(heap, left_elem);
-- HeapFree(GetProcessHeap(), 0, left_elem);
-- }
-- }
--
-- // Coalesce right.
-- entry = wine_rb_next(&elem->entry);
-- if (entry)
-- {
-- TRACE("Coalesced right.\n");
-- struct wined3d_buffer_heap_element *right_elem = WINE_RB_ENTRY_VALUE(entry, struct wined3d_buffer_heap_element, entry);
-- if (elem->range.offset + elem->range.size == right_elem->range.offset)
-- {
-- coalesced_range.size += right_elem->range.size;
--
-- element_remove_free(heap, right_elem);
-- HeapFree(GetProcessHeap(), 0, right_elem);
-- }
-- }
--
-- wine_rb_remove(&heap->free_tree, &elem->entry);
--
-- // Update with coalesced range.
-- elem->range = coalesced_range;
-- element_insert_free(heap, elem);
-+ // Only insert the element into a free bin, coalescing will occur later.
-+ element_insert_free_bin(heap, elem);
-
- LeaveCriticalSection(&heap->temp_lock);
-
-@@ -330,10 +323,21 @@ HRESULT wined3d_buffer_heap_free(struct wined3d_buffer_heap *heap, struct wined3
-
- HRESULT wined3d_buffer_heap_free_fenced(struct wined3d_buffer_heap *heap, struct wined3d_device *device, struct wined3d_map_range range)
- {
-- struct wined3d_buffer_heap_range *elem = HeapAlloc(GetProcessHeap(), 0, sizeof(struct wined3d_buffer_heap_range));
-- elem->range = range;
-- elem->next = heap->pending_fenced_head;
-- heap->pending_fenced_head = elem;
-+ struct wined3d_buffer_heap_element *elem = element_new(range.offset, range.size);
-+ int bin_index = element_bin(elem);
-+ struct wined3d_buffer_heap_bin *bin = &heap->pending_fenced_bins.bins[bin_index];
-+
-+ if (bin->tail)
-+ {
-+ bin->tail->next = elem;
-+ elem->prev = bin->tail;
-+ bin->tail = elem;
-+ }
-+ else
-+ {
-+ bin->head = elem;
-+ bin->tail = elem;
-+ }
-
- heap->pending_fenced_bytes += range.size;
- if (heap->pending_fenced_bytes >= heap->pending_fenced_threshold_bytes)
-@@ -349,13 +353,13 @@ HRESULT wined3d_buffer_heap_free_fenced(struct wined3d_buffer_heap *heap, struct
- return hr;
- }
-
-- fenced_elem = fenced_element_new(heap->pending_fenced_head, fence);
-+ fenced_elem = fenced_element_new(heap->pending_fenced_bins, fence);
- if (!fenced_elem)
- return E_OUTOFMEMORY;
-
- TRACE_(d3d_perf)("Dispatching fenced buffer set.\n");
- heap->pending_fenced_bytes = 0;
-- heap->pending_fenced_head = NULL;
-+ memset(&heap->pending_fenced_bins, 0, sizeof(heap->pending_fenced_bins));
-
- // Append to end of fenced list, which works well if you assume that buffers
- // are freed in some ascending draw call ordering.
-@@ -390,15 +394,33 @@ HRESULT wined3d_buffer_heap_cs_poll_fences(struct wined3d_buffer_heap *heap, str
- case WINED3D_FENCE_NOT_STARTED:
- {
- TRACE_(d3d_perf)("Freed fence group.\n");
-- struct wined3d_buffer_heap_range *range_elem = elem->ranges;
-- // FIXME(acomminos): this might take a while. incrementally do this?
-- while (range_elem)
-+
-+ EnterCriticalSection(&heap->temp_lock);
-+ for (int i = 0; i < WINED3D_BUFFER_HEAP_BINS; i++)
- {
-- struct wined3d_buffer_heap_range *next = range_elem->next;
-- wined3d_buffer_heap_free(heap, range_elem->range);
-- HeapFree(GetProcessHeap(), 0, range_elem);
-- range_elem = next;
-+ struct wined3d_buffer_heap_bin *elem_bin = &elem->free_list.bins[i];
-+ if (!elem_bin->tail)
-+ continue;
-+
-+ struct wined3d_buffer_heap_bin *heap_bin = &heap->free_list.bins[i];
-+ if (heap_bin->head)
-+ {
-+ // Insert to front.
-+ elem_bin->tail->next = heap_bin->head;
-+ heap_bin->head->prev = elem_bin->tail;
-+
-+ elem_bin->head->prev = NULL;
-+ heap_bin->head = elem_bin->head;
-+ }
-+ else
-+ {
-+ elem_bin->head->prev = NULL;
-+ heap_bin->head = elem_bin->head;
-+ elem_bin->tail->next = NULL;
-+ heap_bin->tail = elem_bin->tail;
-+ }
- }
-+ LeaveCriticalSection(&heap->temp_lock);
-
- wined3d_fence_destroy(elem->fence);
-
-@@ -413,3 +435,102 @@ HRESULT wined3d_buffer_heap_cs_poll_fences(struct wined3d_buffer_heap *heap, str
-
- return WINED3D_OK;
- }
-+
-+HRESULT wined3d_buffer_heap_deferred_coalesce(struct wined3d_buffer_heap *heap, GLsizei *coalesced_size)
-+{
-+ struct wined3d_buffer_heap_element *elem = NULL;
-+ struct wine_rb_entry *entry;
-+ struct wined3d_map_range coalesced_range;
-+
-+ // XXX(acomminos): is it always the best idea to coalesce by smallest
-+ // chunks? these are the most likely to be useless.
-+ EnterCriticalSection(&heap->temp_lock);
-+
-+ // TODO(acomminos): on one hand, if there's a lot of elements in the list,
-+ // it's highly fragmented. on the other, we can potentially waste a decent
-+ // sum of time checking for uncoalesced bins.
-+ for (int i = 0; i < WINED3D_BUFFER_HEAP_BINS && elem == NULL; i++)
-+ {
-+ struct wined3d_buffer_heap_element *next = heap->free_list.bins[i].head;
-+ while (next)
-+ {
-+ if (next->in_tree == FALSE)
-+ {
-+ // Find the first element not in-tree.
-+ elem = next;
-+ break;
-+ }
-+ next = next->next;
-+ }
-+ }
-+
-+ // TODO(acomminos): acquire a separate lock for the free tree here.
-+ if (!elem)
-+ {
-+ ERR("Failed to find element to coalesce.\n");
-+ LeaveCriticalSection(&heap->temp_lock);
-+ return E_FAIL;
-+ }
-+ element_remove_free(heap, elem);
-+
-+ // Remove element from free list, we may change its size or offset.
-+ coalesced_range = elem->range;
-+
-+ // TODO(acomminos): implement lower_bound, upper_bound.
-+ // we don't have to allocate a new elem here, this sentry
-+ // is just so I can get this proof of concept out the door.
-+
-+ if (wine_rb_put(&heap->free_tree, &elem->range.offset, &elem->entry) == -1)
-+ {
-+ LeaveCriticalSection(&heap->temp_lock);
-+ return E_FAIL;
-+ }
-+
-+ // Coalesce left.
-+ entry = wine_rb_prev(&elem->entry);
-+ if (entry)
-+ {
-+ TRACE("Coalesced left.\n");
-+ struct wined3d_buffer_heap_element *left_elem = WINE_RB_ENTRY_VALUE(entry, struct wined3d_buffer_heap_element, entry);
-+ if (left_elem->range.offset + left_elem->range.size == coalesced_range.offset)
-+ {
-+ coalesced_range.offset = left_elem->range.offset;
-+ coalesced_range.size += left_elem->range.size;
-+
-+ element_remove_free(heap, left_elem);
-+ HeapFree(GetProcessHeap(), 0, left_elem);
-+ }
-+ }
-+
-+ // Coalesce right.
-+ entry = wine_rb_next(&elem->entry);
-+ if (entry)
-+ {
-+ TRACE("Coalesced right.\n");
-+ struct wined3d_buffer_heap_element *right_elem = WINE_RB_ENTRY_VALUE(entry, struct wined3d_buffer_heap_element, entry);
-+ if (elem->range.offset + elem->range.size == right_elem->range.offset)
-+ {
-+ coalesced_range.size += right_elem->range.size;
-+
-+ element_remove_free(heap, right_elem);
-+ HeapFree(GetProcessHeap(), 0, right_elem);
-+ }
-+ }
-+
-+ wine_rb_remove(&heap->free_tree, &elem->entry);
-+
-+ if (coalesced_range.size > elem->range.size)
-+ FIXME_(d3d_perf)("Coalesced out an extra %lld bytes\n", coalesced_range.size - elem->range.size);
-+
-+ // Update with coalesced range.
-+ elem->range = coalesced_range;
-+
-+ if (coalesced_size)
-+ *coalesced_size = coalesced_range.size;
-+
-+ element_insert_free_bin(heap, elem);
-+ element_insert_free_tree(heap, elem);
-+
-+ LeaveCriticalSection(&heap->temp_lock);
-+ return WINED3D_OK;
-+}
-diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c
-index bae5d9f4a1..8fd9b01a36 100644
---- a/dlls/wined3d/cs.c
-+++ b/dlls/wined3d/cs.c
-@@ -2644,10 +2644,6 @@ static void wined3d_cs_exec_discard_buffer(struct wined3d_cs *cs, const void *da
- struct wined3d_buffer *buffer = op->buffer;
- HRESULT hr;
-
-- // Poll for discarded buffers whose fenced have been triggered here to avoid
-- // excessive VRAM consumption.
-- wined3d_buffer_heap_cs_poll_fences(buffer->buffer_heap, cs->device);
--
- // TODO(acomminos): should call into buffer.c here instead.
- if (FAILED(hr = wined3d_buffer_heap_free_fenced(buffer->buffer_heap, cs->device, buffer->cs_persistent_map)))
- {
-@@ -2975,6 +2971,14 @@ static void poll_queries(struct wined3d_cs *cs)
- list_init(&query->poll_list_entry);
- InterlockedIncrement(&query->counter_retrieved);
- }
-+
-+ // Poll for discarded persistent buffers whose fences have been triggered
-+ // here to avoid excessive VRAM consumption.
-+ // XXX(acomminos): clean this up, integrate with prior section.
-+ if (cs->device->wo_buffer_heap)
-+ wined3d_buffer_heap_cs_poll_fences(cs->device->wo_buffer_heap, cs->device);
-+ if (cs->device->cb_buffer_heap)
-+ wined3d_buffer_heap_cs_poll_fences(cs->device->cb_buffer_heap, cs->device);
- }
-
- static void wined3d_cs_wait_event(struct wined3d_cs *cs)
-diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
-index bdab83b935..9f300ca572 100644
---- a/dlls/wined3d/device.c
-+++ b/dlls/wined3d/device.c
-@@ -848,26 +848,32 @@ static void destroy_default_samplers(struct wined3d_device *device, struct wined
- /* Context activation is done by the caller. */
- static void create_buffer_heap(struct wined3d_device *device, struct wined3d_context *context)
- {
-+ const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
- // TODO(acomminos): check if ARB_buffer_storage is supported, first-
- // possibly make wined3d_buffer_heap_create fail.
-- // TODO(acomminos): 512MB is sane for geometry, maybe not for PBO.
-- const GLsizeiptr HBO_SIZE = min(512000000, device->adapter->vram_bytes / 4);
-- const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
-+ // TODO(acomminos): kill this magic number. perhaps base on vram.
-+ GLsizeiptr geo_heap_size = 512 * 1024 * 1024;
-+ GLsizeiptr cb_heap_size = 256 * 1024 * 1024;
-
- GLint ub_alignment;
- gl_info->gl_ops.gl.p_glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &ub_alignment);
-
-+ // Align constant buffer heap size, in case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT isn't a power of two (for some reason).
-+ cb_heap_size -= cb_heap_size % ub_alignment;
-+
- HRESULT hr;
-- if (FAILED(hr = wined3d_buffer_heap_create(context, HBO_SIZE, 0, TRUE, &device->wo_buffer_heap)))
-+ if (FAILED(hr = wined3d_buffer_heap_create(context, geo_heap_size, 0, TRUE, &device->wo_buffer_heap)))
- {
- ERR("Failed to create write-only persistent buffer heap, hr %#x.\n", hr);
- }
-
- // TODO(acomminos): can likely use a way smaller heap for CBs by querying limits
-- if (FAILED(hr = wined3d_buffer_heap_create(context, HBO_SIZE, ub_alignment, TRUE, &device->cb_buffer_heap)))
-+ if (FAILED(hr = wined3d_buffer_heap_create(context, cb_heap_size, ub_alignment, TRUE, &device->cb_buffer_heap)))
- {
- ERR("Failed to create persistent buffer heap for constant buffers, hr %#x.\n", hr);
- }
-+
-+ FIXME("Initialized wine-pba (geo_heap_size: %lld, cb_heap_size: %lld, ub_align: %d)\n", geo_heap_size, cb_heap_size, ub_alignment);
- }
-
- /* Context activation is done by the caller. */
-diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
-index 14cad92f0f..3011609ee1 100644
---- a/dlls/wined3d/wined3d_private.h
-+++ b/dlls/wined3d/wined3d_private.h
-@@ -3667,11 +3667,21 @@ enum wined3d_buffer_conversion_type
-
- struct wined3d_buffer_heap_element;
- struct wined3d_buffer_heap_fenced_element;
--struct wined3d_buffer_heap_range;
-
- // Number of power-of-two buckets to populate.
- #define WINED3D_BUFFER_HEAP_BINS 32
-
-+struct wined3d_buffer_heap_bin
-+{
-+ struct wined3d_buffer_heap_element *head;
-+ struct wined3d_buffer_heap_element *tail;
-+};
-+
-+struct wined3d_buffer_heap_bin_set
-+{
-+ struct wined3d_buffer_heap_bin bins[WINED3D_BUFFER_HEAP_BINS];
-+};
-+
- // A heap that manages allocations with a single GL buffer.
- struct wined3d_buffer_heap
- {
-@@ -3680,11 +3690,11 @@ struct wined3d_buffer_heap
- GLsizeiptr alignment;
- CRITICAL_SECTION temp_lock; // Temporary lock while we implement the fenced free list.
-
-- struct wined3d_buffer_heap_element *free_bins[WINED3D_BUFFER_HEAP_BINS];
-+ struct wined3d_buffer_heap_bin_set free_list;
- struct wine_rb_tree free_tree; // Free regions keyed on their base address.
-
- // Elements that need to be fenced, but haven't reached the required size.
-- struct wined3d_buffer_heap_range *pending_fenced_head;
-+ struct wined3d_buffer_heap_bin_set pending_fenced_bins;
- GLsizeiptr pending_fenced_bytes; // Number of free bytes in the active fenced region.
- GLsizeiptr pending_fenced_threshold_bytes; // Number of bytes required before fencing.
-
-@@ -3696,6 +3706,7 @@ struct wined3d_buffer_heap
- HRESULT wined3d_buffer_heap_create(struct wined3d_context *context, GLsizeiptr size, GLsizeiptr alignment, BOOL write_only, struct wined3d_buffer_heap **heap) DECLSPEC_HIDDEN;
- HRESULT wined3d_buffer_heap_destroy(struct wined3d_buffer_heap *heap, struct wined3d_context *context) DECLSPEC_HIDDEN;
- // Fetches a buffer from the heap of at least the given size.
-+// Attempts to coalesce blocks under memory pressure.
- HRESULT wined3d_buffer_heap_alloc(struct wined3d_buffer_heap *heap, GLsizeiptr size, struct wined3d_map_range* out_range) DECLSPEC_HIDDEN;
- // Immediately frees a heap-allocated buffer segment.
- HRESULT wined3d_buffer_heap_free(struct wined3d_buffer_heap *heap, struct wined3d_map_range range) DECLSPEC_HIDDEN;
-@@ -3704,6 +3715,11 @@ HRESULT wined3d_buffer_heap_free_fenced(struct wined3d_buffer_heap *heap, struct
- // Moves a buffers with a signaled fence from the fenced list to the free list.
- // Must be executed on the CS thread.
- HRESULT wined3d_buffer_heap_cs_poll_fences(struct wined3d_buffer_heap *heap, struct wined3d_device *device) DECLSPEC_HIDDEN;
-+// Performs deferred coalescing of fenced buffers. To be called when the CS
-+// thread is idle, or under memory pressure.
-+// Outputs the size of the new coalesced region in `coalesced_size`, or an error
-+// if there are no remaining elements to be coalesced.
-+HRESULT wined3d_buffer_heap_deferred_coalesce(struct wined3d_buffer_heap *heap, GLsizei *coalesced_size) DECLSPEC_HIDDEN;
-
- struct wined3d_buffer
- {
---
-2.16.2
-