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