VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Graphics/Wine_new/wined3d/buffer.c@ 62522

Last change on this file since 62522 was 51270, checked in by vboxsync, 11 years ago

wine: update to 1.6.2

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

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