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