summarylogtreecommitdiffstats
path: root/0001-wined3d-Initial-implementation-of-a-persistent-mappe.patch
diff options
context:
space:
mode:
Diffstat (limited to '0001-wined3d-Initial-implementation-of-a-persistent-mappe.patch')
-rw-r--r--0001-wined3d-Initial-implementation-of-a-persistent-mappe.patch806
1 files changed, 0 insertions, 806 deletions
diff --git a/0001-wined3d-Initial-implementation-of-a-persistent-mappe.patch b/0001-wined3d-Initial-implementation-of-a-persistent-mappe.patch
deleted file mode 100644
index ab14b215569b..000000000000
--- a/0001-wined3d-Initial-implementation-of-a-persistent-mappe.patch
+++ /dev/null
@@ -1,806 +0,0 @@
-From 1f69076549bf2351eb6d8d885b35a46b4dc69813 Mon Sep 17 00:00:00 2001
-From: Andrew Comminos <andrew@comminos.com>
-Date: Mon, 5 Mar 2018 15:38:35 -0800
-Subject: [PATCH 1/9] wined3d: Initial implementation of a persistent mapped
- buffer allocator.
-
----
- dlls/wined3d-csmt/Makefile.in | 1 +
- dlls/wined3d/Makefile.in | 1 +
- dlls/wined3d/buffer_heap.c | 508 +++++++++++++++++++++++++++++++++++++++++
- dlls/wined3d/cs.c | 9 +
- dlls/wined3d/device.c | 52 +++++
- dlls/wined3d/directx.c | 3 +
- dlls/wined3d/query.c | 2 +-
- dlls/wined3d/wined3d_gl.h | 1 +
- dlls/wined3d/wined3d_private.h | 68 +++++-
- 9 files changed, 641 insertions(+), 4 deletions(-)
- create mode 100644 dlls/wined3d/buffer_heap.c
-
-diff --git a/dlls/wined3d-csmt/Makefile.in b/dlls/wined3d-csmt/Makefile.in
-index 1d0458eb46..cb3a5484c6 100644
---- a/dlls/wined3d-csmt/Makefile.in
-+++ b/dlls/wined3d-csmt/Makefile.in
-@@ -8,6 +8,7 @@ C_SRCS = \
- arb_program_shader.c \
- ati_fragment_shader.c \
- buffer.c \
-+ buffer_heap.c \
- context.c \
- cs.c \
- device.c \
-diff --git a/dlls/wined3d/Makefile.in b/dlls/wined3d/Makefile.in
-index b850ba6872..52ef8666fb 100644
---- a/dlls/wined3d/Makefile.in
-+++ b/dlls/wined3d/Makefile.in
-@@ -6,6 +6,7 @@ C_SRCS = \
- arb_program_shader.c \
- ati_fragment_shader.c \
- buffer.c \
-+ buffer_heap.c \
- context.c \
- cs.c \
- device.c \
-diff --git a/dlls/wined3d/buffer_heap.c b/dlls/wined3d/buffer_heap.c
-new file mode 100644
-index 0000000000..b133bd6893
---- /dev/null
-+++ b/dlls/wined3d/buffer_heap.c
-@@ -0,0 +1,508 @@
-+/*
-+ * Copyright 2018 Andrew Comminos
-+ *
-+ * This library is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU Lesser General Public
-+ * License as published by the Free Software Foundation; either
-+ * version 2.1 of the License, or (at your option) any later version.
-+ *
-+ * This library is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-+ * Lesser General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Lesser General Public
-+ * License along with this library; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
-+ *
-+ */
-+
-+#include "config.h"
-+#include "wine/port.h"
-+#include "wine/rbtree.h"
-+#include "wined3d_private.h"
-+
-+WINE_DEFAULT_DEBUG_CHANNEL(d3d);
-+WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
-+
-+struct wined3d_buffer_heap_element
-+{
-+ struct wined3d_map_range range;
-+
-+ // rbtree data
-+ struct wine_rb_entry entry;
-+
-+ // Binned free list positions
-+ struct wined3d_buffer_heap_element *next;
-+ struct wined3d_buffer_heap_element *prev;
-+};
-+
-+struct wined3d_buffer_heap_fenced_element
-+{
-+ struct wined3d_buffer_heap_bin_set free_list;
-+ struct wined3d_fence *fence;
-+
-+ struct wined3d_buffer_heap_fenced_element *next;
-+};
-+
-+static struct wined3d_buffer_heap_element* element_new(GLsizei offset, GLsizei size)
-+{
-+ struct wined3d_buffer_heap_element* elem;
-+ elem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct wined3d_buffer_heap_element));
-+ if (!elem)
-+ return NULL;
-+ elem->range.offset = offset;
-+ elem->range.size = size;
-+ return elem;
-+}
-+
-+static inline int bitwise_log2_floor(GLsizei size)
-+{
-+ // XXX(acomminos): I hope this gets unrolled.
-+ for (int i = 8 * sizeof(GLsizei) - 1; i >= 0; i--)
-+ {
-+ if ((size >> i) & 1) {
-+ return i;
-+ }
-+ }
-+ return 0;
-+}
-+
-+static inline int bitwise_log2_ceil(GLsizei size)
-+{
-+ // Add one to the floor of size if size isn't a power of two.
-+ return bitwise_log2_floor(size) + !!(size & (size - 1));
-+}
-+
-+static int element_bin(struct wined3d_buffer_heap_element *elem)
-+{
-+ return min(WINED3D_BUFFER_HEAP_BINS - 1, bitwise_log2_floor(elem->range.size));
-+}
-+
-+// 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_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);
-+}
-+
-+// 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);
-+
-+ if (elem->prev)
-+ elem->prev->next = elem->next;
-+
-+ if (elem->next)
-+ elem->next->prev = elem->prev;
-+
-+ 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;
-+
-+ 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_bin_set bins, struct wined3d_fence* fence)
-+{
-+ struct wined3d_buffer_heap_fenced_element* elem;
-+ elem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct wined3d_buffer_heap_fenced_element));
-+ if (!elem)
-+ return NULL;
-+ elem->free_list = bins;
-+ elem->fence = fence;
-+ elem->next = NULL;
-+ return elem;
-+}
-+
-+static int free_tree_compare(const void *key, const struct wine_rb_entry *entry)
-+{
-+ const GLsizei offset = *(const GLsizei*) key;
-+ struct wined3d_buffer_heap_element *elem = WINE_RB_ENTRY_VALUE(entry, struct wined3d_buffer_heap_element, entry);
-+
-+ if (offset < elem->range.offset)
-+ return -1;
-+ if (offset > elem->range.offset)
-+ return 1;
-+ return 0;
-+}
-+
-+/* Context activation is done by the caller. */
-+HRESULT wined3d_buffer_heap_create(struct wined3d_context *context, GLsizeiptr size, GLsizeiptr alignment, BOOL write_only, struct wined3d_buffer_heap **buffer_heap)
-+{
-+ const struct wined3d_gl_info *gl_info = context->gl_info;
-+ const GLenum buffer_target = GL_ARRAY_BUFFER;
-+ GLbitfield access_flags;
-+ GLbitfield storage_flags;
-+ struct wined3d_buffer_heap_element *initial_elem;
-+
-+ 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;
-+ }
-+
-+ access_flags = GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT | GL_MAP_WRITE_BIT;
-+ if (!write_only)
-+ {
-+ access_flags |= GL_MAP_READ_BIT;
-+ }
-+ storage_flags = access_flags;
-+
-+ // TODO(acomminos): where should we be checking for errors here?
-+ GL_EXTCALL(glGenBuffers(1, &object->buffer_object));
-+
-+ context_bind_bo(context, buffer_target, object->buffer_object);
-+
-+ // TODO(acomminos): assert glBufferStorage supported?
-+ GL_EXTCALL(glBufferStorage(buffer_target, size, NULL, storage_flags));
-+
-+ if (!(object->map_ptr = GL_EXTCALL(glMapBufferRange(buffer_target, 0, size, access_flags))))
-+ {
-+ ERR("Couldn't map persistent buffer.\n");
-+ return -1; // FIXME(acomminos): proper error code, cleanup
-+ }
-+ context_bind_bo(context, buffer_target, 0);
-+
-+ object->fenced_head = object->fenced_tail = NULL;
-+ object->alignment = alignment;
-+ InitializeCriticalSection(&object->temp_lock);
-+
-+ initial_elem = element_new(0, size);
-+ // Don't bother adding the initial allocation to the coalescing tree.
-+ element_insert_free_bin(object, initial_elem);
-+
-+ *buffer_heap = object;
-+
-+ return WINED3D_OK;
-+}
-+
-+/* Context activation is done by the caller. */
-+HRESULT wined3d_buffer_heap_destroy(struct wined3d_buffer_heap *heap, struct wined3d_context *context)
-+{
-+ FIXME("Unimplemented, leaking buffer");
-+ return WINED3D_OK;
-+}
-+
-+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);
-+
-+ // 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);
-+
-+ // Align size values where possible.
-+ if (heap->alignment && (size % heap->alignment != 0))
-+ size += heap->alignment - (size % heap->alignment);
-+
-+ 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_list.bins[i].head;
-+ if (elem)
-+ {
-+ struct wined3d_map_range remaining_range;
-+ remaining_range.offset = elem->range.offset + size;
-+ remaining_range.size = elem->range.size - size;
-+
-+ 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_bin(heap, elem);
-+ }
-+ else
-+ {
-+ HeapFree(GetProcessHeap(), 0, elem);
-+ }
-+
-+ LeaveCriticalSection(&heap->temp_lock);
-+ return WINED3D_OK;
-+ }
-+ }
-+
-+ LeaveCriticalSection(&heap->temp_lock);
-+
-+ FIXME_(d3d_perf)("Forcing coalesce, not enough free space in buffer heap.\n");
-+ int num_coalesced;
-+ if (SUCCEEDED(wined3d_buffer_heap_deferred_coalesce(heap, &num_coalesced)))
-+ {
-+ if (num_coalesced > 0)
-+ return wined3d_buffer_heap_alloc(heap, size, out_range);
-+ }
-+
-+ FIXME_(d3d_perf)("Coalescing did not create new blocks, failing.\n");
-+
-+ 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);
-+
-+ if (!elem)
-+ return E_OUTOFMEMORY;
-+
-+ EnterCriticalSection(&heap->temp_lock);
-+
-+ // Only insert the element into a free bin, coalescing will occur later.
-+ element_insert_free_bin(heap, elem);
-+
-+ LeaveCriticalSection(&heap->temp_lock);
-+
-+ return WINED3D_OK;
-+}
-+
-+HRESULT wined3d_buffer_heap_free_fenced(struct wined3d_buffer_heap *heap, struct wined3d_device *device, struct wined3d_map_range range)
-+{
-+ 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;
-+ }
-+
-+ return WINED3D_OK;
-+}
-+
-+HRESULT wined3d_buffer_heap_cs_fence_issue(struct wined3d_buffer_heap *heap, struct wined3d_device *device)
-+{
-+ struct wined3d_buffer_heap_fenced_element *fenced_elem;
-+ struct wined3d_fence *fence;
-+ HRESULT hr;
-+
-+ if (heap->fenced_head)
-+ {
-+ // XXX(acomminos): double or triple buffer this?
-+ wined3d_buffer_heap_cs_fence_wait(heap, device);
-+ }
-+
-+ if (FAILED(hr = wined3d_fence_create(device, &fence)))
-+ {
-+ ERR("Failed to create fence.\n");
-+ return hr;
-+ }
-+
-+ fenced_elem = fenced_element_new(heap->pending_fenced_bins, fence);
-+ if (!fenced_elem)
-+ return E_OUTOFMEMORY;
-+
-+ TRACE_(d3d_perf)("Dispatching fenced buffer set.\n");
-+ 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.
-+ if (!heap->fenced_head)
-+ {
-+ heap->fenced_head = fenced_elem;
-+ heap->fenced_tail = fenced_elem;
-+ }
-+ else
-+ {
-+ heap->fenced_tail->next = fenced_elem;
-+ heap->fenced_tail = fenced_elem;
-+ }
-+
-+ wined3d_fence_issue(fence, device);
-+ return WINED3D_OK;
-+}
-+
-+HRESULT wined3d_buffer_heap_cs_fence_wait(struct wined3d_buffer_heap *heap, struct wined3d_device *device)
-+{
-+ enum wined3d_fence_result res;
-+ struct wined3d_buffer_heap_fenced_element *elem = heap->fenced_head;
-+ if (!elem)
-+ return WINED3D_OK;
-+
-+ res = wined3d_fence_wait(elem->fence, device);
-+ switch (res)
-+ {
-+ case WINED3D_FENCE_OK:
-+ case WINED3D_FENCE_NOT_STARTED:
-+ {
-+ TRACE_(d3d_perf)("Freed fence group.\n");
-+
-+ EnterCriticalSection(&heap->temp_lock);
-+ for (int i = 0; i < WINED3D_BUFFER_HEAP_BINS; i++)
-+ {
-+ 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);
-+
-+ heap->fenced_head = elem->next;
-+ HeapFree(GetProcessHeap(), 0, elem);
-+ // TODO(acomminos): bother to null out fenced_tail?
-+ break;
-+ }
-+ default:
-+ return WINED3D_OK;
-+ }
-+
-+ return WINED3D_OK;
-+}
-+
-+HRESULT wined3d_buffer_heap_deferred_coalesce(struct wined3d_buffer_heap *heap, int *coalesced_count)
-+{
-+ struct wined3d_buffer_heap_element *elem = NULL;
-+ struct wined3d_buffer_heap_element *next = NULL;
-+ struct wine_rb_entry *entry;
-+ struct wined3d_map_range coalesced_range;
-+
-+ struct wine_rb_tree free_tree;
-+ int num_coalesced = 0;
-+
-+ wine_rb_init(&free_tree, free_tree_compare);
-+
-+ 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; i++)
-+ {
-+ elem = heap->free_list.bins[i].head;
-+ while (elem)
-+ {
-+ // Insert a sentry. FIXME(acomminos): can skip this with traversal.
-+ if (wine_rb_put(&free_tree, &elem->range.offset, &elem->entry) == -1)
-+ {
-+ ERR("Failed to insert key %x in tree.\n", elem->range.offset);
-+ elem = elem->next;
-+ continue;
-+ }
-+
-+ coalesced_range = elem->range;
-+
-+ // 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;
-+
-+ wine_rb_remove(&free_tree, entry);
-+ element_remove_free(heap, right_elem);
-+ HeapFree(GetProcessHeap(), 0, right_elem);
-+
-+ num_coalesced++;
-+ }
-+ }
-+
-+ // 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;
-+
-+ wine_rb_remove(&free_tree, entry);
-+ element_remove_free(heap, left_elem);
-+ HeapFree(GetProcessHeap(), 0, left_elem);
-+
-+ num_coalesced++;
-+ }
-+ }
-+
-+ next = elem->next;
-+
-+ if (elem->range.size != coalesced_range.size)
-+ {
-+ FIXME_(d3d_perf)("Coalesced range from (%p, %ld) to (%p, %ld)\n", elem->range.offset, elem->range.size, coalesced_range.offset, coalesced_range.size);
-+
-+ wine_rb_remove(&free_tree, &elem->entry);
-+
-+ // Move to the correct free bin.
-+ element_remove_free(heap, elem);
-+ elem->range = coalesced_range;
-+ element_insert_free_bin(heap, elem);
-+
-+ wine_rb_put(&free_tree, &elem->range.offset, &elem->entry);
-+ }
-+
-+ elem = next;
-+ }
-+ }
-+
-+ LeaveCriticalSection(&heap->temp_lock);
-+
-+ FIXME_(d3d_perf)("Performed %d coalesces.\n", num_coalesced);
-+ if (coalesced_count)
-+ *coalesced_count = num_coalesced;
-+
-+ return WINED3D_OK;
-+}
-diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c
-index 3a7c95ddd8..50a4d041cd 100644
---- a/dlls/wined3d/cs.c
-+++ b/dlls/wined3d/cs.c
-@@ -472,6 +472,15 @@ static void wined3d_cs_exec_present(struct wined3d_cs *cs, const void *data)
- }
-
- InterlockedDecrement(&cs->pending_presents);
-+
-+ // FIXME(acomminos): is this the right place to put double-buffered frame
-+ // timing based logic?
-+ // FIXME(acomminos): this conditional sucks, replace with fancier feature check
-+ if (cs->device->wo_buffer_heap && cs->device->cb_buffer_heap)
-+ {
-+ wined3d_buffer_heap_cs_fence_issue(cs->device->wo_buffer_heap, cs->device);
-+ wined3d_buffer_heap_cs_fence_issue(cs->device->cb_buffer_heap, cs->device);
-+ }
- }
-
- void wined3d_cs_emit_present(struct wined3d_cs *cs, struct wined3d_swapchain *swapchain,
-diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
-index e2b27e0cf4..785841a062 100644
---- a/dlls/wined3d/device.c
-+++ b/dlls/wined3d/device.c
-@@ -833,6 +833,53 @@ static void destroy_default_samplers(struct wined3d_device *device, struct wined
- device->null_sampler = NULL;
- }
-
-+/* 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): kill this magic number. perhaps base on vram.
-+ GLsizeiptr geo_heap_size = 512 * 1024 * 1024;
-+ // We choose a constant buffer size of 128MB, the same as NVIDIA claims to
-+ // use in their Direct3D driver for discarded constant buffers.
-+ GLsizeiptr cb_heap_size = 128 * 1024 * 1024;
-+ GLint ub_alignment;
-+ HRESULT hr;
-+
-+ if (gl_info->supported[ARB_BUFFER_STORAGE])
-+ {
-+ 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;
-+
-+ 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);
-+ }
-+
-+ 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 PBA (geo_heap_size: %ld, cb_heap_size: %ld, ub_align: %d)\n", geo_heap_size, cb_heap_size, ub_alignment);
-+ }
-+ else
-+ {
-+ FIXME("Not using PBA, ARB_buffer_storage unsupported.\n");
-+ }
-+}
-+
-+/* Context activation is done by the caller. */
-+static void destroy_buffer_heap(struct wined3d_device *device, struct wined3d_context *context)
-+{
-+ if (device->wo_buffer_heap)
-+ wined3d_buffer_heap_destroy(device->wo_buffer_heap, context);
-+
-+ if (device->cb_buffer_heap)
-+ wined3d_buffer_heap_destroy(device->cb_buffer_heap, context);
-+}
-+
- static LONG fullscreen_style(LONG style)
- {
- /* Make sure the window is managed, otherwise we won't get keyboard input. */
-@@ -997,6 +1044,8 @@ static void wined3d_device_delete_opengl_contexts_cs(void *object)
- device->shader_backend->shader_free_private(device);
- destroy_dummy_textures(device, context);
- destroy_default_samplers(device, context);
-+ destroy_buffer_heap(device, context);
-+
- context_release(context);
-
- while (device->context_count)
-@@ -1045,6 +1094,9 @@ static void wined3d_device_create_primary_opengl_context_cs(void *object)
- context = context_acquire(device, target, 0);
- create_dummy_textures(device, context);
- create_default_samplers(device, context);
-+
-+ create_buffer_heap(device, context);
-+
- context_release(context);
- }
-
-diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c
-index 8720fc7ad6..46c6a59536 100644
---- a/dlls/wined3d/directx.c
-+++ b/dlls/wined3d/directx.c
-@@ -111,6 +111,7 @@ static const struct wined3d_extension_map gl_extension_map[] =
- /* ARB */
- {"GL_ARB_base_instance", ARB_BASE_INSTANCE },
- {"GL_ARB_blend_func_extended", ARB_BLEND_FUNC_EXTENDED },
-+ {"GL_ARB_buffer_storage", ARB_BUFFER_STORAGE },
- {"GL_ARB_clear_buffer_object", ARB_CLEAR_BUFFER_OBJECT },
- {"GL_ARB_clear_texture", ARB_CLEAR_TEXTURE },
- {"GL_ARB_clip_control", ARB_CLIP_CONTROL },
-@@ -2714,6 +2715,8 @@ static void load_gl_funcs(struct wined3d_gl_info *gl_info)
- /* GL_ARB_blend_func_extended */
- USE_GL_FUNC(glBindFragDataLocationIndexed)
- USE_GL_FUNC(glGetFragDataIndex)
-+ /* GL_ARB_buffer_storage */
-+ USE_GL_FUNC(glBufferStorage)
- /* GL_ARB_clear_buffer_object */
- USE_GL_FUNC(glClearBufferData)
- USE_GL_FUNC(glClearBufferSubData)
-diff --git a/dlls/wined3d/query.c b/dlls/wined3d/query.c
-index 5ea79b6e4a..f3ca1630e5 100644
---- a/dlls/wined3d/query.c
-+++ b/dlls/wined3d/query.c
-@@ -88,7 +88,7 @@ static BOOL wined3d_fence_supported(const struct wined3d_gl_info *gl_info)
- return gl_info->supported[ARB_SYNC] || gl_info->supported[NV_FENCE] || gl_info->supported[APPLE_FENCE];
- }
-
--static enum wined3d_fence_result wined3d_fence_test(const struct wined3d_fence *fence,
-+enum wined3d_fence_result wined3d_fence_test(const struct wined3d_fence *fence,
- const struct wined3d_device *device, DWORD flags)
- {
- const struct wined3d_gl_info *gl_info;
-diff --git a/dlls/wined3d/wined3d_gl.h b/dlls/wined3d/wined3d_gl.h
-index 87283c850e..7626864ef2 100644
---- a/dlls/wined3d/wined3d_gl.h
-+++ b/dlls/wined3d/wined3d_gl.h
-@@ -44,6 +44,7 @@ enum wined3d_gl_extension
- /* ARB */
- ARB_BASE_INSTANCE,
- ARB_BLEND_FUNC_EXTENDED,
-+ ARB_BUFFER_STORAGE,
- ARB_CLEAR_BUFFER_OBJECT,
- ARB_CLEAR_TEXTURE,
- ARB_CLIP_CONTROL,
-diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
-index 8aa61d811f..3d535f4e17 100644
---- a/dlls/wined3d/wined3d_private.h
-+++ b/dlls/wined3d/wined3d_private.h
-@@ -1712,6 +1712,9 @@ void wined3d_fence_destroy(struct wined3d_fence *fence) DECLSPEC_HIDDEN;
- void wined3d_fence_issue(struct wined3d_fence *fence, const struct wined3d_device *device) DECLSPEC_HIDDEN;
- enum wined3d_fence_result wined3d_fence_wait(const struct wined3d_fence *fence,
- const struct wined3d_device *device) DECLSPEC_HIDDEN;
-+// XXX(acomminos): really expose this?
-+enum wined3d_fence_result wined3d_fence_test(const struct wined3d_fence *fence,
-+ const struct wined3d_device *device, DWORD flags) DECLSPEC_HIDDEN;
-
- /* Direct3D terminology with little modifications. We do not have an issued
- * state because only the driver knows about it, but we have a created state
-@@ -2993,6 +2996,10 @@ struct wined3d_device
- /* Context management */
- struct wined3d_context **contexts;
- UINT context_count;
-+
-+ /* Dynamic buffer heap */
-+ struct wined3d_buffer_heap *wo_buffer_heap;
-+ struct wined3d_buffer_heap *cb_buffer_heap;
- };
-
- void device_clear_render_targets(struct wined3d_device *device, UINT rt_count, const struct wined3d_fb_state *fb,
-@@ -3513,6 +3520,12 @@ void state_init(struct wined3d_state *state, struct wined3d_fb_state *fb,
- DWORD flags) DECLSPEC_HIDDEN;
- void state_unbind_resources(struct wined3d_state *state) DECLSPEC_HIDDEN;
-
-+struct wined3d_map_range
-+{
-+ GLintptr offset;
-+ GLsizeiptr size;
-+};
-+
- enum wined3d_cs_queue_id
- {
- WINED3D_CS_QUEUE_DEFAULT = 0,
-@@ -3692,12 +3705,61 @@ enum wined3d_buffer_conversion_type
- CONV_POSITIONT,
- };
-
--struct wined3d_map_range
-+struct wined3d_buffer_heap_element;
-+struct wined3d_buffer_heap_fenced_element;
-+
-+// Number of power-of-two buckets to populate.
-+#define WINED3D_BUFFER_HEAP_BINS 32
-+
-+struct wined3d_buffer_heap_bin
- {
-- UINT offset;
-- UINT size;
-+ 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
-+{
-+ GLuint buffer_object;
-+ void *map_ptr;
-+ GLsizeiptr alignment;
-+ CRITICAL_SECTION temp_lock; // Temporary lock while we implement the fenced free list.
-+
-+ struct wined3d_buffer_heap_bin_set free_list;
-+
-+ // Elements that need to be fenced, but haven't reached the required size.
-+ struct wined3d_buffer_heap_bin_set pending_fenced_bins;
-+
-+ // List of sets of buffers behind a common fence, in FIFO order.
-+ struct wined3d_buffer_heap_fenced_element *fenced_head;
-+ struct wined3d_buffer_heap_fenced_element *fenced_tail;
-+};
-+
-+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;
-+// Enqueues a buffer segment to return to the heap once its fence has been signaled.
-+HRESULT wined3d_buffer_heap_free_fenced(struct wined3d_buffer_heap *heap, struct wined3d_device *device, struct wined3d_map_range range) DECLSPEC_HIDDEN;
-+// Issues a fence for the current set of pending fenced buffers.
-+// Double-buffered: if the last fence issued has not yet been triggered, waits
-+// on it.
-+HRESULT wined3d_buffer_heap_cs_fence_issue(struct wined3d_buffer_heap *heap, struct wined3d_device *device) DECLSPEC_HIDDEN;
-+// Waits on the next issued fence in FIFO order. Frees the fenced buffers after
-+// the fence has been triggered.
-+HRESULT wined3d_buffer_heap_cs_fence_wait(struct wined3d_buffer_heap *heap, struct wined3d_device *device) DECLSPEC_HIDDEN;
-+// Performs deferred coalescing of buffers. To be called under memory pressure.
-+// Outputs the number of coalesced regions in `num_coalesced`.
-+HRESULT wined3d_buffer_heap_deferred_coalesce(struct wined3d_buffer_heap *heap, int *num_coalesced) DECLSPEC_HIDDEN;
-+
- struct wined3d_buffer
- {
- struct wined3d_resource resource;
---
-2.16.2
-