VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Graphics/Wine/wined3d/buffer.c@ 30585

Last change on this file since 30585 was 28475, checked in by vboxsync, 15 years ago

crOpenGL: update to wine 1.1.43

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette