summarylogtreecommitdiffstats
path: root/0007-wined3d-Avoid-freeing-persistent-buffer-heap-element.patch
blob: d0872df82707ff1b5026793f24a6b60dca092186 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
From fc7907d5264c1606477f9287c949c3c8794859ec Mon Sep 17 00:00:00 2001
From: Andrew Comminos <andrew@comminos.com>
Date: Thu, 8 Mar 2018 23:01:50 -0800
Subject: [PATCH 7/9] wined3d: Avoid freeing persistent buffer heap elements
 during use.

Using HeapFree is expensive, especially when we don't have our buffers
for long.
---
 dlls/wined3d/buffer.c          | 29 +++++++++++----------
 dlls/wined3d/buffer_heap.c     | 57 ++++++++++++++++++------------------------
 dlls/wined3d/context.c         |  4 +--
 dlls/wined3d/cs.c              |  6 ++---
 dlls/wined3d/wined3d_private.h | 25 ++++++++++++------
 5 files changed, 61 insertions(+), 60 deletions(-)

diff --git a/dlls/wined3d/buffer.c b/dlls/wined3d/buffer.c
index c492fcc8c6..74b3ba8abd 100644
--- a/dlls/wined3d/buffer.c
+++ b/dlls/wined3d/buffer.c
@@ -276,7 +276,7 @@ static BOOL buffer_alloc_persistent_map(struct wined3d_buffer *buffer)
 {
     struct wined3d_device *device = buffer->resource.device;
     struct wined3d_buffer_heap *heap;
-    struct wined3d_map_range map_range;
+    struct wined3d_buffer_heap_element *elem;
     HRESULT hr;
 
     if (buffer->bind_flags & WINED3D_BIND_CONSTANT_BUFFER)
@@ -292,12 +292,12 @@ static BOOL buffer_alloc_persistent_map(struct wined3d_buffer *buffer)
     }
 
     buffer->buffer_heap = heap;
-    if (FAILED(hr = wined3d_buffer_heap_alloc(heap, buffer->resource.size, &map_range)))
+    if (FAILED(hr = wined3d_buffer_heap_alloc(heap, buffer->resource.size, &elem)))
     {
         goto fail;
     }
-    buffer->cs_persistent_map = map_range;
-    buffer->mt_persistent_map = map_range;
+    buffer->cs_persistent_map = elem;
+    buffer->mt_persistent_map = elem;
     return TRUE;
 
 fail:
