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