xref: /reactos/dll/directx/wine/wined3d/buffer.c (revision 84ccccab)
1 /*
2  * Copyright 2002-2005 Jason Edmeades
3  * Copyright 2002-2005 Raphael Junqueira
4  * Copyright 2004 Christian Costa
5  * Copyright 2005 Oliver Stieber
6  * Copyright 2007-2011, 2013-2014 Stefan Dösinger for CodeWeavers
7  * Copyright 2009-2010 Henri Verbeet for CodeWeavers
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  *
23  */
24 
25 #include "wined3d_private.h"
26 
27 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
28 
29 #define WINED3D_BUFFER_HASDESC      0x01    /* A vertex description has been found. */
30 #define WINED3D_BUFFER_USE_BO       0x02    /* Use a buffer object for this buffer. */
31 #define WINED3D_BUFFER_PIN_SYSMEM   0x04    /* Keep a system memory copy for this buffer. */
32 #define WINED3D_BUFFER_DISCARD      0x08    /* A DISCARD lock has occurred since the last preload. */
33 #define WINED3D_BUFFER_APPLESYNC    0x10    /* Using sync as in GL_APPLE_flush_buffer_range. */
34 
35 #define VB_MAXDECLCHANGES     100     /* After that number of decl changes we stop converting */
36 #define VB_RESETDECLCHANGE    1000    /* Reset the decl changecount after that number of draws */
37 #define VB_MAXFULLCONVERSIONS 5       /* Number of full conversions before we stop converting */
38 #define VB_RESETFULLCONVS     20      /* Reset full conversion counts after that number of draws */
39 
40 static void wined3d_buffer_evict_sysmem(struct wined3d_buffer *buffer)
41 {
42     if (buffer->flags & WINED3D_BUFFER_PIN_SYSMEM)
43     {
44         TRACE("Not evicting system memory for buffer %p.\n", buffer);
45         return;
46     }
47 
48     TRACE("Evicting system memory for buffer %p.\n", buffer);
49     wined3d_buffer_invalidate_location(buffer, WINED3D_LOCATION_SYSMEM);
50     wined3d_resource_free_sysmem(&buffer->resource);
51 }
52 
53 static void buffer_invalidate_bo_range(struct wined3d_buffer *buffer, unsigned int offset, unsigned int size)
54 {
55     if (!offset && (!size || size == buffer->resource.size))
56         goto invalidate_all;
57 
58     if (offset > buffer->resource.size || size > buffer->resource.size - offset)
59     {
60         WARN("Invalid range specified, invalidating entire buffer.\n");
61         goto invalidate_all;
62     }
63 
64     if (!wined3d_array_reserve((void **)&buffer->maps, &buffer->maps_size,
65             buffer->modified_areas + 1, sizeof(*buffer->maps)))
66     {
67         ERR("Failed to allocate maps array, invalidating entire buffer.\n");
68         goto invalidate_all;
69     }
70 
71     buffer->maps[buffer->modified_areas].offset = offset;
72     buffer->maps[buffer->modified_areas].size = size;
73     ++buffer->modified_areas;
74     return;
75 
76 invalidate_all:
77     buffer->modified_areas = 1;
78     buffer->maps[0].offset = 0;
79     buffer->maps[0].size = buffer->resource.size;
80 }
81 
82 static inline void buffer_clear_dirty_areas(struct wined3d_buffer *This)
83 {
84     This->modified_areas = 0;
85 }
86 
87 static BOOL buffer_is_dirty(const struct wined3d_buffer *buffer)
88 {
89     return !!buffer->modified_areas;
90 }
91 
92 static BOOL buffer_is_fully_dirty(const struct wined3d_buffer *buffer)
93 {
94     return buffer->modified_areas == 1
95             && !buffer->maps->offset && buffer->maps->size == buffer->resource.size;
96 }
97 
98 static void wined3d_buffer_validate_location(struct wined3d_buffer *buffer, DWORD location)
99 {
100     TRACE("buffer %p, location %s.\n", buffer, wined3d_debug_location(location));
101 
102     if (location & WINED3D_LOCATION_BUFFER)
103         buffer_clear_dirty_areas(buffer);
104 
105     buffer->locations |= location;
106 
107     TRACE("New locations flags are %s.\n", wined3d_debug_location(buffer->locations));
108 }
109 
110 static void wined3d_buffer_invalidate_range(struct wined3d_buffer *buffer, DWORD location,
111         unsigned int offset, unsigned int size)
112 {
113     TRACE("buffer %p, location %s, offset %u, size %u.\n",
114             buffer, wined3d_debug_location(location), offset, size);
115 
116     if (location & WINED3D_LOCATION_BUFFER)
117         buffer_invalidate_bo_range(buffer, offset, size);
118 
119     buffer->locations &= ~location;
120 
121     TRACE("New locations flags are %s.\n", wined3d_debug_location(buffer->locations));
122 
123     if (!buffer->locations)
124         ERR("Buffer %p does not have any up to date location.\n", buffer);
125 }
126 
127 void wined3d_buffer_invalidate_location(struct wined3d_buffer *buffer, DWORD location)
128 {
129     wined3d_buffer_invalidate_range(buffer, location, 0, 0);
130 }
131 
132 /* Context activation is done by the caller. */
133 static void buffer_bind(struct wined3d_buffer *buffer, struct wined3d_context *context)
134 {
135     context_bind_bo(context, buffer->buffer_type_hint, buffer->buffer_object);
136 }
137 
138 /* Context activation is done by the caller. */
139 static void buffer_destroy_buffer_object(struct wined3d_buffer *buffer, struct wined3d_context *context)
140 {
141     const struct wined3d_gl_info *gl_info = context->gl_info;
142     struct wined3d_resource *resource = &buffer->resource;
143 
144     if (!buffer->buffer_object)
145         return;
146 
147     /* The stream source state handler might have read the memory of the
148      * vertex buffer already and got the memory in the vbo which is not
149      * valid any longer. Dirtify the stream source to force a reload. This
150      * happens only once per changed vertexbuffer and should occur rather
151      * rarely. */
152     if (resource->bind_count)
153     {
154         if (buffer->bind_flags & WINED3D_BIND_VERTEX_BUFFER)
155             device_invalidate_state(resource->device, STATE_STREAMSRC);
156         if (buffer->bind_flags & WINED3D_BIND_INDEX_BUFFER)
157             device_invalidate_state(resource->device, STATE_INDEXBUFFER);
158         if (buffer->bind_flags & WINED3D_BIND_CONSTANT_BUFFER)
159         {
160             device_invalidate_state(resource->device, STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_VERTEX));
161             device_invalidate_state(resource->device, STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_HULL));
162             device_invalidate_state(resource->device, STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_DOMAIN));
163             device_invalidate_state(resource->device, STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_GEOMETRY));
164             device_invalidate_state(resource->device, STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_PIXEL));
165             device_invalidate_state(resource->device, STATE_CONSTANT_BUFFER(WINED3D_SHADER_TYPE_COMPUTE));
166         }
167         if (buffer->bind_flags & WINED3D_BIND_STREAM_OUTPUT)
168         {
169             device_invalidate_state(resource->device, STATE_STREAM_OUTPUT);
170             if (context->transform_feedback_active)
171             {
172                 /* We have to make sure that transform feedback is not active
173                  * when deleting a potentially bound transform feedback buffer.
174                  * This may happen when the device is being destroyed. */
175                 WARN("Deleting buffer object for buffer %p, disabling transform feedback.\n", buffer);
176                 context_end_transform_feedback(context);
177             }
178         }
179     }
180 
181     GL_EXTCALL(glDeleteBuffers(1, &buffer->buffer_object));
182     checkGLcall("glDeleteBuffers");
183     buffer->buffer_object = 0;
184 
185     if (buffer->fence)
186     {
187         wined3d_fence_destroy(buffer->fence);
188         buffer->fence = NULL;
189     }
190     buffer->flags &= ~WINED3D_BUFFER_APPLESYNC;
191 }
192 
193 /* Context activation is done by the caller. */
194 static BOOL buffer_create_buffer_object(struct wined3d_buffer *buffer, struct wined3d_context *context)
195 {
196     const struct wined3d_gl_info *gl_info = context->gl_info;
197     GLenum gl_usage = GL_STATIC_DRAW;
198     GLenum error;
199 
200     TRACE("Creating an OpenGL buffer object for wined3d_buffer %p with usage %s.\n",
201             buffer, debug_d3dusage(buffer->resource.usage));
202 
203     /* Make sure that the gl error is cleared. Do not use checkGLcall
204      * here because checkGLcall just prints a fixme and continues. However,
205      * if an error during VBO creation occurs we can fall back to non-VBO operation
206      * with full functionality(but performance loss).
207      */
208     while (gl_info->gl_ops.gl.p_glGetError() != GL_NO_ERROR);
209 
210     /* Basically the FVF parameter passed to CreateVertexBuffer is no good.
211      * The vertex declaration from the device determines how the data in the
212      * buffer is interpreted. This means that on each draw call the buffer has
213      * to be verified to check if the rhw and color values are in the correct
214      * format. */
215 
216     GL_EXTCALL(glGenBuffers(1, &buffer->buffer_object));
217     error = gl_info->gl_ops.gl.p_glGetError();
218     if (!buffer->buffer_object || error != GL_NO_ERROR)
219     {
220         ERR("Failed to create a BO with error %s (%#x).\n", debug_glerror(error), error);
221         goto fail;
222     }
223 
224     buffer_bind(buffer, context);
225     error = gl_info->gl_ops.gl.p_glGetError();
226     if (error != GL_NO_ERROR)
227     {
228         ERR("Failed to bind the BO with error %s (%#x).\n", debug_glerror(error), error);
229         goto fail;
230     }
231 
232     if (buffer->resource.usage & WINED3DUSAGE_DYNAMIC)
233     {
234         TRACE("Buffer has WINED3DUSAGE_DYNAMIC set.\n");
235         gl_usage = GL_STREAM_DRAW_ARB;
236 
237         if (gl_info->supported[APPLE_FLUSH_BUFFER_RANGE])
238         {
239             GL_EXTCALL(glBufferParameteriAPPLE(buffer->buffer_type_hint, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE));
240             GL_EXTCALL(glBufferParameteriAPPLE(buffer->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE));
241             checkGLcall("glBufferParameteriAPPLE");
242             buffer->flags |= WINED3D_BUFFER_APPLESYNC;
243         }
244         /* No setup is needed here for GL_ARB_map_buffer_range. */
245     }
246 
247     GL_EXTCALL(glBufferData(buffer->buffer_type_hint, buffer->resource.size, NULL, gl_usage));
248     error = gl_info->gl_ops.gl.p_glGetError();
249     if (error != GL_NO_ERROR)
250     {
251         ERR("glBufferData failed with error %s (%#x).\n", debug_glerror(error), error);
252         goto fail;
253     }
254 
255     buffer->buffer_object_usage = gl_usage;
256     buffer_invalidate_bo_range(buffer, 0, 0);
257 
258     return TRUE;
259 
260 fail:
261     /* Clean up all BO init, but continue because we can work without a BO :-) */
262     ERR("Failed to create a buffer object. Continuing, but performance issues may occur.\n");
263     buffer->flags &= ~WINED3D_BUFFER_USE_BO;
264     buffer_destroy_buffer_object(buffer, context);
265     buffer_clear_dirty_areas(buffer);
266     return FALSE;
267 }
268 
269 static BOOL buffer_process_converted_attribute(struct wined3d_buffer *buffer,
270         const enum wined3d_buffer_conversion_type conversion_type,
271         const struct wined3d_stream_info_element *attrib, DWORD *stride_this_run)
272 {
273     const struct wined3d_format *format = attrib->format;
274     BOOL ret = FALSE;
275     unsigned int i;
276     DWORD_PTR data;
277 
278     /* Check for some valid situations which cause us pain. One is if the buffer is used for
279      * constant attributes(stride = 0), the other one is if the buffer is used on two streams
280      * with different strides. In the 2nd case we might have to drop conversion entirely,
281      * it is possible that the same bytes are once read as FLOAT2 and once as UBYTE4N.
282      */
283     if (!attrib->stride)
284     {
285         FIXME("%s used with stride 0, let's hope we get the vertex stride from somewhere else.\n",
286                 debug_d3dformat(format->id));
287     }
288     else if (attrib->stride != *stride_this_run && *stride_this_run)
289     {
290         FIXME("Got two concurrent strides, %d and %d.\n", attrib->stride, *stride_this_run);
291     }
292     else
293     {
294         *stride_this_run = attrib->stride;
295         if (buffer->stride != *stride_this_run)
296         {
297             /* We rely that this happens only on the first converted attribute that is found,
298              * if at all. See above check
299              */
300             TRACE("Reconverting because converted attributes occur, and the stride changed.\n");
301             buffer->stride = *stride_this_run;
302             HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer->conversion_map);
303             buffer->conversion_map = wined3d_calloc(buffer->stride, sizeof(*buffer->conversion_map));
304             ret = TRUE;
305         }
306     }
307 
308     data = ((DWORD_PTR)attrib->data.addr) % buffer->stride;
309     for (i = 0; i < format->attribute_size; ++i)
310     {
311         DWORD_PTR idx = (data + i) % buffer->stride;
312         if (buffer->conversion_map[idx] != conversion_type)
313         {
314             TRACE("Byte %lu in vertex changed:\n", idx);
315             TRACE("    It was type %#x, is %#x now.\n", buffer->conversion_map[idx], conversion_type);
316             ret = TRUE;
317             buffer->conversion_map[idx] = conversion_type;
318         }
319     }
320 
321     return ret;
322 }
323 
324 #define WINED3D_BUFFER_FIXUP_D3DCOLOR   0x01
325 #define WINED3D_BUFFER_FIXUP_XYZRHW     0x02
326 
327 static BOOL buffer_check_attribute(struct wined3d_buffer *This, const struct wined3d_stream_info *si,
328         const struct wined3d_state *state, UINT attrib_idx, DWORD fixup_flags, DWORD *stride_this_run)
329 {
330     const struct wined3d_stream_info_element *attrib = &si->elements[attrib_idx];
331     enum wined3d_format_id format;
332     BOOL ret = FALSE;
333 
334     /* Ignore attributes that do not have our vbo. After that check we can be sure that the attribute is
335      * there, on nonexistent attribs the vbo is 0.
336      */
337     if (!(si->use_map & (1u << attrib_idx))
338             || state->streams[attrib->stream_idx].buffer != This)
339         return FALSE;
340 
341     format = attrib->format->id;
342     /* Look for newly appeared conversion */
343     if (fixup_flags & WINED3D_BUFFER_FIXUP_D3DCOLOR && format == WINED3DFMT_B8G8R8A8_UNORM)
344     {
345         ret = buffer_process_converted_attribute(This, CONV_D3DCOLOR, attrib, stride_this_run);
346     }
347     else if (fixup_flags & WINED3D_BUFFER_FIXUP_XYZRHW && si->position_transformed)
348     {
349         if (format != WINED3DFMT_R32G32B32A32_FLOAT)
350         {
351             FIXME("Unexpected format %s for transformed position.\n", debug_d3dformat(format));
352             return FALSE;
353         }
354 
355         ret = buffer_process_converted_attribute(This, CONV_POSITIONT, attrib, stride_this_run);
356     }
357     else if (This->conversion_map)
358     {
359         ret = buffer_process_converted_attribute(This, CONV_NONE, attrib, stride_this_run);
360     }
361 
362     return ret;
363 }
364 
365 static BOOL buffer_find_decl(struct wined3d_buffer *This, const struct wined3d_stream_info *si,
366         const struct wined3d_state *state, DWORD fixup_flags)
367 {
368     UINT stride_this_run = 0;
369     BOOL ret = FALSE;
370 
371     /* In d3d7 the vertex buffer declaration NEVER changes because it is stored in the d3d7 vertex buffer.
372      * Once we have our declaration there is no need to look it up again. Index buffers also never need
373      * conversion, so once the (empty) conversion structure is created don't bother checking again
374      */
375     if (This->flags & WINED3D_BUFFER_HASDESC)
376     {
377         if(This->resource.usage & WINED3DUSAGE_STATICDECL) return FALSE;
378     }
379 
380     if (!fixup_flags)
381     {
382         TRACE("No fixup required.\n");
383         if(This->conversion_map)
384         {
385             HeapFree(GetProcessHeap(), 0, This->conversion_map);
386             This->conversion_map = NULL;
387             This->stride = 0;
388             return TRUE;
389         }
390 
391         return FALSE;
392     }
393 
394     TRACE("Finding vertex buffer conversion information\n");
395     /* Certain declaration types need some fixups before we can pass them to
396      * opengl. This means D3DCOLOR attributes with fixed function vertex
397      * processing, FLOAT4 POSITIONT with fixed function, and FLOAT16 if
398      * GL_ARB_half_float_vertex is not supported.
399      *
400      * Note for d3d8 and d3d9:
401      * The vertex buffer FVF doesn't help with finding them, we have to use
402      * the decoded vertex declaration and pick the things that concern the
403      * current buffer. A problem with this is that this can change between
404      * draws, so we have to validate the information and reprocess the buffer
405      * if it changes, and avoid false positives for performance reasons.
406      * WineD3D doesn't even know the vertex buffer any more, it is managed
407      * by the client libraries and passed to SetStreamSource and ProcessVertices
408      * as needed.
409      *
410      * We have to distinguish between vertex shaders and fixed function to
411      * pick the way we access the strided vertex information.
412      *
413      * This code sets up a per-byte array with the size of the detected
414      * stride of the arrays in the buffer. For each byte we have a field
415      * that marks the conversion needed on this byte. For example, the
416      * following declaration with fixed function vertex processing:
417      *
418      *      POSITIONT, FLOAT4
419      *      NORMAL, FLOAT3
420      *      DIFFUSE, FLOAT16_4
421      *      SPECULAR, D3DCOLOR
422      *
423      * Will result in
424      * {                 POSITIONT                    }{             NORMAL                }{    DIFFUSE          }{SPECULAR }
425      * [P][P][P][P][P][P][P][P][P][P][P][P][P][P][P][P][0][0][0][0][0][0][0][0][0][0][0][0][F][F][F][F][F][F][F][F][C][C][C][C]
426      *
427      * Where in this example map P means 4 component position conversion, 0
428      * means no conversion, F means FLOAT16_2 conversion and C means D3DCOLOR
429      * conversion (red / blue swizzle).
430      *
431      * If we're doing conversion and the stride changes we have to reconvert
432      * the whole buffer. Note that we do not mind if the semantic changes,
433      * we only care for the conversion type. So if the NORMAL is replaced
434      * with a TEXCOORD, nothing has to be done, or if the DIFFUSE is replaced
435      * with a D3DCOLOR BLENDWEIGHT we can happily dismiss the change. Some
436      * conversion types depend on the semantic as well, for example a FLOAT4
437      * texcoord needs no conversion while a FLOAT4 positiont needs one
438      */
439 
440     ret = buffer_check_attribute(This, si, state, WINED3D_FFP_POSITION,
441             fixup_flags, &stride_this_run) || ret;
442     fixup_flags &= ~WINED3D_BUFFER_FIXUP_XYZRHW;
443 
444     ret = buffer_check_attribute(This, si, state, WINED3D_FFP_BLENDWEIGHT,
445             fixup_flags, &stride_this_run) || ret;
446     ret = buffer_check_attribute(This, si, state, WINED3D_FFP_BLENDINDICES,
447             fixup_flags, &stride_this_run) || ret;
448     ret = buffer_check_attribute(This, si, state, WINED3D_FFP_NORMAL,
449             fixup_flags, &stride_this_run) || ret;
450     ret = buffer_check_attribute(This, si, state, WINED3D_FFP_DIFFUSE,
451             fixup_flags, &stride_this_run) || ret;
452     ret = buffer_check_attribute(This, si, state, WINED3D_FFP_SPECULAR,
453             fixup_flags, &stride_this_run) || ret;
454     ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD0,
455             fixup_flags, &stride_this_run) || ret;
456     ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD1,
457             fixup_flags, &stride_this_run) || ret;
458     ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD2,
459             fixup_flags, &stride_this_run) || ret;
460     ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD3,
461             fixup_flags, &stride_this_run) || ret;
462     ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD4,
463             fixup_flags, &stride_this_run) || ret;
464     ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD5,
465             fixup_flags, &stride_this_run) || ret;
466     ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD6,
467             fixup_flags, &stride_this_run) || ret;
468     ret = buffer_check_attribute(This, si, state, WINED3D_FFP_TEXCOORD7,
469             fixup_flags, &stride_this_run) || ret;
470 
471     if (!stride_this_run && This->conversion_map)
472     {
473         /* Sanity test */
474         if (!ret) ERR("no converted attributes found, old conversion map exists, and no declaration change?\n");
475         HeapFree(GetProcessHeap(), 0, This->conversion_map);
476         This->conversion_map = NULL;
477         This->stride = 0;
478     }
479 
480     if (ret) TRACE("Conversion information changed\n");
481 
482     return ret;
483 }
484 
485 static inline unsigned int fixup_d3dcolor(DWORD *dst_color)
486 {
487     DWORD src_color = *dst_color;
488 
489     /* Color conversion like in draw_primitive_immediate_mode(). Watch out for
490      * endianness. If we want this to work on big-endian machines as well we
491      * have to consider more things.
492      *
493      * 0xff000000: Alpha mask
494      * 0x00ff0000: Blue mask
495      * 0x0000ff00: Green mask
496      * 0x000000ff: Red mask
497      */
498     *dst_color = 0;
499     *dst_color |= (src_color & 0xff00ff00u);         /* Alpha Green */
500     *dst_color |= (src_color & 0x00ff0000u) >> 16;   /* Red */
501     *dst_color |= (src_color & 0x000000ffu) << 16;   /* Blue */
502 
503     return sizeof(*dst_color);
504 }
505 
506 static inline unsigned int fixup_transformed_pos(struct wined3d_vec4 *p)
507 {
508     /* rhw conversion like in position_float4(). */
509     if (p->w != 1.0f && p->w != 0.0f)
510     {
511         float w = 1.0f / p->w;
512         p->x *= w;
513         p->y *= w;
514         p->z *= w;
515         p->w = w;
516     }
517 
518     return sizeof(*p);
519 }
520 
521 ULONG CDECL wined3d_buffer_incref(struct wined3d_buffer *buffer)
522 {
523     ULONG refcount = InterlockedIncrement(&buffer->resource.ref);
524 
525     TRACE("%p increasing refcount to %u.\n", buffer, refcount);
526 
527     return refcount;
528 }
529 
530 /* Context activation is done by the caller. */
531 static void wined3d_buffer_upload_ranges(struct wined3d_buffer *buffer, struct wined3d_context *context,
532         const void *data, unsigned int range_count, const struct wined3d_map_range *ranges)
533 {
534     const struct wined3d_gl_info *gl_info = context->gl_info;
535     const struct wined3d_map_range *range;
536 
537     buffer_bind(buffer, context);
538 
539     while (range_count--)
540     {
541         range = &ranges[range_count];
542         GL_EXTCALL(glBufferSubData(buffer->buffer_type_hint,
543                 range->offset, range->size, (BYTE *)data + range->offset));
544     }
545     checkGLcall("glBufferSubData");
546 }
547 
548 static void buffer_conversion_upload(struct wined3d_buffer *buffer, struct wined3d_context *context)
549 {
550     unsigned int i, j, range_idx, start, end, vertex_count;
551     BYTE *data;
552 
553     if (!wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_SYSMEM))
554     {
555         ERR("Failed to load system memory.\n");
556         return;
557     }
558     buffer->flags |= WINED3D_BUFFER_PIN_SYSMEM;
559 
560     /* Now for each vertex in the buffer that needs conversion. */
561     vertex_count = buffer->resource.size / buffer->stride;
562 
563     if (!(data = HeapAlloc(GetProcessHeap(), 0, buffer->resource.size)))
564     {
565         ERR("Out of memory.\n");
566         return;
567     }
568 
569     for (range_idx = 0; range_idx < buffer->modified_areas; ++range_idx)
570     {
571         start = buffer->maps[range_idx].offset;
572         end = start + buffer->maps[range_idx].size;
573 
574         memcpy(data + start, (BYTE *)buffer->resource.heap_memory + start, end - start);
575         for (i = start / buffer->stride; i < min((end / buffer->stride) + 1, vertex_count); ++i)
576         {
577             for (j = 0; j < buffer->stride;)
578             {
579                 switch (buffer->conversion_map[j])
580                 {
581                     case CONV_NONE:
582                         /* Done already */
583                         j += sizeof(DWORD);
584                         break;
585                     case CONV_D3DCOLOR:
586                         j += fixup_d3dcolor((DWORD *) (data + i * buffer->stride + j));
587                         break;
588                     case CONV_POSITIONT:
589                         j += fixup_transformed_pos((struct wined3d_vec4 *) (data + i * buffer->stride + j));
590                         break;
591                     default:
592                         FIXME("Unimplemented conversion %d in shifted conversion.\n", buffer->conversion_map[j]);
593                         ++j;
594                 }
595             }
596         }
597     }
598 
599     wined3d_buffer_upload_ranges(buffer, context, data, buffer->modified_areas, buffer->maps);
600 
601     HeapFree(GetProcessHeap(), 0, data);
602 }
603 
604 static BOOL wined3d_buffer_prepare_location(struct wined3d_buffer *buffer,
605         struct wined3d_context *context, DWORD location)
606 {
607     switch (location)
608     {
609         case WINED3D_LOCATION_SYSMEM:
610             if (buffer->resource.heap_memory)
611                 return TRUE;
612 
613             if (!wined3d_resource_allocate_sysmem(&buffer->resource))
614             {
615                 ERR("Failed to allocate system memory.\n");
616                 return FALSE;
617             }
618             return TRUE;
619 
620         case WINED3D_LOCATION_BUFFER:
621             if (buffer->buffer_object)
622                 return TRUE;
623 
624             if (!(buffer->flags & WINED3D_BUFFER_USE_BO))
625             {
626                 WARN("Trying to create BO for buffer %p with no WINED3D_BUFFER_USE_BO.\n", buffer);
627                 return FALSE;
628             }
629             return buffer_create_buffer_object(buffer, context);
630 
631         default:
632             ERR("Invalid location %s.\n", wined3d_debug_location(location));
633             return FALSE;
634     }
635 }
636 
637 BOOL wined3d_buffer_load_location(struct wined3d_buffer *buffer,
638         struct wined3d_context *context, DWORD location)
639 {
640     const struct wined3d_gl_info *gl_info = context->gl_info;
641 
642     TRACE("buffer %p, context %p, location %s.\n",
643             buffer, context, wined3d_debug_location(location));
644 
645     if (buffer->locations & location)
646     {
647         TRACE("Location (%#x) is already up to date.\n", location);
648         return TRUE;
649     }
650 
651     if (!buffer->locations)
652     {
653         ERR("Buffer %p does not have any up to date location.\n", buffer);
654         wined3d_buffer_validate_location(buffer, WINED3D_LOCATION_DISCARDED);
655         return wined3d_buffer_load_location(buffer, context, location);
656     }
657 
658     TRACE("Current buffer location %s.\n", wined3d_debug_location(buffer->locations));
659 
660     if (!wined3d_buffer_prepare_location(buffer, context, location))
661         return FALSE;
662 
663     if (buffer->locations & WINED3D_LOCATION_DISCARDED)
664     {
665         TRACE("Buffer previously discarded, nothing to do.\n");
666         wined3d_buffer_validate_location(buffer, location);
667         wined3d_buffer_invalidate_location(buffer, WINED3D_LOCATION_DISCARDED);
668         return TRUE;
669     }
670 
671     switch (location)
672     {
673         case WINED3D_LOCATION_SYSMEM:
674             buffer_bind(buffer, context);
675             GL_EXTCALL(glGetBufferSubData(buffer->buffer_type_hint, 0, buffer->resource.size,
676                     buffer->resource.heap_memory));
677             checkGLcall("buffer download");
678             break;
679 
680         case WINED3D_LOCATION_BUFFER:
681             if (!buffer->conversion_map)
682                 wined3d_buffer_upload_ranges(buffer, context, buffer->resource.heap_memory,
683                         buffer->modified_areas, buffer->maps);
684             else
685                 buffer_conversion_upload(buffer, context);
686             break;
687 
688         default:
689             ERR("Invalid location %s.\n", wined3d_debug_location(location));
690             return FALSE;
691     }
692 
693     wined3d_buffer_validate_location(buffer, location);
694     if (buffer->resource.heap_memory && location == WINED3D_LOCATION_BUFFER
695             && !(buffer->resource.usage & WINED3DUSAGE_DYNAMIC))
696         wined3d_buffer_evict_sysmem(buffer);
697 
698     return TRUE;
699 }
700 
701 /* Context activation is done by the caller. */
702 BYTE *wined3d_buffer_load_sysmem(struct wined3d_buffer *buffer, struct wined3d_context *context)
703 {
704     if (wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_SYSMEM))
705         buffer->flags |= WINED3D_BUFFER_PIN_SYSMEM;
706     return buffer->resource.heap_memory;
707 }
708 
709 DWORD wined3d_buffer_get_memory(struct wined3d_buffer *buffer,
710         struct wined3d_bo_address *data, DWORD locations)
711 {
712     TRACE("buffer %p, data %p, locations %s.\n",
713             buffer, data, wined3d_debug_location(locations));
714 
715     if (locations & WINED3D_LOCATION_BUFFER)
716     {
717         data->buffer_object = buffer->buffer_object;
718         data->addr = NULL;
719         return WINED3D_LOCATION_BUFFER;
720     }
721     if (locations & WINED3D_LOCATION_SYSMEM)
722     {
723         data->buffer_object = 0;
724         data->addr = buffer->resource.heap_memory;
725         return WINED3D_LOCATION_SYSMEM;
726     }
727 
728     ERR("Unexpected locations %s.\n", wined3d_debug_location(locations));
729     data->buffer_object = 0;
730     data->addr = NULL;
731     return 0;
732 }
733 
734 static void buffer_unload(struct wined3d_resource *resource)
735 {
736     struct wined3d_buffer *buffer = buffer_from_resource(resource);
737 
738     TRACE("buffer %p.\n", buffer);
739 
740     if (buffer->buffer_object)
741     {
742         struct wined3d_context *context;
743 
744         context = context_acquire(resource->device, NULL, 0);
745 
746         wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_SYSMEM);
747         wined3d_buffer_invalidate_location(buffer, WINED3D_LOCATION_BUFFER);
748         buffer_destroy_buffer_object(buffer, context);
749         buffer_clear_dirty_areas(buffer);
750 
751         context_release(context);
752 
753         HeapFree(GetProcessHeap(), 0, buffer->conversion_map);
754         buffer->conversion_map = NULL;
755         buffer->stride = 0;
756         buffer->conversion_stride = 0;
757         buffer->flags &= ~WINED3D_BUFFER_HASDESC;
758     }
759 
760     resource_unload(resource);
761 }
762 
763 static void wined3d_buffer_drop_bo(struct wined3d_buffer *buffer)
764 {
765     buffer->flags &= ~WINED3D_BUFFER_USE_BO;
766     buffer_unload(&buffer->resource);
767 }
768 
769 static void wined3d_buffer_destroy_object(void *object)
770 {
771     struct wined3d_buffer *buffer = object;
772     struct wined3d_context *context;
773 
774     if (buffer->buffer_object)
775     {
776         context = context_acquire(buffer->resource.device, NULL, 0);
777         buffer_destroy_buffer_object(buffer, context);
778         context_release(context);
779 
780         HeapFree(GetProcessHeap(), 0, buffer->conversion_map);
781     }
782 
783     HeapFree(GetProcessHeap(), 0, buffer->maps);
784     HeapFree(GetProcessHeap(), 0, buffer);
785 }
786 
787 ULONG CDECL wined3d_buffer_decref(struct wined3d_buffer *buffer)
788 {
789     ULONG refcount = InterlockedDecrement(&buffer->resource.ref);
790 
791     TRACE("%p decreasing refcount to %u.\n", buffer, refcount);
792 
793     if (!refcount)
794     {
795         buffer->resource.parent_ops->wined3d_object_destroyed(buffer->resource.parent);
796         resource_cleanup(&buffer->resource);
797         wined3d_cs_destroy_object(buffer->resource.device->cs, wined3d_buffer_destroy_object, buffer);
798     }
799 
800     return refcount;
801 }
802 
803 void * CDECL wined3d_buffer_get_parent(const struct wined3d_buffer *buffer)
804 {
805     TRACE("buffer %p.\n", buffer);
806 
807     return buffer->resource.parent;
808 }
809 
810 /* The caller provides a context and binds the buffer */
811 static void buffer_sync_apple(struct wined3d_buffer *buffer, DWORD flags, const struct wined3d_gl_info *gl_info)
812 {
813     enum wined3d_fence_result ret;
814     HRESULT hr;
815 
816     /* No fencing needs to be done if the app promises not to overwrite
817      * existing data. */
818     if (flags & WINED3D_MAP_NOOVERWRITE)
819         return;
820 
821     if (flags & WINED3D_MAP_DISCARD)
822     {
823         GL_EXTCALL(glBufferData(buffer->buffer_type_hint, buffer->resource.size, NULL, buffer->buffer_object_usage));
824         checkGLcall("glBufferData");
825         return;
826     }
827 
828     if (!buffer->fence)
829     {
830         TRACE("Creating fence for buffer %p.\n", buffer);
831 
832         if (FAILED(hr = wined3d_fence_create(buffer->resource.device, &buffer->fence)))
833         {
834             if (hr == WINED3DERR_NOTAVAILABLE)
835                 FIXME("Fences not supported, dropping async buffer locks.\n");
836             else
837                 ERR("Failed to create fence, hr %#x.\n", hr);
838             goto drop_fence;
839         }
840 
841         /* Since we don't know about old draws a glFinish is needed once */
842         gl_info->gl_ops.gl.p_glFinish();
843         return;
844     }
845 
846     TRACE("Synchronizing buffer %p.\n", buffer);
847     ret = wined3d_fence_wait(buffer->fence, buffer->resource.device);
848     switch (ret)
849     {
850         case WINED3D_FENCE_NOT_STARTED:
851         case WINED3D_FENCE_OK:
852             /* All done */
853             return;
854 
855         case WINED3D_FENCE_WRONG_THREAD:
856             WARN("Cannot synchronize buffer lock due to a thread conflict.\n");
857             goto drop_fence;
858 
859         default:
860             ERR("wined3d_fence_wait() returned %u, dropping async buffer locks.\n", ret);
861             goto drop_fence;
862     }
863 
864 drop_fence:
865     if (buffer->fence)
866     {
867         wined3d_fence_destroy(buffer->fence);
868         buffer->fence = NULL;
869     }
870 
871     gl_info->gl_ops.gl.p_glFinish();
872     GL_EXTCALL(glBufferParameteriAPPLE(buffer->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_TRUE));
873     checkGLcall("glBufferParameteriAPPLE(buffer->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_TRUE)");
874     buffer->flags &= ~WINED3D_BUFFER_APPLESYNC;
875 }
876 
877 static void buffer_mark_used(struct wined3d_buffer *buffer)
878 {
879     buffer->flags &= ~WINED3D_BUFFER_DISCARD;
880 }
881 
882 /* Context activation is done by the caller. */
883 void wined3d_buffer_load(struct wined3d_buffer *buffer, struct wined3d_context *context,
884         const struct wined3d_state *state)
885 {
886     const struct wined3d_gl_info *gl_info = context->gl_info;
887     BOOL decl_changed = FALSE;
888 
889     TRACE("buffer %p.\n", buffer);
890 
891     if (buffer->resource.map_count)
892     {
893         WARN("Buffer is mapped, skipping preload.\n");
894         return;
895     }
896 
897     buffer_mark_used(buffer);
898 
899     /* TODO: Make converting independent from VBOs */
900     if (!(buffer->flags & WINED3D_BUFFER_USE_BO))
901     {
902         /* Not doing any conversion */
903         return;
904     }
905 
906     if (!wined3d_buffer_prepare_location(buffer, context, WINED3D_LOCATION_BUFFER))
907     {
908         ERR("Failed to prepare buffer location.\n");
909         return;
910     }
911 
912     /* Reading the declaration makes only sense if we have valid state information
913      * (i.e., if this function is called during draws). */
914     if (state)
915     {
916         DWORD fixup_flags = 0;
917 
918         if (!use_vs(state))
919         {
920             if (!gl_info->supported[ARB_VERTEX_ARRAY_BGRA] && !context->d3d_info->ffp_generic_attributes)
921                 fixup_flags |= WINED3D_BUFFER_FIXUP_D3DCOLOR;
922             if (!context->d3d_info->xyzrhw)
923                 fixup_flags |= WINED3D_BUFFER_FIXUP_XYZRHW;
924         }
925 
926         decl_changed = buffer_find_decl(buffer, &context->stream_info, state, fixup_flags);
927         buffer->flags |= WINED3D_BUFFER_HASDESC;
928     }
929 
930     if (!decl_changed && !(buffer->flags & WINED3D_BUFFER_HASDESC && buffer_is_dirty(buffer)))
931     {
932         ++buffer->draw_count;
933         if (buffer->draw_count > VB_RESETDECLCHANGE)
934             buffer->decl_change_count = 0;
935         if (buffer->draw_count > VB_RESETFULLCONVS)
936             buffer->full_conversion_count = 0;
937         return;
938     }
939 
940     /* If applications change the declaration over and over, reconverting all the time is a huge
941      * performance hit. So count the declaration changes and release the VBO if there are too many
942      * of them (and thus stop converting)
943      */
944     if (decl_changed)
945     {
946         ++buffer->decl_change_count;
947         buffer->draw_count = 0;
948 
949         if (buffer->decl_change_count > VB_MAXDECLCHANGES
950                 || (buffer->conversion_map && (buffer->resource.usage & WINED3DUSAGE_DYNAMIC)))
951         {
952             FIXME("Too many declaration changes or converting dynamic buffer, stopping converting.\n");
953             wined3d_buffer_drop_bo(buffer);
954             return;
955         }
956 
957         /* The declaration changed, reload the whole buffer. */
958         WARN("Reloading buffer because of a vertex declaration change.\n");
959         buffer_invalidate_bo_range(buffer, 0, 0);
960     }
961     else
962     {
963         /* However, it is perfectly fine to change the declaration every now and then. We don't want a game that
964          * changes it every minute drop the VBO after VB_MAX_DECL_CHANGES minutes. So count draws without
965          * decl changes and reset the decl change count after a specific number of them
966          */
967         if (buffer->conversion_map && buffer_is_fully_dirty(buffer))
968         {
969             ++buffer->full_conversion_count;
970             if (buffer->full_conversion_count > VB_MAXFULLCONVERSIONS)
971             {
972                 FIXME("Too many full buffer conversions, stopping converting.\n");
973                 wined3d_buffer_drop_bo(buffer);
974                 return;
975             }
976         }
977         else
978         {
979             ++buffer->draw_count;
980             if (buffer->draw_count > VB_RESETDECLCHANGE)
981                 buffer->decl_change_count = 0;
982             if (buffer->draw_count > VB_RESETFULLCONVS)
983                 buffer->full_conversion_count = 0;
984         }
985     }
986 
987     if (!wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_BUFFER))
988         ERR("Failed to load buffer location.\n");
989 }
990 
991 struct wined3d_resource * CDECL wined3d_buffer_get_resource(struct wined3d_buffer *buffer)
992 {
993     TRACE("buffer %p.\n", buffer);
994 
995     return &buffer->resource;
996 }
997 
998 static HRESULT wined3d_buffer_map(struct wined3d_buffer *buffer, UINT offset, UINT size, BYTE **data, DWORD flags)
999 {
1000     struct wined3d_device *device = buffer->resource.device;
1001     struct wined3d_context *context;
1002     LONG count;
1003     BYTE *base;
1004 
1005     TRACE("buffer %p, offset %u, size %u, data %p, flags %#x.\n", buffer, offset, size, data, flags);
1006 
1007     count = ++buffer->resource.map_count;
1008 
1009     if (buffer->buffer_object)
1010     {
1011         unsigned int dirty_offset = offset, dirty_size = size;
1012 
1013         /* DISCARD invalidates the entire buffer, regardless of the specified
1014          * offset and size. Some applications also depend on the entire buffer
1015          * being uploaded in that case. Two such applications are Port Royale
1016          * and Darkstar One. */
1017         if (flags & WINED3D_MAP_DISCARD)
1018         {
1019             dirty_offset = 0;
1020             dirty_size = 0;
1021         }
1022 
1023         if (!(flags & (WINED3D_MAP_NOOVERWRITE | WINED3D_MAP_DISCARD | WINED3D_MAP_READONLY))
1024                 || ((flags & WINED3D_MAP_READONLY) && (buffer->locations & WINED3D_LOCATION_SYSMEM))
1025                 || buffer->flags & WINED3D_BUFFER_PIN_SYSMEM)
1026         {
1027             if (!(buffer->locations & WINED3D_LOCATION_SYSMEM))
1028             {
1029                 context = context_acquire(device, NULL, 0);
1030                 wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_SYSMEM);
1031                 context_release(context);
1032             }
1033 
1034             if (!(flags & WINED3D_MAP_READONLY))
1035                 wined3d_buffer_invalidate_range(buffer, WINED3D_LOCATION_BUFFER, dirty_offset, dirty_size);
1036         }
1037         else
1038         {
1039             const struct wined3d_gl_info *gl_info;
1040 
1041             context = context_acquire(device, NULL, 0);
1042             gl_info = context->gl_info;
1043 
1044             if (flags & WINED3D_MAP_DISCARD)
1045                 wined3d_buffer_validate_location(buffer, WINED3D_LOCATION_BUFFER);
1046             else
1047                 wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_BUFFER);
1048 
1049             if (!(flags & WINED3D_MAP_READONLY))
1050                 buffer_invalidate_bo_range(buffer, dirty_offset, dirty_size);
1051 
1052             if ((flags & WINED3D_MAP_DISCARD) && buffer->resource.heap_memory)
1053                 wined3d_buffer_evict_sysmem(buffer);
1054 
1055             if (count == 1)
1056             {
1057                 buffer_bind(buffer, context);
1058 
1059                 /* Filter redundant WINED3D_MAP_DISCARD maps. The 3DMark2001
1060                  * multitexture fill rate test seems to depend on this. When
1061                  * we map a buffer with GL_MAP_INVALIDATE_BUFFER_BIT, the
1062                  * driver is free to discard the previous contents of the
1063                  * buffer. The r600g driver only does this when the buffer is
1064                  * currently in use, while the proprietary NVIDIA driver
1065                  * appears to do this unconditionally. */
1066                 if (buffer->flags & WINED3D_BUFFER_DISCARD)
1067                     flags &= ~WINED3D_MAP_DISCARD;
1068 
1069                 if (gl_info->supported[ARB_MAP_BUFFER_RANGE])
1070                 {
1071                     GLbitfield mapflags = wined3d_resource_gl_map_flags(flags);
1072                     buffer->map_ptr = GL_EXTCALL(glMapBufferRange(buffer->buffer_type_hint,
1073                             0, buffer->resource.size, mapflags));
1074                     checkGLcall("glMapBufferRange");
1075                 }
1076                 else
1077                 {
1078                     if (buffer->flags & WINED3D_BUFFER_APPLESYNC)
1079                         buffer_sync_apple(buffer, flags, gl_info);
1080                     buffer->map_ptr = GL_EXTCALL(glMapBuffer(buffer->buffer_type_hint,
1081                             GL_READ_WRITE));
1082                     checkGLcall("glMapBuffer");
1083                 }
1084 
1085                 if (((DWORD_PTR)buffer->map_ptr) & (RESOURCE_ALIGNMENT - 1))
1086                 {
1087                     WARN("Pointer %p is not %u byte aligned.\n", buffer->map_ptr, RESOURCE_ALIGNMENT);
1088 
1089                     GL_EXTCALL(glUnmapBuffer(buffer->buffer_type_hint));
1090                     checkGLcall("glUnmapBuffer");
1091                     buffer->map_ptr = NULL;
1092 
1093                     if (buffer->resource.usage & WINED3DUSAGE_DYNAMIC)
1094                     {
1095                         /* The extra copy is more expensive than not using VBOs at
1096                          * all on the Nvidia Linux driver, which is the only driver
1097                          * that returns unaligned pointers.
1098                          */
1099                         TRACE("Dynamic buffer, dropping VBO.\n");
1100                         wined3d_buffer_drop_bo(buffer);
1101                     }
1102                     else
1103                     {
1104                         TRACE("Falling back to doublebuffered operation.\n");
1105                         wined3d_buffer_load_location(buffer, context, WINED3D_LOCATION_SYSMEM);
1106                         buffer->flags |= WINED3D_BUFFER_PIN_SYSMEM;
1107                     }
1108                     TRACE("New pointer is %p.\n", buffer->resource.heap_memory);
1109                 }
1110             }
1111 
1112             context_release(context);
1113         }
1114 
1115         if (flags & WINED3D_MAP_DISCARD)
1116             buffer->flags |= WINED3D_BUFFER_DISCARD;
1117     }
1118 
1119     base = buffer->map_ptr ? buffer->map_ptr : buffer->resource.heap_memory;
1120     *data = base + offset;
1121 
1122     TRACE("Returning memory at %p (base %p, offset %u).\n", *data, base, offset);
1123     /* TODO: check Flags compatibility with buffer->currentDesc.Usage (see MSDN) */
1124 
1125     return WINED3D_OK;
1126 }
1127 
1128 static void wined3d_buffer_unmap(struct wined3d_buffer *buffer)
1129 {
1130     ULONG i;
1131 
1132     TRACE("buffer %p.\n", buffer);
1133 
1134     /* In the case that the number of Unmap calls > the
1135      * number of Map calls, d3d returns always D3D_OK.
1136      * This is also needed to prevent Map from returning garbage on
1137      * the next call (this will happen if the lock_count is < 0). */
1138     if (!buffer->resource.map_count)
1139     {
1140         WARN("Unmap called without a previous map call.\n");
1141         return;
1142     }
1143 
1144     if (--buffer->resource.map_count)
1145     {
1146         /* Delay loading the buffer until everything is unlocked */
1147         TRACE("Ignoring unmap.\n");
1148         return;
1149     }
1150 
1151     if (buffer->map_ptr)
1152     {
1153         struct wined3d_device *device = buffer->resource.device;
1154         const struct wined3d_gl_info *gl_info;
1155         struct wined3d_context *context;
1156 
1157         context = context_acquire(device, NULL, 0);
1158         gl_info = context->gl_info;
1159 
1160         buffer_bind(buffer, context);
1161 
1162         if (gl_info->supported[ARB_MAP_BUFFER_RANGE])
1163         {
1164             for (i = 0; i < buffer->modified_areas; ++i)
1165             {
1166                 GL_EXTCALL(glFlushMappedBufferRange(buffer->buffer_type_hint,
1167                         buffer->maps[i].offset, buffer->maps[i].size));
1168                 checkGLcall("glFlushMappedBufferRange");
1169             }
1170         }
1171         else if (buffer->flags & WINED3D_BUFFER_APPLESYNC)
1172         {
1173             for (i = 0; i < buffer->modified_areas; ++i)
1174             {
1175                 GL_EXTCALL(glFlushMappedBufferRangeAPPLE(buffer->buffer_type_hint,
1176                         buffer->maps[i].offset, buffer->maps[i].size));
1177                 checkGLcall("glFlushMappedBufferRangeAPPLE");
1178             }
1179         }
1180 
1181         GL_EXTCALL(glUnmapBuffer(buffer->buffer_type_hint));
1182         if (wined3d_settings.strict_draw_ordering)
1183             gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
1184         context_release(context);
1185 
1186         buffer_clear_dirty_areas(buffer);
1187         buffer->map_ptr = NULL;
1188     }
1189 }
1190 
1191 void wined3d_buffer_copy(struct wined3d_buffer *dst_buffer, unsigned int dst_offset,
1192         struct wined3d_buffer *src_buffer, unsigned int src_offset, unsigned int size)
1193 {
1194     struct wined3d_bo_address dst, src;
1195     struct wined3d_context *context;
1196     DWORD dst_location;
1197 
1198     buffer_mark_used(dst_buffer);
1199     buffer_mark_used(src_buffer);
1200 
1201     dst_location = wined3d_buffer_get_memory(dst_buffer, &dst, dst_buffer->locations);
1202     dst.addr += dst_offset;
1203 
1204     wined3d_buffer_get_memory(src_buffer, &src, src_buffer->locations);
1205     src.addr += src_offset;
1206 
1207     context = context_acquire(dst_buffer->resource.device, NULL, 0);
1208     context_copy_bo_address(context, &dst, dst_buffer->buffer_type_hint,
1209             &src, src_buffer->buffer_type_hint, size);
1210     context_release(context);
1211 
1212     wined3d_buffer_invalidate_range(dst_buffer, ~dst_location, dst_offset, size);
1213 }
1214 
1215 HRESULT wined3d_buffer_upload_data(struct wined3d_buffer *buffer,
1216         const struct wined3d_box *box, const void *data)
1217 {
1218     UINT offset, size;
1219 #if defined(STAGING_CSMT)
1220     DWORD flags = 0;
1221 #endif /* STAGING_CSMT */
1222     HRESULT hr;
1223     BYTE *ptr;
1224 
1225     if (box)
1226     {
1227         offset = box->left;
1228         size = box->right - box->left;
1229     }
1230     else
1231     {
1232         offset = 0;
1233         size = buffer->resource.size;
1234     }
1235 
1236 #if !defined(STAGING_CSMT)
1237     if (FAILED(hr = wined3d_buffer_map(buffer, offset, size, &ptr, 0)))
1238 #else  /* STAGING_CSMT */
1239     if (offset == 0 && size == buffer->resource.size)
1240         flags = WINED3D_MAP_DISCARD;
1241 
1242     if (FAILED(hr = wined3d_buffer_map(buffer, offset, size, &ptr, flags)))
1243 #endif /* STAGING_CSMT */
1244         return hr;
1245 
1246     memcpy(ptr, data, size);
1247 
1248     wined3d_buffer_unmap(buffer);
1249     return WINED3D_OK;
1250 }
1251 
1252 static ULONG buffer_resource_incref(struct wined3d_resource *resource)
1253 {
1254     return wined3d_buffer_incref(buffer_from_resource(resource));
1255 }
1256 
1257 static ULONG buffer_resource_decref(struct wined3d_resource *resource)
1258 {
1259     return wined3d_buffer_decref(buffer_from_resource(resource));
1260 }
1261 
1262 static void buffer_resource_preload(struct wined3d_resource *resource)
1263 {
1264     struct wined3d_context *context;
1265 
1266     context = context_acquire(resource->device, NULL, 0);
1267     wined3d_buffer_load(buffer_from_resource(resource), context, NULL);
1268     context_release(context);
1269 }
1270 
1271 static HRESULT buffer_resource_sub_resource_map(struct wined3d_resource *resource, unsigned int sub_resource_idx,
1272         struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags)
1273 {
1274     struct wined3d_buffer *buffer = buffer_from_resource(resource);
1275     UINT offset, size;
1276 
1277     if (sub_resource_idx)
1278     {
1279         WARN("Invalid sub_resource_idx %u.\n", sub_resource_idx);
1280         return E_INVALIDARG;
1281     }
1282 
1283     if (box)
1284     {
1285         offset = box->left;
1286         size = box->right - box->left;
1287     }
1288     else
1289     {
1290         offset = size = 0;
1291     }
1292 
1293     map_desc->row_pitch = map_desc->slice_pitch = buffer->desc.byte_width;
1294     return wined3d_buffer_map(buffer, offset, size, (BYTE **)&map_desc->data, flags);
1295 }
1296 
1297 static HRESULT buffer_resource_sub_resource_map_info(struct wined3d_resource *resource, unsigned int sub_resource_idx,
1298         struct wined3d_map_info *info, DWORD flags)
1299 {
1300     struct wined3d_buffer *buffer = buffer_from_resource(resource);
1301 
1302     if (sub_resource_idx)
1303     {
1304         WARN("Invalid sub_resource_idx %u.\n", sub_resource_idx);
1305         return E_INVALIDARG;
1306     }
1307 
1308     info->row_pitch   = buffer->desc.byte_width;
1309     info->slice_pitch = buffer->desc.byte_width;
1310     info->size        = buffer->resource.size;
1311 
1312     return WINED3D_OK;
1313 }
1314 
1315 static HRESULT buffer_resource_sub_resource_unmap(struct wined3d_resource *resource, unsigned int sub_resource_idx)
1316 {
1317     if (sub_resource_idx)
1318     {
1319         WARN("Invalid sub_resource_idx %u.\n", sub_resource_idx);
1320         return E_INVALIDARG;
1321     }
1322 
1323     wined3d_buffer_unmap(buffer_from_resource(resource));
1324     return WINED3D_OK;
1325 }
1326 
1327 static const struct wined3d_resource_ops buffer_resource_ops =
1328 {
1329     buffer_resource_incref,
1330     buffer_resource_decref,
1331     buffer_resource_preload,
1332     buffer_unload,
1333     buffer_resource_sub_resource_map,
1334     buffer_resource_sub_resource_map_info,
1335     buffer_resource_sub_resource_unmap,
1336 };
1337 
1338 static GLenum buffer_type_hint_from_bind_flags(const struct wined3d_gl_info *gl_info,
1339         unsigned int bind_flags)
1340 {
1341     if (bind_flags == WINED3D_BIND_INDEX_BUFFER)
1342         return GL_ELEMENT_ARRAY_BUFFER;
1343 
1344     if (bind_flags & (WINED3D_BIND_SHADER_RESOURCE | WINED3D_BIND_UNORDERED_ACCESS)
1345             && gl_info->supported[ARB_TEXTURE_BUFFER_OBJECT])
1346         return GL_TEXTURE_BUFFER;
1347 
1348     if (bind_flags & WINED3D_BIND_CONSTANT_BUFFER)
1349         return GL_UNIFORM_BUFFER;
1350 
1351     if (bind_flags & WINED3D_BIND_STREAM_OUTPUT)
1352         return GL_TRANSFORM_FEEDBACK_BUFFER;
1353 
1354     if (bind_flags & ~(WINED3D_BIND_VERTEX_BUFFER | WINED3D_BIND_INDEX_BUFFER))
1355         FIXME("Unhandled bind flags %#x.\n", bind_flags);
1356 
1357     return GL_ARRAY_BUFFER;
1358 }
1359 
1360 static HRESULT buffer_init(struct wined3d_buffer *buffer, struct wined3d_device *device,
1361         UINT size, DWORD usage, enum wined3d_format_id format_id, enum wined3d_pool pool, unsigned int bind_flags,
1362         const struct wined3d_sub_resource_data *data, void *parent, const struct wined3d_parent_ops *parent_ops)
1363 {
1364     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1365     const struct wined3d_format *format = wined3d_get_format(gl_info, format_id, usage);
1366     BOOL dynamic_buffer_ok;
1367     HRESULT hr;
1368 
1369     if (!size)
1370     {
1371         WARN("Size 0 requested, returning E_INVALIDARG.\n");
1372         return E_INVALIDARG;
1373     }
1374 
1375     if (bind_flags & WINED3D_BIND_CONSTANT_BUFFER && size & (WINED3D_CONSTANT_BUFFER_ALIGNMENT - 1))
1376     {
1377         WARN("Size %#x is not suitably aligned for constant buffers.\n", size);
1378         return E_INVALIDARG;
1379     }
1380 
1381     if (data && !data->data)
1382     {
1383         WARN("Invalid sub-resource data specified.\n");
1384         return E_INVALIDARG;
1385     }
1386 
1387     hr = resource_init(&buffer->resource, device, WINED3D_RTYPE_BUFFER, format,
1388             WINED3D_MULTISAMPLE_NONE, 0, usage, pool, size, 1, 1, size, parent, parent_ops, &buffer_resource_ops);
1389     if (FAILED(hr))
1390     {
1391         WARN("Failed to initialize resource, hr %#x.\n", hr);
1392         return hr;
1393     }
1394     buffer->buffer_type_hint = buffer_type_hint_from_bind_flags(gl_info, bind_flags);
1395     buffer->bind_flags = bind_flags;
1396     buffer->locations = WINED3D_LOCATION_SYSMEM;
1397 
1398     TRACE("buffer %p, size %#x, usage %#x, format %s, memory @ %p.\n",
1399             buffer, buffer->resource.size, buffer->resource.usage,
1400             debug_d3dformat(buffer->resource.format->id), buffer->resource.heap_memory);
1401 
1402     if (device->create_parms.flags & WINED3DCREATE_SOFTWARE_VERTEXPROCESSING || pool == WINED3D_POOL_MANAGED)
1403     {
1404         /* SWvp and managed buffers always return the same pointer in buffer
1405          * maps and retain data in DISCARD maps. Keep a system memory copy of
1406          * the buffer to provide the same behavior to the application. */
1407         TRACE("Using doublebuffer mode.\n");
1408         buffer->flags |= WINED3D_BUFFER_PIN_SYSMEM;
1409     }
1410 
1411     /* Observations show that draw_primitive_immediate_mode() is faster on
1412      * dynamic vertex buffers than converting + draw_primitive_arrays().
1413      * (Half-Life 2 and others.) */
1414     dynamic_buffer_ok = gl_info->supported[APPLE_FLUSH_BUFFER_RANGE] || gl_info->supported[ARB_MAP_BUFFER_RANGE];
1415 
1416     if (!gl_info->supported[ARB_VERTEX_BUFFER_OBJECT])
1417     {
1418         TRACE("Not creating a BO because GL_ARB_vertex_buffer is not supported.\n");
1419     }
1420     else if (buffer->resource.pool == WINED3D_POOL_SYSTEM_MEM)
1421     {
1422         TRACE("Not creating a BO because the buffer is in system memory.\n");
1423     }
1424     else if (!dynamic_buffer_ok && (buffer->resource.usage & WINED3DUSAGE_DYNAMIC))
1425     {
1426         TRACE("Not creating a BO because the buffer has dynamic usage and no GL support.\n");
1427     }
1428     else
1429     {
1430         buffer->flags |= WINED3D_BUFFER_USE_BO;
1431     }
1432 
1433     if (!(buffer->maps = HeapAlloc(GetProcessHeap(), 0, sizeof(*buffer->maps))))
1434     {
1435         ERR("Out of memory.\n");
1436         buffer_unload(&buffer->resource);
1437         resource_cleanup(&buffer->resource);
1438         wined3d_resource_wait_idle(&buffer->resource);
1439         return E_OUTOFMEMORY;
1440     }
1441     buffer->maps_size = 1;
1442 
1443     if (data)
1444         wined3d_device_update_sub_resource(device, &buffer->resource,
1445                 0, NULL, data->data, data->row_pitch, data->slice_pitch);
1446 
1447     return WINED3D_OK;
1448 }
1449 
1450 HRESULT CDECL wined3d_buffer_create(struct wined3d_device *device, const struct wined3d_buffer_desc *desc,
1451         const struct wined3d_sub_resource_data *data, void *parent, const struct wined3d_parent_ops *parent_ops,
1452         struct wined3d_buffer **buffer)
1453 {
1454     struct wined3d_buffer *object;
1455     enum wined3d_pool pool;
1456     HRESULT hr;
1457 
1458     TRACE("device %p, desc %p, data %p, parent %p, parent_ops %p, buffer %p.\n",
1459             device, desc, data, parent, parent_ops, buffer);
1460 
1461     if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
1462         return E_OUTOFMEMORY;
1463 
1464     FIXME("Ignoring access flags (pool).\n");
1465 
1466     /* Some applications map the whole buffer even if they
1467      * only update a small portion of it. If we pin such a
1468      * buffer into system memory things get very slow as
1469      * we upload the whole buffer even though just parts of
1470      * it changed. Most drivers can handle this case more
1471      * efficient using the OpenGL map functions. Applications
1472      * affected by this problem are Banished and Witcher 3.
1473      */
1474     if (desc->byte_width > 0x10000)
1475         pool = WINED3D_POOL_DEFAULT;
1476     else
1477         pool = WINED3D_POOL_MANAGED;
1478 
1479     if (FAILED(hr = buffer_init(object, device, desc->byte_width, desc->usage, WINED3DFMT_UNKNOWN,
1480             pool, desc->bind_flags, data, parent, parent_ops)))
1481     {
1482         WARN("Failed to initialize buffer, hr %#x.\n", hr);
1483         HeapFree(GetProcessHeap(), 0, object);
1484         return hr;
1485     }
1486     object->desc = *desc;
1487 
1488     TRACE("Created buffer %p.\n", object);
1489 
1490     *buffer = object;
1491 
1492     return WINED3D_OK;
1493 }
1494 
1495 HRESULT CDECL wined3d_buffer_create_vb(struct wined3d_device *device, UINT size, DWORD usage, enum wined3d_pool pool,
1496         void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer)
1497 {
1498     struct wined3d_buffer *object;
1499     HRESULT hr;
1500 
1501     TRACE("device %p, size %u, usage %#x, pool %#x, parent %p, parent_ops %p, buffer %p.\n",
1502             device, size, usage, pool, parent, parent_ops, buffer);
1503 
1504     if (pool == WINED3D_POOL_SCRATCH)
1505     {
1506         /* The d3d9 tests shows that this is not allowed. It doesn't make much
1507          * sense anyway, SCRATCH buffers wouldn't be usable anywhere. */
1508         WARN("Vertex buffer in WINED3D_POOL_SCRATCH requested, returning WINED3DERR_INVALIDCALL.\n");
1509         *buffer = NULL;
1510         return WINED3DERR_INVALIDCALL;
1511     }
1512 
1513     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1514     if (!object)
1515     {
1516         *buffer = NULL;
1517         return WINED3DERR_OUTOFVIDEOMEMORY;
1518     }
1519 
1520     hr = buffer_init(object, device, size, usage, WINED3DFMT_UNKNOWN,
1521             pool, WINED3D_BIND_VERTEX_BUFFER, NULL, parent, parent_ops);
1522     if (FAILED(hr))
1523     {
1524         WARN("Failed to initialize buffer, hr %#x.\n", hr);
1525         HeapFree(GetProcessHeap(), 0, object);
1526         return hr;
1527     }
1528 
1529     TRACE("Created buffer %p.\n", object);
1530     *buffer = object;
1531 
1532     return WINED3D_OK;
1533 }
1534 
1535 HRESULT CDECL wined3d_buffer_create_ib(struct wined3d_device *device, UINT size, DWORD usage, enum wined3d_pool pool,
1536         void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer)
1537 {
1538     struct wined3d_buffer *object;
1539     HRESULT hr;
1540 
1541     TRACE("device %p, size %u, usage %#x, pool %#x, parent %p, parent_ops %p, buffer %p.\n",
1542             device, size, usage, pool, parent, parent_ops, buffer);
1543 
1544     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1545     if (!object)
1546     {
1547         *buffer = NULL;
1548         return WINED3DERR_OUTOFVIDEOMEMORY;
1549     }
1550 
1551     hr = buffer_init(object, device, size, usage | WINED3DUSAGE_STATICDECL,
1552             WINED3DFMT_UNKNOWN, pool, WINED3D_BIND_INDEX_BUFFER, NULL,
1553             parent, parent_ops);
1554     if (FAILED(hr))
1555     {
1556         WARN("Failed to initialize buffer, hr %#x\n", hr);
1557         HeapFree(GetProcessHeap(), 0, object);
1558         return hr;
1559     }
1560 
1561     TRACE("Created buffer %p.\n", object);
1562     *buffer = object;
1563 
1564     return WINED3D_OK;
1565 }
1566