@@ -753,7 +753,7 @@ BOOL wined3d_buffer_load_location(struct wined3d_buffer *buffer,
             if (buffer->conversion_map)
                 FIXME("Attempting to use conversion map with persistent mapping.\n");
             memcpy(buffer->buffer_heap->map_ptr +
-                   buffer->cs_persistent_map.offset,
+                   buffer->cs_persistent_map->range.offset,
                    buffer->resource.heap_memory, buffer->resource.size);
             break;
 
@@ -801,11 +801,11 @@ DWORD wined3d_buffer_get_memory(struct wined3d_buffer *buffer,
     {
         // FIXME(acomminos): should we expose a buffer object we don't wholly own here?
         data->buffer_object = buffer->buffer_heap->buffer_object;
-        data->addr = buffer->cs_persistent_map.offset;
+        data->addr = buffer->cs_persistent_map->range.offset;
         // Note that the size of the underlying buffer allocation may be larger
         // than the buffer knows about. In this case, we've rounded it up to be
         // aligned (e.g. for uniform buffer offsets).
-        data->length = buffer->cs_persistent_map.size;
+        data->length = buffer->cs_persistent_map->range.size;
         return WINED3D_LOCATION_PERSISTENT_MAP;
     }
     if (locations & WINED3D_LOCATION_SYSMEM)
@@ -1122,7 +1122,7 @@ static HRESULT wined3d_buffer_map(struct wined3d_buffer *buffer, UINT offset, UI
         gl_info->gl_ops.gl.p_glFinish();
 
         base = buffer->buffer_heap->map_ptr
-             + buffer->cs_persistent_map.offset;
+             + buffer->cs_persistent_map->range.offset;
         *data = base + offset;
 
         context_release(context);
@@ -1412,22 +1412,21 @@ static HRESULT buffer_resource_sub_resource_map(struct wined3d_resource *resourc
         if (flags & WINED3D_MAP_DISCARD)
         {
             HRESULT hr;
-            struct wined3d_map_range map_range;
-            if (FAILED(hr = wined3d_buffer_heap_alloc(buffer->buffer_heap, resource->size, &map_range)))
+            struct wined3d_buffer_heap_element *mt_elem;
+            if (FAILED(hr = wined3d_buffer_heap_alloc(buffer->buffer_heap, resource->size, &mt_elem)))
             {
                 FIXME_(d3d_perf)("Failed to allocate new buffer, falling back to sync path.\n");
                 return hr;
             }
-            map_desc->data = buffer->buffer_heap->map_ptr + map_range.offset + offset;
+            map_desc->data = buffer->buffer_heap->map_ptr + mt_elem->range.offset + offset;
             resource->map_count++;
 
-            buffer->mt_persistent_map = map_range;
+            buffer->mt_persistent_map = mt_elem;
 
             // Discard handler on CSMT thread is responsible for returning the
             // currently used buffer to the free pool, along with the fence that
             // must be called before the buffer can be reused.
-            wined3d_cs_emit_discard_buffer(resource->device->cs, buffer, map_range);
-
+            wined3d_cs_emit_discard_buffer(resource->device->cs, buffer, mt_elem);
             return WINED3D_OK;
         }
         else if (flags & WINED3D_MAP_NOOVERWRITE)
@@ -1435,7 +1434,7 @@ static HRESULT buffer_resource_sub_resource_map(struct wined3d_resource *resourc
             // Allow immediate access for persistent buffers without a fence.
             // Always use the latest buffer in this case in case the latest
             // DISCARDed one hasn't reached the command stream yet.
-            struct wined3d_map_range map_range = buffer->mt_persistent_map;
+            struct wined3d_map_range map_range = buffer->mt_persistent_map->range;
             map_desc->data = buffer->buffer_heap->map_ptr + map_range.offset + offset;
             resource->map_count++;
 
diff --git a/dlls/wined3d/buffer_heap.c b/dlls/wined3d/buffer_heap.c
index 75f84b0088..80670c515f 100644
--- a/dlls/wined3d/buffer_heap.c
+++ b/dlls/wined3d/buffer_heap.c
@@ -25,18 +25,6 @@
 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;
@@ -82,6 +70,11 @@ static int element_bin(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)
 {
+    if (elem->prev || elem->next)
+    {
+        ERR("Element %p in already in a free list (for some reason).\n", elem);
+    }
+
     int bin = element_bin(elem);
 
     elem->prev = NULL;
@@ -206,7 +199,7 @@ HRESULT wined3d_buffer_heap_destroy(struct wined3d_buffer_heap *heap, struct win
     return WINED3D_OK;
 }
 
-HRESULT wined3d_buffer_heap_alloc(struct wined3d_buffer_heap *heap, GLsizeiptr size, struct wined3d_map_range *out_range)
+HRESULT wined3d_buffer_heap_alloc(struct wined3d_buffer_heap *heap, GLsizeiptr size, struct wined3d_buffer_heap_element **out_elem)
 {
     int initial_bin;
     int initial_size = size;
@@ -233,24 +226,24 @@ HRESULT wined3d_buffer_heap_alloc(struct wined3d_buffer_heap *heap, GLsizeiptr s
             remaining_range.offset = elem->range.offset + size;
             remaining_range.size = elem->range.size - size;
 
-            out_range->offset = elem->range.offset;
-            out_range->size = size;
+            // Take the element from the free list, transferring ownership to
+            // the caller.
+            element_remove_free(heap, elem);
+            // Resize the element so that we can free the remainder.
+            elem->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);
+            *out_elem = elem;
 
-            // Remove the element from its current free bin to move it to the correct list.
-            element_remove_free(heap, elem);
+            TRACE_(d3d_perf)("Allocated %d (requested %d) at %p from bin %d (initial %d)\n", size, initial_size, elem->range.offset, i, initial_bin);
 
             if (remaining_range.size > 0)
             {
+                struct wined3d_buffer_heap_element *remaining_elem;
+
                 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);
+                remaining_elem = element_new(remaining_range.offset, remaining_range.size);
+                element_insert_free_bin(heap, remaining_elem);
             }
 
             LeaveCriticalSection(&heap->temp_lock);
@@ -265,7 +258,7 @@ HRESULT wined3d_buffer_heap_alloc(struct wined3d_buffer_heap *heap, GLsizeiptr s
     if (SUCCEEDED(wined3d_buffer_heap_deferred_coalesce(heap, &num_coalesced)))
     {
         if (num_coalesced > 0)
-            return wined3d_buffer_heap_alloc(heap, size, out_range);
+            return wined3d_buffer_heap_alloc(heap, size, out_elem);
     }
 
     FIXME_(d3d_perf)("Coalescing did not create new blocks, failing.\n");
@@ -273,16 +266,15 @@ HRESULT wined3d_buffer_heap_alloc(struct wined3d_buffer_heap *heap, GLsizeiptr s
     return WINED3DERR_OUTOFVIDEOMEMORY;
 }
 
-HRESULT wined3d_buffer_heap_free(struct wined3d_buffer_heap *heap, struct wined3d_map_range range)
+HRESULT wined3d_buffer_heap_free(struct wined3d_buffer_heap *heap, struct wined3d_buffer_heap_element *elem)
 {
-    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.
+    //
+    // Note that the reason that we pass around wined3d_buffer_heap_element
+    // instead of a range is to avoid frequent HeapAlloc/HeapFree operations
+    // when we're reusing buffers.
     element_insert_free_bin(heap, elem);
 
     LeaveCriticalSection(&heap->temp_lock);
@@ -290,9 +282,8 @@ HRESULT wined3d_buffer_heap_free(struct wined3d_buffer_heap *heap, struct wined3
     return WINED3D_OK;
 }
 
-HRESULT wined3d_buffer_heap_free_fenced(struct wined3d_buffer_heap *heap, struct wined3d_device *device, struct wined3d_map_range range)
+HRESULT wined3d_buffer_heap_free_fenced(struct wined3d_buffer_heap *heap, struct wined3d_device *device, struct wined3d_buffer_heap_element *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];
 
diff --git a/dlls/wined3d/context.c b/dlls/wined3d/context.c
index eae2c3a79d..01aa53597f 100644
--- a/dlls/wined3d/context.c
+++ b/dlls/wined3d/context.c
@@ -5005,9 +5005,9 @@ void draw_primitive(struct wined3d_device *device, const struct wined3d_state *s
     if (parameters->indexed)
     {
         struct wined3d_buffer *index_buffer = state->index_buffer;
-        if (index_buffer->locations & WINED3D_LOCATION_PERSISTENT_MAP)
+        if (index_buffer->cs_persistent_map)
         {
-            idx_data = index_buffer->cs_persistent_map.offset;
+            idx_data = index_buffer->cs_persistent_map->range.offset;
         }
         else if (!index_buffer->buffer_object || !stream_info->all_vbo)
         {
diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c
index e61b8dedbb..d1f665d505 100644
--- a/dlls/wined3d/cs.c
+++ b/dlls/wined3d/cs.c
@@ -444,7 +444,7 @@ struct wined3d_cs_discard_buffer
 {
     enum wined3d_cs_op opcode;
     struct wined3d_buffer *buffer;
-    struct wined3d_map_range map_range;
+    struct wined3d_buffer_heap_element *map_range;
 };
 
 struct wined3d_cs_stop
@@ -2496,14 +2496,14 @@ static void wined3d_cs_exec_discard_buffer(struct wined3d_cs *cs, const void *da
     wined3d_resource_release(&op->buffer->resource);
 }
 
-void wined3d_cs_emit_discard_buffer(struct wined3d_cs *cs, struct wined3d_buffer *buffer, struct wined3d_map_range map_range)
+void wined3d_cs_emit_discard_buffer(struct wined3d_cs *cs, struct wined3d_buffer *buffer, struct wined3d_buffer_heap_element *elem)
 {
     struct wined3d_cs_discard_buffer *op;
 
     op = cs->ops->require_space(cs, sizeof(*op), WINED3D_CS_QUEUE_DEFAULT);
     op->opcode = WINED3D_CS_OP_DISCARD_BUFFER;
     op->buffer = buffer;
-    op->map_range = map_range;
+    op->map_range = elem;
 
     wined3d_resource_acquire(&buffer->resource);
 
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index b3fd0136ff..0114444943 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -3531,6 +3531,18 @@ struct wined3d_map_range
     GLsizeiptr size;
 };
 
+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;
+};
+
 enum wined3d_cs_queue_id
 {
     WINED3D_CS_QUEUE_DEFAULT = 0,
@@ -3677,7 +3689,7 @@ void wined3d_cs_emit_unload_resource(struct wined3d_cs *cs, struct wined3d_resou
 void wined3d_cs_emit_update_sub_resource(struct wined3d_cs *cs, struct wined3d_resource *resource,
         unsigned int sub_resource_idx, const struct wined3d_box *box, const void *data, unsigned int row_pitch,
         unsigned int slice_pitch) DECLSPEC_HIDDEN;
-void wined3d_cs_emit_discard_buffer(struct wined3d_cs *cs, struct wined3d_buffer *buffer, struct wined3d_map_range map_range) DECLSPEC_HIDDEN;
+void wined3d_cs_emit_discard_buffer(struct wined3d_cs *cs, struct wined3d_buffer *buffer, struct wined3d_buffer_heap_element *map_range) DECLSPEC_HIDDEN;
 void wined3d_cs_init_object(struct wined3d_cs *cs,
         void (*callback)(void *object), void *object) DECLSPEC_HIDDEN;
 HRESULT wined3d_cs_map(struct wined3d_cs *cs, struct wined3d_resource *resource, unsigned int sub_resource_idx,
@@ -3711,7 +3723,6 @@ enum wined3d_buffer_conversion_type
     CONV_POSITIONT,
 };
 
-struct wined3d_buffer_heap_element;
 struct wined3d_buffer_heap_fenced_element;
 
 // Number of power-of-two buckets to populate.
@@ -3750,11 +3761,11 @@ HRESULT wined3d_buffer_heap_create(struct wined3d_context *context, GLsizeiptr s
 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;
+HRESULT wined3d_buffer_heap_alloc(struct wined3d_buffer_heap *heap, GLsizeiptr size, struct wined3d_buffer_heap_element** out_elem) 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;
+HRESULT wined3d_buffer_heap_free(struct wined3d_buffer_heap *heap, struct wined3d_buffer_heap_element *elem) 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;
+HRESULT wined3d_buffer_heap_free_fenced(struct wined3d_buffer_heap *heap, struct wined3d_device *device, struct wined3d_buffer_heap_element *elem) 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.
@@ -3793,8 +3804,8 @@ struct wined3d_buffer
 
     /* persistent mapped buffer */
     struct wined3d_buffer_heap *buffer_heap;
-    struct wined3d_map_range cs_persistent_map;
-    struct wined3d_map_range mt_persistent_map; // TODO: make struct list?
+    struct wined3d_buffer_heap_element *cs_persistent_map;
+    struct wined3d_buffer_heap_element *mt_persistent_map;
 };
 
 static inline struct wined3d_buffer *buffer_from_resource(struct wined3d_resource *resource)
-- 
2.16.2