wined3d: Get rid of the glsl_src_param_t typedef.
[wine] / dlls / wined3d / buffer.c
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-2010 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 "config.h"
26 #include "wine/port.h"
27
28 #include "wined3d_private.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
31
32 #define VB_MAXDECLCHANGES     100     /* After that number of decl changes we stop converting */
33 #define VB_RESETDECLCHANGE    1000    /* Reset the decl changecount after that number of draws */
34 #define VB_MAXFULLCONVERSIONS 5       /* Number of full conversions before we stop converting */
35 #define VB_RESETFULLCONVS     20      /* Reset full conversion counts after that number of draws */
36
37 static inline BOOL buffer_add_dirty_area(struct wined3d_buffer *This, UINT offset, UINT size)
38 {
39     if (!This->buffer_object) return TRUE;
40
41     if (This->maps_size <= This->modified_areas)
42     {
43         void *new = HeapReAlloc(GetProcessHeap(), 0, This->maps,
44                                 This->maps_size * 2 * sizeof(*This->maps));
45         if (!new)
46         {
47             ERR("Out of memory\n");
48             return FALSE;
49         }
50         else
51         {
52             This->maps = new;
53             This->maps_size *= 2;
54         }
55     }
56
57     if(offset > This->resource.size || offset + size > This->resource.size)
58     {
59         WARN("Invalid range dirtified, marking entire buffer dirty\n");
60         offset = 0;
61         size = This->resource.size;
62     }
63     else if(!offset && !size)
64     {
65         size = This->resource.size;
66     }
67
68     This->maps[This->modified_areas].offset = offset;
69     This->maps[This->modified_areas].size = size;
70     This->modified_areas++;
71     return TRUE;
72 }
73
74 static inline void buffer_clear_dirty_areas(struct wined3d_buffer *This)
75 {
76     This->modified_areas = 0;
77 }
78
79 static inline BOOL buffer_is_dirty(struct wined3d_buffer *This)
80 {
81     return !!This->modified_areas;
82 }
83
84 static inline BOOL buffer_is_fully_dirty(struct wined3d_buffer *This)
85 {
86     unsigned int i;
87
88     for(i = 0; i < This->modified_areas; i++)
89     {
90         if (!This->maps[i].offset && This->maps[i].size == This->resource.size)
91         {
92             return TRUE;
93         }
94     }
95     return FALSE;
96 }
97
98 /* Context activation is done by the caller */
99 static void delete_gl_buffer(struct wined3d_buffer *This, const struct wined3d_gl_info *gl_info)
100 {
101     if(!This->buffer_object) return;
102
103     ENTER_GL();
104     GL_EXTCALL(glDeleteBuffersARB(1, &This->buffer_object));
105     checkGLcall("glDeleteBuffersARB");
106     LEAVE_GL();
107     This->buffer_object = 0;
108
109     if(This->query)
110     {
111         wined3d_event_query_destroy(This->query);
112         This->query = NULL;
113     }
114     This->flags &= ~WINED3D_BUFFER_APPLESYNC;
115 }
116
117 /* Context activation is done by the caller. */
118 static void buffer_create_buffer_object(struct wined3d_buffer *This, const struct wined3d_gl_info *gl_info)
119 {
120     GLenum error, gl_usage;
121
122     TRACE("Creating an OpenGL vertex buffer object for IWineD3DVertexBuffer %p Usage(%s)\n",
123             This, debug_d3dusage(This->resource.usage));
124
125     ENTER_GL();
126
127     /* Make sure that the gl error is cleared. Do not use checkGLcall
128     * here because checkGLcall just prints a fixme and continues. However,
129     * if an error during VBO creation occurs we can fall back to non-vbo operation
130     * with full functionality(but performance loss)
131     */
132     while (glGetError() != GL_NO_ERROR);
133
134     /* Basically the FVF parameter passed to CreateVertexBuffer is no good
135      * It is the FVF set with IWineD3DDevice::SetFVF or the Vertex Declaration set with
136      * IWineD3DDevice::SetVertexDeclaration that decides how the vertices in the buffer
137      * look like. This means that on each DrawPrimitive call the vertex buffer has to be verified
138      * to check if the rhw and color values are in the correct format.
139      */
140
141     GL_EXTCALL(glGenBuffersARB(1, &This->buffer_object));
142     error = glGetError();
143     if (!This->buffer_object || error != GL_NO_ERROR)
144     {
145         ERR("Failed to create a VBO with error %s (%#x)\n", debug_glerror(error), error);
146         LEAVE_GL();
147         goto fail;
148     }
149
150     if (This->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
151         device_invalidate_state(This->resource.device, STATE_INDEXBUFFER);
152     GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
153     error = glGetError();
154     if (error != GL_NO_ERROR)
155     {
156         ERR("Failed to bind the VBO with error %s (%#x)\n", debug_glerror(error), error);
157         LEAVE_GL();
158         goto fail;
159     }
160
161     /* Don't use static, because dx apps tend to update the buffer
162      * quite often even if they specify 0 usage.
163      */
164     if(This->resource.usage & WINED3DUSAGE_DYNAMIC)
165     {
166         TRACE("Gl usage = GL_STREAM_DRAW_ARB\n");
167         gl_usage = GL_STREAM_DRAW_ARB;
168
169         if(gl_info->supported[APPLE_FLUSH_BUFFER_RANGE])
170         {
171             GL_EXTCALL(glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE));
172             checkGLcall("glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE)");
173             This->flags |= WINED3D_BUFFER_FLUSH;
174
175             GL_EXTCALL(glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE));
176             checkGLcall("glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE)");
177             This->flags |= WINED3D_BUFFER_APPLESYNC;
178         }
179         /* No setup is needed here for GL_ARB_map_buffer_range */
180     }
181     else
182     {
183         TRACE("Gl usage = GL_DYNAMIC_DRAW_ARB\n");
184         gl_usage = GL_DYNAMIC_DRAW_ARB;
185     }
186
187     /* Reserve memory for the buffer. The amount of data won't change
188      * so we are safe with calling glBufferData once and
189      * calling glBufferSubData on updates. Upload the actual data in case
190      * we're not double buffering, so we can release the heap mem afterwards
191      */
192     GL_EXTCALL(glBufferDataARB(This->buffer_type_hint, This->resource.size, This->resource.allocatedMemory, gl_usage));
193     error = glGetError();
194     LEAVE_GL();
195     if (error != GL_NO_ERROR)
196     {
197         ERR("glBufferDataARB failed with error %s (%#x)\n", debug_glerror(error), error);
198         goto fail;
199     }
200
201     This->buffer_object_size = This->resource.size;
202     This->buffer_object_usage = gl_usage;
203
204     if(This->flags & WINED3D_BUFFER_DOUBLEBUFFER)
205     {
206         if(!buffer_add_dirty_area(This, 0, 0))
207         {
208             ERR("buffer_add_dirty_area failed, this is not expected\n");
209             goto fail;
210         }
211     }
212     else
213     {
214         HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
215         This->resource.allocatedMemory = NULL;
216         This->resource.heapMemory = NULL;
217     }
218
219     return;
220
221 fail:
222     /* Clean up all vbo init, but continue because we can work without a vbo :-) */
223     ERR("Failed to create a vertex buffer object. Continuing, but performance issues may occur\n");
224     delete_gl_buffer(This, gl_info);
225     buffer_clear_dirty_areas(This);
226 }
227
228 static BOOL buffer_process_converted_attribute(struct wined3d_buffer *This,
229         const enum wined3d_buffer_conversion_type conversion_type,
230         const struct wined3d_stream_info_element *attrib, DWORD *stride_this_run)
231 {
232     DWORD offset = This->resource.device->stateBlock->state.streams[attrib->stream_idx].offset;
233     DWORD attrib_size;
234     BOOL ret = FALSE;
235     unsigned int i;
236     DWORD_PTR data;
237
238     /* Check for some valid situations which cause us pain. One is if the buffer is used for
239      * constant attributes(stride = 0), the other one is if the buffer is used on two streams
240      * with different strides. In the 2nd case we might have to drop conversion entirely,
241      * it is possible that the same bytes are once read as FLOAT2 and once as UBYTE4N.
242      */
243     if (!attrib->stride)
244     {
245         FIXME("%s used with stride 0, let's hope we get the vertex stride from somewhere else\n",
246                 debug_d3dformat(attrib->format->id));
247     }
248     else if(attrib->stride != *stride_this_run && *stride_this_run)
249     {
250         FIXME("Got two concurrent strides, %d and %d\n", attrib->stride, *stride_this_run);
251     }
252     else
253     {
254         *stride_this_run = attrib->stride;
255         if (This->stride != *stride_this_run)
256         {
257             /* We rely that this happens only on the first converted attribute that is found,
258              * if at all. See above check
259              */
260             TRACE("Reconverting because converted attributes occur, and the stride changed\n");
261             This->stride = *stride_this_run;
262             HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, This->conversion_map);
263             This->conversion_map = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
264                     sizeof(*This->conversion_map) * This->stride);
265             ret = TRUE;
266         }
267     }
268
269     data = (((DWORD_PTR)attrib->data) + offset) % This->stride;
270     attrib_size = attrib->format->component_count * attrib->format->component_size;
271     for (i = 0; i < attrib_size; ++i)
272     {
273         DWORD_PTR idx = (data + i) % This->stride;
274         if (This->conversion_map[idx] != conversion_type)
275         {
276             TRACE("Byte %ld in vertex changed\n", idx);
277             TRACE("It was type %d, is %d now\n", This->conversion_map[idx], conversion_type);
278             ret = TRUE;
279             This->conversion_map[idx] = conversion_type;
280         }
281     }
282
283     return ret;
284 }
285
286 static BOOL buffer_check_attribute(struct wined3d_buffer *This, const struct wined3d_stream_info *si,
287         UINT attrib_idx, const BOOL check_d3dcolor, const BOOL is_ffp_position, const BOOL is_ffp_color,
288         DWORD *stride_this_run)
289 {
290     const struct wined3d_stream_info_element *attrib = &si->elements[attrib_idx];
291     enum wined3d_format_id format;
292     BOOL ret = FALSE;
293
294     /* Ignore attributes that do not have our vbo. After that check we can be sure that the attribute is
295      * there, on nonexistent attribs the vbo is 0.
296      */
297     if (!(si->use_map & (1 << attrib_idx))
298             || attrib->buffer_object != This->buffer_object)
299         return FALSE;
300
301     format = attrib->format->id;
302     /* Look for newly appeared conversion */
303     if (check_d3dcolor && format == WINED3DFMT_B8G8R8A8_UNORM)
304     {
305         ret = buffer_process_converted_attribute(This, CONV_D3DCOLOR, attrib, stride_this_run);
306
307         if (!is_ffp_color) FIXME("Test for non-color fixed function WINED3DFMT_B8G8R8A8_UNORM format\n");
308     }
309     else if (is_ffp_position && format == WINED3DFMT_R32G32B32A32_FLOAT)
310     {
311         ret = buffer_process_converted_attribute(This, CONV_POSITIONT, attrib, stride_this_run);
312     }
313     else if (This->conversion_map)
314     {
315         ret = buffer_process_converted_attribute(This, CONV_NONE, attrib, stride_this_run);
316     }
317
318     return ret;
319 }
320
321 static BOOL buffer_find_decl(struct wined3d_buffer *This)
322 {
323     struct wined3d_device *device = This->resource.device;
324     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
325     const struct wined3d_stream_info *si = &device->strided_streams;
326     const struct wined3d_state *state = &device->stateBlock->state;
327     UINT stride_this_run = 0;
328     BOOL ret = FALSE;
329     BOOL support_d3dcolor = gl_info->supported[ARB_VERTEX_ARRAY_BGRA];
330
331     /* In d3d7 the vertex buffer declaration NEVER changes because it is stored in the d3d7 vertex buffer.
332      * Once we have our declaration there is no need to look it up again. Index buffers also never need
333      * conversion, so once the (empty) conversion structure is created don't bother checking again
334      */
335     if (This->flags & WINED3D_BUFFER_HASDESC)
336     {
337         if(This->resource.usage & WINED3DUSAGE_STATICDECL) return FALSE;
338     }
339
340     if (use_vs(state))
341     {
342         TRACE("Vertex shaders used, no VBO conversion is needed\n");
343         if(This->conversion_map)
344         {
345             HeapFree(GetProcessHeap(), 0, This->conversion_map);
346             This->conversion_map = NULL;
347             This->stride = 0;
348             return TRUE;
349         }
350
351         return FALSE;
352     }
353
354     TRACE("Finding vertex buffer conversion information\n");
355     /* Certain declaration types need some fixups before we can pass them to
356      * opengl. This means D3DCOLOR attributes with fixed function vertex
357      * processing, FLOAT4 POSITIONT with fixed function, and FLOAT16 if
358      * GL_ARB_half_float_vertex is not supported.
359      *
360      * Note for d3d8 and d3d9:
361      * The vertex buffer FVF doesn't help with finding them, we have to use
362      * the decoded vertex declaration and pick the things that concern the
363      * current buffer. A problem with this is that this can change between
364      * draws, so we have to validate the information and reprocess the buffer
365      * if it changes, and avoid false positives for performance reasons.
366      * WineD3D doesn't even know the vertex buffer any more, it is managed
367      * by the client libraries and passed to SetStreamSource and ProcessVertices
368      * as needed.
369      *
370      * We have to distinguish between vertex shaders and fixed function to
371      * pick the way we access the strided vertex information.
372      *
373      * This code sets up a per-byte array with the size of the detected
374      * stride of the arrays in the buffer. For each byte we have a field
375      * that marks the conversion needed on this byte. For example, the
376      * following declaration with fixed function vertex processing:
377      *
378      *      POSITIONT, FLOAT4
379      *      NORMAL, FLOAT3
380      *      DIFFUSE, FLOAT16_4
381      *      SPECULAR, D3DCOLOR
382      *
383      * Will result in
384      * {                 POSITIONT                    }{             NORMAL                }{    DIFFUSE          }{SPECULAR }
385      * [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]
386      *
387      * Where in this example map P means 4 component position conversion, 0
388      * means no conversion, F means FLOAT16_2 conversion and C means D3DCOLOR
389      * conversion (red / blue swizzle).
390      *
391      * If we're doing conversion and the stride changes we have to reconvert
392      * the whole buffer. Note that we do not mind if the semantic changes,
393      * we only care for the conversion type. So if the NORMAL is replaced
394      * with a TEXCOORD, nothing has to be done, or if the DIFFUSE is replaced
395      * with a D3DCOLOR BLENDWEIGHT we can happily dismiss the change. Some
396      * conversion types depend on the semantic as well, for example a FLOAT4
397      * texcoord needs no conversion while a FLOAT4 positiont needs one
398      */
399
400     ret = buffer_check_attribute(This, si, WINED3D_FFP_POSITION,
401             TRUE, TRUE,  FALSE, &stride_this_run) || ret;
402     ret = buffer_check_attribute(This, si, WINED3D_FFP_NORMAL,
403             TRUE, FALSE, FALSE, &stride_this_run) || ret;
404     ret = buffer_check_attribute(This, si, WINED3D_FFP_DIFFUSE,
405             !support_d3dcolor, FALSE, TRUE,  &stride_this_run) || ret;
406     ret = buffer_check_attribute(This, si, WINED3D_FFP_SPECULAR,
407             !support_d3dcolor, FALSE, TRUE,  &stride_this_run) || ret;
408     ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD0,
409             TRUE, FALSE, FALSE, &stride_this_run) || ret;
410     ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD1,
411             TRUE, FALSE, FALSE, &stride_this_run) || ret;
412     ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD2,
413             TRUE, FALSE, FALSE, &stride_this_run) || ret;
414     ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD3,
415             TRUE, FALSE, FALSE, &stride_this_run) || ret;
416     ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD4,
417             TRUE, FALSE, FALSE, &stride_this_run) || ret;
418     ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD5,
419             TRUE, FALSE, FALSE, &stride_this_run) || ret;
420     ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD6,
421             TRUE, FALSE, FALSE, &stride_this_run) || ret;
422     ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD7,
423             TRUE, FALSE, FALSE, &stride_this_run) || ret;
424
425     if (!stride_this_run && This->conversion_map)
426     {
427         /* Sanity test */
428         if (!ret) ERR("no converted attributes found, old conversion map exists, and no declaration change?\n");
429         HeapFree(GetProcessHeap(), 0, This->conversion_map);
430         This->conversion_map = NULL;
431         This->stride = 0;
432     }
433
434     if (ret) TRACE("Conversion information changed\n");
435
436     return ret;
437 }
438
439 static inline void fixup_d3dcolor(DWORD *dst_color)
440 {
441     DWORD src_color = *dst_color;
442
443     /* Color conversion like in drawStridedSlow. watch out for little endianity
444      * If we want that stuff to work on big endian machines too we have to consider more things
445      *
446      * 0xff000000: Alpha mask
447      * 0x00ff0000: Blue mask
448      * 0x0000ff00: Green mask
449      * 0x000000ff: Red mask
450      */
451     *dst_color = 0;
452     *dst_color |= (src_color & 0xff00ff00);         /* Alpha Green */
453     *dst_color |= (src_color & 0x00ff0000) >> 16;   /* Red */
454     *dst_color |= (src_color & 0x000000ff) << 16;   /* Blue */
455 }
456
457 static inline void fixup_transformed_pos(float *p)
458 {
459     /* rhw conversion like in position_float4(). */
460     if (p[3] != 1.0f && p[3] != 0.0f)
461     {
462         float w = 1.0f / p[3];
463         p[0] *= w;
464         p[1] *= w;
465         p[2] *= w;
466         p[3] = w;
467     }
468 }
469
470 /* Context activation is done by the caller. */
471 const BYTE *buffer_get_memory(struct wined3d_buffer *buffer,
472         const struct wined3d_gl_info *gl_info, GLuint *buffer_object)
473 {
474     *buffer_object = buffer->buffer_object;
475     if (!buffer->buffer_object)
476     {
477         if (buffer->flags & WINED3D_BUFFER_CREATEBO)
478         {
479             buffer_create_buffer_object(buffer, gl_info);
480             buffer->flags &= ~WINED3D_BUFFER_CREATEBO;
481             if (buffer->buffer_object)
482             {
483                 *buffer_object = buffer->buffer_object;
484                 return NULL;
485             }
486         }
487         return buffer->resource.allocatedMemory;
488     }
489     else
490     {
491         return NULL;
492     }
493 }
494
495 ULONG CDECL wined3d_buffer_incref(struct wined3d_buffer *buffer)
496 {
497     ULONG refcount = InterlockedIncrement(&buffer->resource.ref);
498
499     TRACE("%p increasing refcount to %u.\n", buffer, refcount);
500
501     return refcount;
502 }
503
504 /* Context activation is done by the caller. */
505 BYTE *buffer_get_sysmem(struct wined3d_buffer *This, const struct wined3d_gl_info *gl_info)
506 {
507     /* AllocatedMemory exists if the buffer is double buffered or has no buffer object at all */
508     if(This->resource.allocatedMemory) return This->resource.allocatedMemory;
509
510     This->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + RESOURCE_ALIGNMENT);
511     This->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
512
513     if (This->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
514         device_invalidate_state(This->resource.device, STATE_INDEXBUFFER);
515
516     ENTER_GL();
517     GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
518     GL_EXTCALL(glGetBufferSubDataARB(This->buffer_type_hint, 0, This->resource.size, This->resource.allocatedMemory));
519     LEAVE_GL();
520     This->flags |= WINED3D_BUFFER_DOUBLEBUFFER;
521
522     return This->resource.allocatedMemory;
523 }
524
525 /* Do not call while under the GL lock. */
526 static void buffer_unload(struct wined3d_resource *resource)
527 {
528     struct wined3d_buffer *buffer = buffer_from_resource(resource);
529
530     TRACE("buffer %p.\n", buffer);
531
532     if (buffer->buffer_object)
533     {
534         struct wined3d_device *device = resource->device;
535         struct wined3d_context *context;
536
537         context = context_acquire(device, NULL);
538
539         /* Download the buffer, but don't permanently enable double buffering */
540         if (!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER))
541         {
542             buffer_get_sysmem(buffer, context->gl_info);
543             buffer->flags &= ~WINED3D_BUFFER_DOUBLEBUFFER;
544         }
545
546         delete_gl_buffer(buffer, context->gl_info);
547         buffer->flags |= WINED3D_BUFFER_CREATEBO; /* Recreate the buffer object next load */
548         buffer_clear_dirty_areas(buffer);
549
550         context_release(context);
551
552         HeapFree(GetProcessHeap(), 0, buffer->conversion_map);
553         buffer->conversion_map = NULL;
554         buffer->stride = 0;
555         buffer->conversion_stride = 0;
556         buffer->flags &= ~WINED3D_BUFFER_HASDESC;
557     }
558
559     resource_unload(resource);
560 }
561
562 /* Do not call while under the GL lock. */
563 ULONG CDECL wined3d_buffer_decref(struct wined3d_buffer *buffer)
564 {
565     ULONG refcount = InterlockedDecrement(&buffer->resource.ref);
566
567     TRACE("%p decreasing refcount to %u.\n", buffer, refcount);
568
569     if (!refcount)
570     {
571         buffer_unload(&buffer->resource);
572         resource_cleanup(&buffer->resource);
573         buffer->resource.parent_ops->wined3d_object_destroyed(buffer->resource.parent);
574         HeapFree(GetProcessHeap(), 0, buffer->maps);
575         HeapFree(GetProcessHeap(), 0, buffer);
576     }
577
578     return refcount;
579 }
580
581 void * CDECL wined3d_buffer_get_parent(const struct wined3d_buffer *buffer)
582 {
583     TRACE("buffer %p.\n", buffer);
584
585     return buffer->resource.parent;
586 }
587
588 DWORD CDECL wined3d_buffer_set_priority(struct wined3d_buffer *buffer, DWORD priority)
589 {
590     return resource_set_priority(&buffer->resource, priority);
591 }
592
593 DWORD CDECL wined3d_buffer_get_priority(const struct wined3d_buffer *buffer)
594 {
595     return resource_get_priority(&buffer->resource);
596 }
597
598 /* The caller provides a context and binds the buffer */
599 static void buffer_sync_apple(struct wined3d_buffer *This, DWORD flags, const struct wined3d_gl_info *gl_info)
600 {
601     enum wined3d_event_query_result ret;
602
603     /* No fencing needs to be done if the app promises not to overwrite
604      * existing data */
605     if(flags & WINED3DLOCK_NOOVERWRITE) return;
606     if(flags & WINED3DLOCK_DISCARD)
607     {
608         ENTER_GL();
609         GL_EXTCALL(glBufferDataARB(This->buffer_type_hint, This->resource.size, NULL, This->buffer_object_usage));
610         checkGLcall("glBufferDataARB\n");
611         LEAVE_GL();
612         return;
613     }
614
615     if(!This->query)
616     {
617         TRACE("Creating event query for buffer %p\n", This);
618
619         if (!wined3d_event_query_supported(gl_info))
620         {
621             FIXME("Event queries not supported, dropping async buffer locks.\n");
622             goto drop_query;
623         }
624
625         This->query = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This->query));
626         if (!This->query)
627         {
628             ERR("Failed to allocate event query memory, dropping async buffer locks.\n");
629             goto drop_query;
630         }
631
632         /* Since we don't know about old draws a glFinish is needed once */
633         wglFinish();
634         return;
635     }
636     TRACE("Synchronizing buffer %p\n", This);
637     ret = wined3d_event_query_finish(This->query, This->resource.device);
638     switch(ret)
639     {
640         case WINED3D_EVENT_QUERY_NOT_STARTED:
641         case WINED3D_EVENT_QUERY_OK:
642             /* All done */
643             return;
644
645         case WINED3D_EVENT_QUERY_WRONG_THREAD:
646             WARN("Cannot synchronize buffer lock due to a thread conflict\n");
647             goto drop_query;
648
649         default:
650             ERR("wined3d_event_query_finish returned %u, dropping async buffer locks\n", ret);
651             goto drop_query;
652     }
653
654 drop_query:
655     if(This->query)
656     {
657         wined3d_event_query_destroy(This->query);
658         This->query = NULL;
659     }
660
661     wglFinish();
662     ENTER_GL();
663     GL_EXTCALL(glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_TRUE));
664     checkGLcall("glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_TRUE)");
665     LEAVE_GL();
666     This->flags &= ~WINED3D_BUFFER_APPLESYNC;
667 }
668
669 /* The caller provides a GL context */
670 static void buffer_direct_upload(struct wined3d_buffer *This, const struct wined3d_gl_info *gl_info, DWORD flags)
671 {
672     BYTE *map;
673     UINT start = 0, len = 0;
674
675     ENTER_GL();
676
677     /* This potentially invalidates the element array buffer binding, but the
678      * caller always takes care of this. */
679     GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
680     checkGLcall("glBindBufferARB");
681     if (gl_info->supported[ARB_MAP_BUFFER_RANGE])
682     {
683         GLbitfield mapflags;
684         mapflags = GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
685         if (flags & WINED3D_BUFFER_DISCARD)
686         {
687             mapflags |= GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_BUFFER_BIT;
688         }
689         else if (flags & WINED3D_BUFFER_NOSYNC)
690         {
691             mapflags |= GL_MAP_UNSYNCHRONIZED_BIT;
692         }
693         map = GL_EXTCALL(glMapBufferRange(This->buffer_type_hint, 0,
694                     This->resource.size, mapflags));
695         checkGLcall("glMapBufferRange");
696     }
697     else
698     {
699         if (This->flags & WINED3D_BUFFER_APPLESYNC)
700         {
701             DWORD syncflags = 0;
702             if (flags & WINED3D_BUFFER_DISCARD) syncflags |= WINED3DLOCK_DISCARD;
703             if (flags & WINED3D_BUFFER_NOSYNC) syncflags |= WINED3DLOCK_NOOVERWRITE;
704             LEAVE_GL();
705             buffer_sync_apple(This, syncflags, gl_info);
706             ENTER_GL();
707         }
708         map = GL_EXTCALL(glMapBufferARB(This->buffer_type_hint, GL_WRITE_ONLY_ARB));
709         checkGLcall("glMapBufferARB");
710     }
711     if (!map)
712     {
713         LEAVE_GL();
714         ERR("Failed to map opengl buffer\n");
715         return;
716     }
717
718     while (This->modified_areas)
719     {
720         This->modified_areas--;
721         start = This->maps[This->modified_areas].offset;
722         len = This->maps[This->modified_areas].size;
723
724         memcpy(map + start, This->resource.allocatedMemory + start, len);
725
726         if (gl_info->supported[ARB_MAP_BUFFER_RANGE])
727         {
728             GL_EXTCALL(glFlushMappedBufferRange(This->buffer_type_hint, start, len));
729             checkGLcall("glFlushMappedBufferRange");
730         }
731         else if (This->flags & WINED3D_BUFFER_FLUSH)
732         {
733             GL_EXTCALL(glFlushMappedBufferRangeAPPLE(This->buffer_type_hint, start, len));
734             checkGLcall("glFlushMappedBufferRangeAPPLE");
735         }
736     }
737     GL_EXTCALL(glUnmapBufferARB(This->buffer_type_hint));
738     checkGLcall("glUnmapBufferARB");
739
740     LEAVE_GL();
741 }
742
743 /* Do not call while under the GL lock. */
744 void CDECL wined3d_buffer_preload(struct wined3d_buffer *buffer)
745 {
746     DWORD flags = buffer->flags & (WINED3D_BUFFER_NOSYNC | WINED3D_BUFFER_DISCARD);
747     struct wined3d_device *device = buffer->resource.device;
748     UINT start = 0, end = 0, len = 0, vertices;
749     const struct wined3d_gl_info *gl_info;
750     struct wined3d_context *context;
751     BOOL decl_changed = FALSE;
752     unsigned int i, j;
753     BYTE *data;
754
755     TRACE("buffer %p.\n", buffer);
756
757     buffer->flags &= ~(WINED3D_BUFFER_NOSYNC | WINED3D_BUFFER_DISCARD);
758
759     if (!buffer->buffer_object)
760     {
761         /* TODO: Make converting independent from VBOs */
762         if (buffer->flags & WINED3D_BUFFER_CREATEBO)
763         {
764             context = context_acquire(device, NULL);
765             buffer_create_buffer_object(buffer, context->gl_info);
766             context_release(context);
767             buffer->flags &= ~WINED3D_BUFFER_CREATEBO;
768         }
769         else
770         {
771             /* Not doing any conversion */
772             return;
773         }
774     }
775
776     /* Reading the declaration makes only sense if the stateblock is finalized and the buffer bound to a stream */
777     if (device->isInDraw && buffer->bind_count > 0)
778     {
779         decl_changed = buffer_find_decl(buffer);
780         buffer->flags |= WINED3D_BUFFER_HASDESC;
781     }
782
783     if (!decl_changed && !(buffer->flags & WINED3D_BUFFER_HASDESC && buffer_is_dirty(buffer)))
784     {
785         ++buffer->draw_count;
786         if (buffer->draw_count > VB_RESETDECLCHANGE)
787             buffer->decl_change_count = 0;
788         if (buffer->draw_count > VB_RESETFULLCONVS)
789             buffer->full_conversion_count = 0;
790         return;
791     }
792
793     /* If applications change the declaration over and over, reconverting all the time is a huge
794      * performance hit. So count the declaration changes and release the VBO if there are too many
795      * of them (and thus stop converting)
796      */
797     if (decl_changed)
798     {
799         ++buffer->decl_change_count;
800         buffer->draw_count = 0;
801
802         if (buffer->decl_change_count > VB_MAXDECLCHANGES
803                 || (buffer->conversion_map && (buffer->resource.usage & WINED3DUSAGE_DYNAMIC)))
804         {
805             FIXME("Too many declaration changes or converting dynamic buffer, stopping converting\n");
806
807             buffer_unload(&buffer->resource);
808             buffer->flags &= ~WINED3D_BUFFER_CREATEBO;
809
810             /* The stream source state handler might have read the memory of
811              * the vertex buffer already and got the memory in the vbo which
812              * is not valid any longer. Dirtify the stream source to force a
813              * reload. This happens only once per changed vertexbuffer and
814              * should occur rather rarely. */
815             device_invalidate_state(device, STATE_STREAMSRC);
816             return;
817         }
818
819         /* The declaration changed, reload the whole buffer */
820         WARN("Reloading buffer because of decl change\n");
821         buffer_clear_dirty_areas(buffer);
822         if (!buffer_add_dirty_area(buffer, 0, 0))
823         {
824             ERR("buffer_add_dirty_area failed, this is not expected\n");
825             return;
826         }
827         /* Avoid unfenced updates, we might overwrite more areas of the buffer than the application
828          * cleared for unsynchronized updates
829          */
830         flags = 0;
831     }
832     else
833     {
834         /* However, it is perfectly fine to change the declaration every now and then. We don't want a game that
835          * changes it every minute drop the VBO after VB_MAX_DECL_CHANGES minutes. So count draws without
836          * decl changes and reset the decl change count after a specific number of them
837          */
838         if (buffer->conversion_map && buffer_is_fully_dirty(buffer))
839         {
840             ++buffer->full_conversion_count;
841             if (buffer->full_conversion_count > VB_MAXFULLCONVERSIONS)
842             {
843                 FIXME("Too many full buffer conversions, stopping converting.\n");
844                 buffer_unload(&buffer->resource);
845                 buffer->flags &= ~WINED3D_BUFFER_CREATEBO;
846                 if (buffer->bind_count)
847                     device_invalidate_state(device, STATE_STREAMSRC);
848                 return;
849             }
850         }
851         else
852         {
853             ++buffer->draw_count;
854             if (buffer->draw_count > VB_RESETDECLCHANGE)
855                 buffer->decl_change_count = 0;
856             if (buffer->draw_count > VB_RESETFULLCONVS)
857                 buffer->full_conversion_count = 0;
858         }
859     }
860
861     if (buffer->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
862         device_invalidate_state(device, STATE_INDEXBUFFER);
863
864     if (!buffer->conversion_map)
865     {
866         /* That means that there is nothing to fixup. Just upload from
867          * buffer->resource.allocatedMemory directly into the vbo. Do not
868          * free the system memory copy because drawPrimitive may need it if
869          * the stride is 0, for instancing emulation, vertex blending
870          * emulation or shader emulation. */
871         TRACE("No conversion needed.\n");
872
873         /* Nothing to do because we locked directly into the vbo */
874         if (!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER))
875         {
876             return;
877         }
878
879         context = context_acquire(device, NULL);
880         buffer_direct_upload(buffer, context->gl_info, flags);
881
882         context_release(context);
883         return;
884     }
885
886     context = context_acquire(device, NULL);
887     gl_info = context->gl_info;
888
889     if(!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER))
890     {
891         buffer_get_sysmem(buffer, gl_info);
892     }
893
894     /* Now for each vertex in the buffer that needs conversion */
895     vertices = buffer->resource.size / buffer->stride;
896
897     data = HeapAlloc(GetProcessHeap(), 0, buffer->resource.size);
898
899     while(buffer->modified_areas)
900     {
901         buffer->modified_areas--;
902         start = buffer->maps[buffer->modified_areas].offset;
903         len = buffer->maps[buffer->modified_areas].size;
904         end = start + len;
905
906         memcpy(data + start, buffer->resource.allocatedMemory + start, end - start);
907         for (i = start / buffer->stride; i < min((end / buffer->stride) + 1, vertices); ++i)
908         {
909             for (j = 0; j < buffer->stride; ++j)
910             {
911                 switch (buffer->conversion_map[j])
912                 {
913                     case CONV_NONE:
914                         /* Done already */
915                         j += 3;
916                         break;
917                     case CONV_D3DCOLOR:
918                         fixup_d3dcolor((DWORD *) (data + i * buffer->stride + j));
919                         j += 3;
920                         break;
921
922                     case CONV_POSITIONT:
923                         fixup_transformed_pos((float *) (data + i * buffer->stride + j));
924                         j += 15;
925                         break;
926                     default:
927                         FIXME("Unimplemented conversion %d in shifted conversion\n", buffer->conversion_map[j]);
928                 }
929             }
930         }
931
932         ENTER_GL();
933         GL_EXTCALL(glBindBufferARB(buffer->buffer_type_hint, buffer->buffer_object));
934         checkGLcall("glBindBufferARB");
935         GL_EXTCALL(glBufferSubDataARB(buffer->buffer_type_hint, start, len, data + start));
936         checkGLcall("glBufferSubDataARB");
937         LEAVE_GL();
938     }
939
940     HeapFree(GetProcessHeap(), 0, data);
941     context_release(context);
942 }
943
944 static DWORD buffer_sanitize_flags(struct wined3d_buffer *buffer, DWORD flags)
945 {
946     /* Not all flags make sense together, but Windows never returns an error. Catch the
947      * cases that could cause issues */
948     if(flags & WINED3DLOCK_READONLY)
949     {
950         if(flags & WINED3DLOCK_DISCARD)
951         {
952             WARN("WINED3DLOCK_READONLY combined with WINED3DLOCK_DISCARD, ignoring flags\n");
953             return 0;
954         }
955         if(flags & WINED3DLOCK_NOOVERWRITE)
956         {
957             WARN("WINED3DLOCK_READONLY combined with WINED3DLOCK_NOOVERWRITE, ignoring flags\n");
958             return 0;
959         }
960     }
961     else if((flags & (WINED3DLOCK_DISCARD | WINED3DLOCK_NOOVERWRITE)) == (WINED3DLOCK_DISCARD | WINED3DLOCK_NOOVERWRITE))
962     {
963         WARN("WINED3DLOCK_DISCARD and WINED3DLOCK_NOOVERWRITE used together, ignoring\n");
964         return 0;
965     }
966     else if (flags & (WINED3DLOCK_DISCARD | WINED3DLOCK_NOOVERWRITE) && !(buffer->resource.usage & WINED3DUSAGE_DYNAMIC))
967     {
968         WARN("DISCARD or NOOVERWRITE lock on non-dynamic buffer, ignoring\n");
969         return 0;
970     }
971
972     return flags;
973 }
974
975 static GLbitfield buffer_gl_map_flags(DWORD d3d_flags)
976 {
977     GLbitfield ret = 0;
978
979     if (!(d3d_flags & WINED3DLOCK_READONLY)) ret = GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
980
981     if (d3d_flags & (WINED3DLOCK_DISCARD | WINED3DLOCK_NOOVERWRITE))
982     {
983         if(d3d_flags & WINED3DLOCK_DISCARD) ret |= GL_MAP_INVALIDATE_BUFFER_BIT;
984         ret |= GL_MAP_UNSYNCHRONIZED_BIT;
985     }
986     else
987     {
988         ret |= GL_MAP_READ_BIT;
989     }
990
991     return ret;
992 }
993
994 struct wined3d_resource * CDECL wined3d_buffer_get_resource(struct wined3d_buffer *buffer)
995 {
996     TRACE("buffer %p.\n", buffer);
997
998     return &buffer->resource;
999 }
1000
1001 HRESULT CDECL wined3d_buffer_map(struct wined3d_buffer *buffer, UINT offset, UINT size, BYTE **data, DWORD flags)
1002 {
1003     BOOL dirty = buffer_is_dirty(buffer);
1004     LONG count;
1005
1006     TRACE("buffer %p, offset %u, size %u, data %p, flags %#x\n", buffer, offset, size, data, flags);
1007
1008     flags = buffer_sanitize_flags(buffer, flags);
1009     if (!(flags & WINED3DLOCK_READONLY))
1010     {
1011         if (!buffer_add_dirty_area(buffer, offset, size)) return E_OUTOFMEMORY;
1012     }
1013
1014     count = InterlockedIncrement(&buffer->lock_count);
1015
1016     if (buffer->buffer_object)
1017     {
1018         if (!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER))
1019         {
1020             if (count == 1)
1021             {
1022                 struct wined3d_device *device = buffer->resource.device;
1023                 struct wined3d_context *context;
1024                 const struct wined3d_gl_info *gl_info;
1025
1026                 if (buffer->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
1027                     device_invalidate_state(device, STATE_INDEXBUFFER);
1028
1029                 context = context_acquire(device, NULL);
1030                 gl_info = context->gl_info;
1031                 ENTER_GL();
1032                 GL_EXTCALL(glBindBufferARB(buffer->buffer_type_hint, buffer->buffer_object));
1033
1034                 if (gl_info->supported[ARB_MAP_BUFFER_RANGE])
1035                 {
1036                     GLbitfield mapflags = buffer_gl_map_flags(flags);
1037                     buffer->resource.allocatedMemory = GL_EXTCALL(glMapBufferRange(buffer->buffer_type_hint,
1038                             0, buffer->resource.size, mapflags));
1039                     checkGLcall("glMapBufferRange");
1040                 }
1041                 else
1042                 {
1043                     if (buffer->flags & WINED3D_BUFFER_APPLESYNC)
1044                     {
1045                         LEAVE_GL();
1046                         buffer_sync_apple(buffer, flags, gl_info);
1047                         ENTER_GL();
1048                     }
1049                     buffer->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(buffer->buffer_type_hint,
1050                             GL_READ_WRITE_ARB));
1051                     checkGLcall("glMapBufferARB");
1052                 }
1053                 LEAVE_GL();
1054
1055                 if (((DWORD_PTR)buffer->resource.allocatedMemory) & (RESOURCE_ALIGNMENT - 1))
1056                 {
1057                     WARN("Pointer %p is not %u byte aligned.\n", buffer->resource.allocatedMemory, RESOURCE_ALIGNMENT);
1058
1059                     ENTER_GL();
1060                     GL_EXTCALL(glUnmapBufferARB(buffer->buffer_type_hint));
1061                     checkGLcall("glUnmapBufferARB");
1062                     LEAVE_GL();
1063                     buffer->resource.allocatedMemory = NULL;
1064
1065                     if (buffer->resource.usage & WINED3DUSAGE_DYNAMIC)
1066                     {
1067                         /* The extra copy is more expensive than not using VBOs at
1068                          * all on the Nvidia Linux driver, which is the only driver
1069                          * that returns unaligned pointers
1070                          */
1071                         TRACE("Dynamic buffer, dropping VBO\n");
1072                         buffer_unload(&buffer->resource);
1073                         buffer->flags &= ~WINED3D_BUFFER_CREATEBO;
1074                         if (buffer->bind_count)
1075                             device_invalidate_state(device, STATE_STREAMSRC);
1076                     }
1077                     else
1078                     {
1079                         TRACE("Falling back to doublebuffered operation\n");
1080                         buffer_get_sysmem(buffer, gl_info);
1081                     }
1082                     TRACE("New pointer is %p.\n", buffer->resource.allocatedMemory);
1083                 }
1084                 context_release(context);
1085             }
1086         }
1087         else
1088         {
1089             if (dirty)
1090             {
1091                 if (buffer->flags & WINED3D_BUFFER_NOSYNC && !(flags & WINED3DLOCK_NOOVERWRITE))
1092                 {
1093                     buffer->flags &= ~WINED3D_BUFFER_NOSYNC;
1094                 }
1095             }
1096             else if(flags & WINED3DLOCK_NOOVERWRITE)
1097             {
1098                 buffer->flags |= WINED3D_BUFFER_NOSYNC;
1099             }
1100
1101             if (flags & WINED3DLOCK_DISCARD)
1102             {
1103                 buffer->flags |= WINED3D_BUFFER_DISCARD;
1104             }
1105         }
1106     }
1107
1108     *data = buffer->resource.allocatedMemory + offset;
1109
1110     TRACE("Returning memory at %p (base %p, offset %u).\n", *data, buffer->resource.allocatedMemory, offset);
1111     /* TODO: check Flags compatibility with buffer->currentDesc.Usage (see MSDN) */
1112
1113     return WINED3D_OK;
1114 }
1115
1116 void CDECL wined3d_buffer_unmap(struct wined3d_buffer *buffer)
1117 {
1118     ULONG i;
1119
1120     TRACE("buffer %p.\n", buffer);
1121
1122     /* In the case that the number of Unmap calls > the
1123      * number of Map calls, d3d returns always D3D_OK.
1124      * This is also needed to prevent Map from returning garbage on
1125      * the next call (this will happen if the lock_count is < 0). */
1126     if (!buffer->lock_count)
1127     {
1128         WARN("Unmap called without a previous map call.\n");
1129         return;
1130     }
1131
1132     if (InterlockedDecrement(&buffer->lock_count))
1133     {
1134         /* Delay loading the buffer until everything is unlocked */
1135         TRACE("Ignoring unmap.\n");
1136         return;
1137     }
1138
1139     if (!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER) && buffer->buffer_object)
1140     {
1141         struct wined3d_device *device = buffer->resource.device;
1142         const struct wined3d_gl_info *gl_info;
1143         struct wined3d_context *context;
1144
1145         if (buffer->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
1146             device_invalidate_state(device, STATE_INDEXBUFFER);
1147
1148         context = context_acquire(device, NULL);
1149         gl_info = context->gl_info;
1150         ENTER_GL();
1151         GL_EXTCALL(glBindBufferARB(buffer->buffer_type_hint, buffer->buffer_object));
1152
1153         if (gl_info->supported[ARB_MAP_BUFFER_RANGE])
1154         {
1155             for (i = 0; i < buffer->modified_areas; ++i)
1156             {
1157                 GL_EXTCALL(glFlushMappedBufferRange(buffer->buffer_type_hint,
1158                         buffer->maps[i].offset, buffer->maps[i].size));
1159                 checkGLcall("glFlushMappedBufferRange");
1160             }
1161         }
1162         else if (buffer->flags & WINED3D_BUFFER_FLUSH)
1163         {
1164             for (i = 0; i < buffer->modified_areas; ++i)
1165             {
1166                 GL_EXTCALL(glFlushMappedBufferRangeAPPLE(buffer->buffer_type_hint,
1167                         buffer->maps[i].offset, buffer->maps[i].size));
1168                 checkGLcall("glFlushMappedBufferRangeAPPLE");
1169             }
1170         }
1171
1172         GL_EXTCALL(glUnmapBufferARB(buffer->buffer_type_hint));
1173         LEAVE_GL();
1174         context_release(context);
1175
1176         buffer->resource.allocatedMemory = NULL;
1177         buffer_clear_dirty_areas(buffer);
1178     }
1179     else if (buffer->flags & WINED3D_BUFFER_HASDESC)
1180     {
1181         wined3d_buffer_preload(buffer);
1182     }
1183 }
1184
1185 static const struct wined3d_resource_ops buffer_resource_ops =
1186 {
1187     buffer_unload,
1188 };
1189
1190 static HRESULT buffer_init(struct wined3d_buffer *buffer, struct wined3d_device *device,
1191         UINT size, DWORD usage, enum wined3d_format_id format_id, WINED3DPOOL pool, GLenum bind_hint,
1192         const char *data, void *parent, const struct wined3d_parent_ops *parent_ops)
1193 {
1194     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1195     const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
1196     HRESULT hr;
1197     BOOL dynamic_buffer_ok;
1198
1199     if (!size)
1200     {
1201         WARN("Size 0 requested, returning WINED3DERR_INVALIDCALL\n");
1202         return WINED3DERR_INVALIDCALL;
1203     }
1204
1205     hr = resource_init(&buffer->resource, device, WINED3DRTYPE_BUFFER, format,
1206             WINED3DMULTISAMPLE_NONE, 0, usage, pool, size, 1, 1, size,
1207             parent, parent_ops, &buffer_resource_ops);
1208     if (FAILED(hr))
1209     {
1210         WARN("Failed to initialize resource, hr %#x\n", hr);
1211         return hr;
1212     }
1213     buffer->buffer_type_hint = bind_hint;
1214
1215     TRACE("size %#x, usage %#x, format %s, memory @ %p, iface @ %p.\n", buffer->resource.size, buffer->resource.usage,
1216             debug_d3dformat(buffer->resource.format->id), buffer->resource.allocatedMemory, buffer);
1217
1218     dynamic_buffer_ok = gl_info->supported[APPLE_FLUSH_BUFFER_RANGE] || gl_info->supported[ARB_MAP_BUFFER_RANGE];
1219
1220     /* Observations show that drawStridedSlow is faster on dynamic VBs than converting +
1221      * drawStridedFast (half-life 2 and others).
1222      *
1223      * Basically converting the vertices in the buffer is quite expensive, and observations
1224      * show that drawStridedSlow is faster than converting + uploading + drawStridedFast.
1225      * Therefore do not create a VBO for WINED3DUSAGE_DYNAMIC buffers.
1226      */
1227     if (!gl_info->supported[ARB_VERTEX_BUFFER_OBJECT])
1228     {
1229         TRACE("Not creating a vbo because GL_ARB_vertex_buffer is not supported\n");
1230     }
1231     else if(buffer->resource.pool == WINED3DPOOL_SYSTEMMEM)
1232     {
1233         TRACE("Not creating a vbo because the vertex buffer is in system memory\n");
1234     }
1235     else if(!dynamic_buffer_ok && (buffer->resource.usage & WINED3DUSAGE_DYNAMIC))
1236     {
1237         TRACE("Not creating a vbo because the buffer has dynamic usage and no GL support\n");
1238     }
1239     else
1240     {
1241         buffer->flags |= WINED3D_BUFFER_CREATEBO;
1242     }
1243
1244     if (data)
1245     {
1246         BYTE *ptr;
1247
1248         hr = wined3d_buffer_map(buffer, 0, size, &ptr, 0);
1249         if (FAILED(hr))
1250         {
1251             ERR("Failed to map buffer, hr %#x\n", hr);
1252             buffer_unload(&buffer->resource);
1253             resource_cleanup(&buffer->resource);
1254             return hr;
1255         }
1256
1257         memcpy(ptr, data, size);
1258
1259         wined3d_buffer_unmap(buffer);
1260     }
1261
1262     buffer->maps = HeapAlloc(GetProcessHeap(), 0, sizeof(*buffer->maps));
1263     if (!buffer->maps)
1264     {
1265         ERR("Out of memory\n");
1266         buffer_unload(&buffer->resource);
1267         resource_cleanup(&buffer->resource);
1268         return E_OUTOFMEMORY;
1269     }
1270     buffer->maps_size = 1;
1271
1272     return WINED3D_OK;
1273 }
1274
1275 HRESULT CDECL wined3d_buffer_create(struct wined3d_device *device, struct wined3d_buffer_desc *desc, const void *data,
1276         void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer)
1277 {
1278     struct wined3d_buffer *object;
1279     HRESULT hr;
1280
1281     TRACE("device %p, desc %p, data %p, parent %p, buffer %p\n", device, desc, data, parent, buffer);
1282
1283     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1284     if (!object)
1285     {
1286         ERR("Failed to allocate memory\n");
1287         return E_OUTOFMEMORY;
1288     }
1289
1290     FIXME("Ignoring access flags (pool)\n");
1291
1292     hr = buffer_init(object, device, desc->byte_width, desc->usage, WINED3DFMT_UNKNOWN,
1293             WINED3DPOOL_MANAGED, GL_ARRAY_BUFFER_ARB, data, parent, parent_ops);
1294     if (FAILED(hr))
1295     {
1296         WARN("Failed to initialize buffer, hr %#x.\n", hr);
1297         HeapFree(GetProcessHeap(), 0, object);
1298         return hr;
1299     }
1300     object->desc = *desc;
1301
1302     TRACE("Created buffer %p.\n", object);
1303
1304     *buffer = object;
1305
1306     return WINED3D_OK;
1307 }
1308
1309 HRESULT CDECL wined3d_buffer_create_vb(struct wined3d_device *device, UINT size, DWORD usage, WINED3DPOOL pool,
1310         void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer)
1311 {
1312     struct wined3d_buffer *object;
1313     HRESULT hr;
1314
1315     TRACE("device %p, size %u, usage %#x, pool %#x, parent %p, parent_ops %p, buffer %p.\n",
1316             device, size, usage, pool, parent, parent_ops, buffer);
1317
1318     if (pool == WINED3DPOOL_SCRATCH)
1319     {
1320         /* The d3d9 tests shows that this is not allowed. It doesn't make much
1321          * sense anyway, SCRATCH buffers wouldn't be usable anywhere. */
1322         WARN("Vertex buffer in WINED3DPOOL_SCRATCH requested, returning WINED3DERR_INVALIDCALL.\n");
1323         *buffer = NULL;
1324         return WINED3DERR_INVALIDCALL;
1325     }
1326
1327     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1328     if (!object)
1329     {
1330         ERR("Out of memory\n");
1331         *buffer = NULL;
1332         return WINED3DERR_OUTOFVIDEOMEMORY;
1333     }
1334
1335     hr = buffer_init(object, device, size, usage, WINED3DFMT_VERTEXDATA,
1336             pool, GL_ARRAY_BUFFER_ARB, NULL, parent, parent_ops);
1337     if (FAILED(hr))
1338     {
1339         WARN("Failed to initialize buffer, hr %#x.\n", hr);
1340         HeapFree(GetProcessHeap(), 0, object);
1341         return hr;
1342     }
1343
1344     TRACE("Created buffer %p.\n", object);
1345     *buffer = object;
1346
1347     return WINED3D_OK;
1348 }
1349
1350 HRESULT CDECL wined3d_buffer_create_ib(struct wined3d_device *device, UINT size, DWORD usage, WINED3DPOOL pool,
1351         void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer)
1352 {
1353     struct wined3d_buffer *object;
1354     HRESULT hr;
1355
1356     TRACE("device %p, size %u, usage %#x, pool %#x, parent %p, parent_ops %p, buffer %p.\n",
1357             device, size, usage, pool, parent, parent_ops, buffer);
1358
1359     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1360     if (!object)
1361     {
1362         ERR("Out of memory\n");
1363         *buffer = NULL;
1364         return WINED3DERR_OUTOFVIDEOMEMORY;
1365     }
1366
1367     hr = buffer_init(object, device, size, usage | WINED3DUSAGE_STATICDECL,
1368             WINED3DFMT_UNKNOWN, pool, GL_ELEMENT_ARRAY_BUFFER_ARB, NULL,
1369             parent, parent_ops);
1370     if (FAILED(hr))
1371     {
1372         WARN("Failed to initialize buffer, hr %#x\n", hr);
1373         HeapFree(GetProcessHeap(), 0, object);
1374         return hr;
1375     }
1376
1377     TRACE("Created buffer %p.\n", object);
1378     *buffer = object;
1379
1380     return WINED3D_OK;
1381 }