VirtualBox

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

Last change on this file since 69362 was 63025, checked in by vboxsync, 9 years ago

GA/NT/Graphics: warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 54.3 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#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
374 DWORD stride_this_run = 0;
375 DWORD offset_this_run = 0;
376#else
377 UINT stride_this_run = 0;
378#endif
379 BOOL ret = FALSE;
380
381 /* In d3d7 the vertex buffer declaration NEVER changes because it is stored in the d3d7 vertex buffer.
382 * Once we have our declaration there is no need to look it up again. Index buffers also never need
383 * conversion, so once the (empty) conversion structure is created don't bother checking again
384 */
385 if (This->flags & WINED3D_BUFFER_HASDESC)
386 {
387 if(This->resource.usage & WINED3DUSAGE_STATICDECL) return FALSE;
388 }
389
390 if (use_vs(state))
391 {
392 TRACE("Vertex shaders used, no VBO conversion is needed\n");
393 if(This->conversion_map)
394 {
395 HeapFree(GetProcessHeap(), 0, This->conversion_map);
396 This->conversion_map = NULL;
397 This->stride = 0;
398 return TRUE;
399 }
400
401 return FALSE;
402 }
403
404 TRACE("Finding vertex buffer conversion information\n");
405 /* Certain declaration types need some fixups before we can pass them to
406 * opengl. This means D3DCOLOR attributes with fixed function vertex
407 * processing, FLOAT4 POSITIONT with fixed function, and FLOAT16 if
408 * GL_ARB_half_float_vertex is not supported.
409 *
410 * Note for d3d8 and d3d9:
411 * The vertex buffer FVF doesn't help with finding them, we have to use
412 * the decoded vertex declaration and pick the things that concern the
413 * current buffer. A problem with this is that this can change between
414 * draws, so we have to validate the information and reprocess the buffer
415 * if it changes, and avoid false positives for performance reasons.
416 * WineD3D doesn't even know the vertex buffer any more, it is managed
417 * by the client libraries and passed to SetStreamSource and ProcessVertices
418 * as needed.
419 *
420 * We have to distinguish between vertex shaders and fixed function to
421 * pick the way we access the strided vertex information.
422 *
423 * This code sets up a per-byte array with the size of the detected
424 * stride of the arrays in the buffer. For each byte we have a field
425 * that marks the conversion needed on this byte. For example, the
426 * following declaration with fixed function vertex processing:
427 *
428 * POSITIONT, FLOAT4
429 * NORMAL, FLOAT3
430 * DIFFUSE, FLOAT16_4
431 * SPECULAR, D3DCOLOR
432 *
433 * Will result in
434 * { POSITIONT }{ NORMAL }{ DIFFUSE }{SPECULAR }
435 * [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]
436 *
437 * Where in this example map P means 4 component position conversion, 0
438 * means no conversion, F means FLOAT16_2 conversion and C means D3DCOLOR
439 * conversion (red / blue swizzle).
440 *
441 * If we're doing conversion and the stride changes we have to reconvert
442 * the whole buffer. Note that we do not mind if the semantic changes,
443 * we only care for the conversion type. So if the NORMAL is replaced
444 * with a TEXCOORD, nothing has to be done, or if the DIFFUSE is replaced
445 * with a D3DCOLOR BLENDWEIGHT we can happily dismiss the change. Some
446 * conversion types depend on the semantic as well, for example a FLOAT4
447 * texcoord needs no conversion while a FLOAT4 positiont needs one
448 */
449
450 ret = buffer_check_attribute(This, si, WINED3D_FFP_POSITION,
451 TRUE, !support_xyzrhw, FALSE, &stride_this_run
452#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
453 , &offset_this_run
454#endif
455 ) || ret;
456 ret = buffer_check_attribute(This, si, WINED3D_FFP_NORMAL,
457 TRUE, FALSE, FALSE, &stride_this_run
458#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
459 , &offset_this_run
460#endif
461 ) || ret;
462 ret = buffer_check_attribute(This, si, WINED3D_FFP_DIFFUSE,
463 !support_d3dcolor, FALSE, TRUE, &stride_this_run
464#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
465 , &offset_this_run
466#endif
467 ) || ret;
468 ret = buffer_check_attribute(This, si, WINED3D_FFP_SPECULAR,
469 !support_d3dcolor, FALSE, TRUE, &stride_this_run
470#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
471 , &offset_this_run
472#endif
473 ) || ret;
474 ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD0,
475 TRUE, FALSE, FALSE, &stride_this_run
476#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
477 , &offset_this_run
478#endif
479 ) || ret;
480 ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD1,
481 TRUE, FALSE, FALSE, &stride_this_run
482#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
483 , &offset_this_run
484#endif
485 ) || ret;
486 ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD2,
487 TRUE, FALSE, FALSE, &stride_this_run
488#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
489 , &offset_this_run
490#endif
491 ) || ret;
492 ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD3,
493 TRUE, FALSE, FALSE, &stride_this_run
494#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
495 , &offset_this_run
496#endif
497 ) || ret;
498 ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD4,
499 TRUE, FALSE, FALSE, &stride_this_run
500#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
501 , &offset_this_run
502#endif
503 ) || ret;
504 ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD5,
505 TRUE, FALSE, FALSE, &stride_this_run
506#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
507 , &offset_this_run
508#endif
509 ) || ret;
510 ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD6,
511 TRUE, FALSE, FALSE, &stride_this_run
512#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
513 , &offset_this_run
514#endif
515 ) || ret;
516 ret = buffer_check_attribute(This, si, WINED3D_FFP_TEXCOORD7,
517 TRUE, FALSE, FALSE, &stride_this_run
518#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
519 , &offset_this_run
520#endif
521 ) || ret;
522
523 if (!stride_this_run && This->conversion_map)
524 {
525 /* Sanity test */
526 if (!ret) ERR("no converted attributes found, old conversion map exists, and no declaration change?\n");
527 HeapFree(GetProcessHeap(), 0, This->conversion_map);
528 This->conversion_map = NULL;
529 This->stride = 0;
530 }
531
532 if (ret) TRACE("Conversion information changed\n");
533
534 return ret;
535}
536
537static inline void fixup_d3dcolor(DWORD *dst_color)
538{
539 DWORD src_color = *dst_color;
540
541 /* Color conversion like in drawStridedSlow. watch out for little endianity
542 * If we want that stuff to work on big endian machines too we have to consider more things
543 *
544 * 0xff000000: Alpha mask
545 * 0x00ff0000: Blue mask
546 * 0x0000ff00: Green mask
547 * 0x000000ff: Red mask
548 */
549 *dst_color = 0;
550 *dst_color |= (src_color & 0xff00ff00); /* Alpha Green */
551 *dst_color |= (src_color & 0x00ff0000) >> 16; /* Red */
552 *dst_color |= (src_color & 0x000000ff) << 16; /* Blue */
553}
554
555static inline void fixup_transformed_pos(float *p)
556{
557 /* rhw conversion like in position_float4(). */
558 if (p[3] != 1.0f && p[3] != 0.0f)
559 {
560 float w = 1.0f / p[3];
561 p[0] *= w;
562 p[1] *= w;
563 p[2] *= w;
564 p[3] = w;
565 }
566}
567
568/* Context activation is done by the caller. */
569void buffer_get_memory(struct wined3d_buffer *buffer, const struct wined3d_gl_info *gl_info,
570 struct wined3d_bo_address *data)
571{
572 data->buffer_object = buffer->buffer_object;
573 if (!buffer->buffer_object)
574 {
575 if ((buffer->flags & WINED3D_BUFFER_CREATEBO) && !buffer->resource.map_count)
576 {
577 buffer_create_buffer_object(buffer, gl_info);
578 buffer->flags &= ~WINED3D_BUFFER_CREATEBO;
579 if (buffer->buffer_object)
580 {
581 data->buffer_object = buffer->buffer_object;
582 data->addr = NULL;
583 return;
584 }
585 }
586 data->addr = buffer->resource.allocatedMemory;
587 }
588 else
589 {
590 data->addr = NULL;
591 }
592}
593
594ULONG CDECL wined3d_buffer_incref(struct wined3d_buffer *buffer)
595{
596 ULONG refcount = InterlockedIncrement(&buffer->resource.ref);
597
598 TRACE("%p increasing refcount to %u.\n", buffer, refcount);
599
600 return refcount;
601}
602
603/* Context activation is done by the caller. */
604BYTE *buffer_get_sysmem(struct wined3d_buffer *This, const struct wined3d_gl_info *gl_info)
605{
606 /* AllocatedMemory exists if the buffer is double buffered or has no buffer object at all */
607 if(This->resource.allocatedMemory) return This->resource.allocatedMemory;
608
609 This->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + RESOURCE_ALIGNMENT);
610 This->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
611
612 if (This->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
613 device_invalidate_state(This->resource.device, STATE_INDEXBUFFER);
614
615 GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
616 GL_EXTCALL(glGetBufferSubDataARB(This->buffer_type_hint, 0, This->resource.size, This->resource.allocatedMemory));
617 This->flags |= WINED3D_BUFFER_DOUBLEBUFFER;
618
619 return This->resource.allocatedMemory;
620}
621
622/* Do not call while under the GL lock. */
623static void buffer_unload(struct wined3d_resource *resource)
624{
625 struct wined3d_buffer *buffer = buffer_from_resource(resource);
626
627 TRACE("buffer %p.\n", buffer);
628
629 if (buffer->buffer_object)
630 {
631 struct wined3d_device *device = resource->device;
632 struct wined3d_context *context;
633
634 context = context_acquire(device, NULL);
635
636 /* Download the buffer, but don't permanently enable double buffering */
637 if (!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER))
638 {
639 buffer_get_sysmem(buffer, context->gl_info);
640 buffer->flags &= ~WINED3D_BUFFER_DOUBLEBUFFER;
641 }
642
643 delete_gl_buffer(buffer, context->gl_info);
644 buffer->flags |= WINED3D_BUFFER_CREATEBO; /* Recreate the buffer object next load */
645 buffer_clear_dirty_areas(buffer);
646
647 context_release(context);
648
649 HeapFree(GetProcessHeap(), 0, buffer->conversion_map);
650 buffer->conversion_map = NULL;
651 buffer->stride = 0;
652 buffer->conversion_stride = 0;
653 buffer->flags &= ~WINED3D_BUFFER_HASDESC;
654 }
655
656 resource_unload(resource);
657}
658
659/* Do not call while under the GL lock. */
660ULONG CDECL wined3d_buffer_decref(struct wined3d_buffer *buffer)
661{
662 ULONG refcount = InterlockedDecrement(&buffer->resource.ref);
663 struct wined3d_context *context;
664
665 TRACE("%p decreasing refcount to %u.\n", buffer, refcount);
666
667 if (!refcount)
668 {
669 if (buffer->buffer_object)
670 {
671 context = context_acquire(buffer->resource.device, NULL);
672 delete_gl_buffer(buffer, context->gl_info);
673 context_release(context);
674
675 HeapFree(GetProcessHeap(), 0, buffer->conversion_map);
676 }
677
678 resource_cleanup(&buffer->resource);
679 buffer->resource.parent_ops->wined3d_object_destroyed(buffer->resource.parent);
680 HeapFree(GetProcessHeap(), 0, buffer->maps);
681 HeapFree(GetProcessHeap(), 0, buffer);
682 }
683
684 return refcount;
685}
686
687void * CDECL wined3d_buffer_get_parent(const struct wined3d_buffer *buffer)
688{
689 TRACE("buffer %p.\n", buffer);
690
691 return buffer->resource.parent;
692}
693
694DWORD CDECL wined3d_buffer_set_priority(struct wined3d_buffer *buffer, DWORD priority)
695{
696 return resource_set_priority(&buffer->resource, priority);
697}
698
699DWORD CDECL wined3d_buffer_get_priority(const struct wined3d_buffer *buffer)
700{
701 return resource_get_priority(&buffer->resource);
702}
703
704/* The caller provides a context and binds the buffer */
705static void buffer_sync_apple(struct wined3d_buffer *This, DWORD flags, const struct wined3d_gl_info *gl_info)
706{
707 enum wined3d_event_query_result ret;
708
709 /* No fencing needs to be done if the app promises not to overwrite
710 * existing data. */
711 if (flags & WINED3D_MAP_NOOVERWRITE)
712 return;
713
714 if (flags & WINED3D_MAP_DISCARD)
715 {
716 GL_EXTCALL(glBufferDataARB(This->buffer_type_hint, This->resource.size, NULL, This->buffer_object_usage));
717 checkGLcall("glBufferDataARB\n");
718 return;
719 }
720
721 if(!This->query)
722 {
723 TRACE("Creating event query for buffer %p\n", This);
724
725 if (!wined3d_event_query_supported(gl_info))
726 {
727 FIXME("Event queries not supported, dropping async buffer locks.\n");
728 goto drop_query;
729 }
730
731 This->query = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This->query));
732 if (!This->query)
733 {
734 ERR("Failed to allocate event query memory, dropping async buffer locks.\n");
735 goto drop_query;
736 }
737
738 /* Since we don't know about old draws a glFinish is needed once */
739 gl_info->gl_ops.gl.p_glFinish();
740 return;
741 }
742 TRACE("Synchronizing buffer %p\n", This);
743 ret = wined3d_event_query_finish(This->query, This->resource.device);
744 switch(ret)
745 {
746 case WINED3D_EVENT_QUERY_NOT_STARTED:
747 case WINED3D_EVENT_QUERY_OK:
748 /* All done */
749 return;
750
751 case WINED3D_EVENT_QUERY_WRONG_THREAD:
752 WARN("Cannot synchronize buffer lock due to a thread conflict\n");
753 goto drop_query;
754
755 default:
756 ERR("wined3d_event_query_finish returned %u, dropping async buffer locks\n", ret);
757 goto drop_query;
758 }
759
760drop_query:
761 if(This->query)
762 {
763 wined3d_event_query_destroy(This->query);
764 This->query = NULL;
765 }
766
767 gl_info->gl_ops.gl.p_glFinish();
768 GL_EXTCALL(glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_TRUE));
769 checkGLcall("glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_TRUE)");
770 This->flags &= ~WINED3D_BUFFER_APPLESYNC;
771}
772
773/* The caller provides a GL context */
774static void buffer_direct_upload(struct wined3d_buffer *This, const struct wined3d_gl_info *gl_info, DWORD flags)
775{
776 BYTE *map;
777 UINT start = 0, len = 0;
778
779 /* This potentially invalidates the element array buffer binding, but the
780 * caller always takes care of this. */
781 GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
782 checkGLcall("glBindBufferARB");
783 if (gl_info->supported[ARB_MAP_BUFFER_RANGE])
784 {
785 GLbitfield mapflags;
786 mapflags = GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
787 if (flags & WINED3D_BUFFER_DISCARD)
788 mapflags |= GL_MAP_INVALIDATE_BUFFER_BIT;
789 if (flags & WINED3D_BUFFER_NOSYNC)
790 mapflags |= GL_MAP_UNSYNCHRONIZED_BIT;
791 map = GL_EXTCALL(glMapBufferRange(This->buffer_type_hint, 0,
792 This->resource.size, mapflags));
793 checkGLcall("glMapBufferRange");
794 }
795 else
796 {
797 if (This->flags & WINED3D_BUFFER_APPLESYNC)
798 {
799 DWORD syncflags = 0;
800 if (flags & WINED3D_BUFFER_DISCARD)
801 syncflags |= WINED3D_MAP_DISCARD;
802 if (flags & WINED3D_BUFFER_NOSYNC)
803 syncflags |= WINED3D_MAP_NOOVERWRITE;
804 buffer_sync_apple(This, syncflags, gl_info);
805 }
806 map = GL_EXTCALL(glMapBufferARB(This->buffer_type_hint, GL_WRITE_ONLY_ARB));
807 checkGLcall("glMapBufferARB");
808 }
809 if (!map)
810 {
811 ERR("Failed to map opengl buffer\n");
812 return;
813 }
814
815 while (This->modified_areas)
816 {
817 This->modified_areas--;
818 start = This->maps[This->modified_areas].offset;
819 len = This->maps[This->modified_areas].size;
820
821 memcpy(map + start, This->resource.allocatedMemory + start, len);
822
823 if (gl_info->supported[ARB_MAP_BUFFER_RANGE])
824 {
825 GL_EXTCALL(glFlushMappedBufferRange(This->buffer_type_hint, start, len));
826 checkGLcall("glFlushMappedBufferRange");
827 }
828 else if (This->flags & WINED3D_BUFFER_FLUSH)
829 {
830 GL_EXTCALL(glFlushMappedBufferRangeAPPLE(This->buffer_type_hint, start, len));
831 checkGLcall("glFlushMappedBufferRangeAPPLE");
832 }
833 }
834 GL_EXTCALL(glUnmapBufferARB(This->buffer_type_hint));
835 checkGLcall("glUnmapBufferARB");
836}
837
838#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
839static UINT wined3d_buffer_stream_offset_left(UINT x, UINT offset, UINT stride)
840{
841 UINT off = ((UINT)(RT_ABS((INT)(x - offset))) % stride);
842 if (!off)
843 return x;
844 if (x > offset)
845 return x - off;
846
847 /* x < offset */
848 return x - stride + off;
849}
850
851static UINT wined3d_buffer_stream_offset_right(UINT x, UINT offset, UINT stride)
852{
853 UINT left = wined3d_buffer_stream_offset_left(x, offset, stride);
854
855 if (left == x)
856 return x;
857 return left + stride;
858}
859#endif
860
861/* Do not call while under the GL lock. */
862void CDECL wined3d_buffer_preload(struct wined3d_buffer *buffer)
863{
864 DWORD flags = buffer->flags & (WINED3D_BUFFER_NOSYNC | WINED3D_BUFFER_DISCARD);
865 struct wined3d_device *device = buffer->resource.device;
866 UINT start = 0, end = 0, len = 0, vertices;
867 const struct wined3d_gl_info *gl_info;
868 struct wined3d_context *context;
869 BOOL decl_changed = FALSE;
870#ifndef VBOX_WITH_WINE_FIX_BUFOFFSET
871 unsigned int i, j;
872#endif
873 BYTE *data;
874#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
875 unsigned int j;
876 BYTE *cur_ptr;
877 UINT conv_start, conv_end;
878#endif
879
880 TRACE("buffer %p.\n", buffer);
881
882 if (buffer->resource.map_count)
883 {
884 WARN("Buffer is mapped, skipping preload.\n");
885 return;
886 }
887
888 buffer->flags &= ~(WINED3D_BUFFER_NOSYNC | WINED3D_BUFFER_DISCARD);
889
890 if (!buffer->buffer_object)
891 {
892 /* TODO: Make converting independent from VBOs */
893 if (buffer->flags & WINED3D_BUFFER_CREATEBO)
894 {
895 context = context_acquire(device, NULL);
896 buffer_create_buffer_object(buffer, context->gl_info);
897 context_release(context);
898 buffer->flags &= ~WINED3D_BUFFER_CREATEBO;
899 }
900 else
901 {
902 /* Not doing any conversion */
903 return;
904 }
905 }
906
907 /* Reading the declaration makes only sense if the stateblock is finalized and the buffer bound to a stream */
908 if (device->isInDraw && buffer->resource.bind_count > 0)
909 {
910 decl_changed = buffer_find_decl(buffer);
911 buffer->flags |= WINED3D_BUFFER_HASDESC;
912 }
913
914 if (!decl_changed && !(buffer->flags & WINED3D_BUFFER_HASDESC && buffer_is_dirty(buffer)))
915 {
916 ++buffer->draw_count;
917 if (buffer->draw_count > VB_RESETDECLCHANGE)
918 buffer->decl_change_count = 0;
919 if (buffer->draw_count > VB_RESETFULLCONVS)
920 buffer->full_conversion_count = 0;
921 return;
922 }
923
924 /* If applications change the declaration over and over, reconverting all the time is a huge
925 * performance hit. So count the declaration changes and release the VBO if there are too many
926 * of them (and thus stop converting)
927 */
928 if (decl_changed)
929 {
930 ++buffer->decl_change_count;
931 buffer->draw_count = 0;
932
933 if (buffer->decl_change_count > VB_MAXDECLCHANGES
934 || (buffer->conversion_map && (buffer->resource.usage & WINED3DUSAGE_DYNAMIC)))
935 {
936 FIXME("Too many declaration changes or converting dynamic buffer, stopping converting\n");
937
938 buffer_unload(&buffer->resource);
939 buffer->flags &= ~WINED3D_BUFFER_CREATEBO;
940
941 /* The stream source state handler might have read the memory of
942 * the vertex buffer already and got the memory in the vbo which
943 * is not valid any longer. Dirtify the stream source to force a
944 * reload. This happens only once per changed vertexbuffer and
945 * should occur rather rarely. */
946 device_invalidate_state(device, STATE_STREAMSRC);
947 return;
948 }
949
950 /* The declaration changed, reload the whole buffer */
951 WARN("Reloading buffer because of decl change\n");
952 buffer_clear_dirty_areas(buffer);
953 if (!buffer_add_dirty_area(buffer, 0, 0))
954 {
955 ERR("buffer_add_dirty_area failed, this is not expected\n");
956 return;
957 }
958 /* Avoid unfenced updates, we might overwrite more areas of the buffer than the application
959 * cleared for unsynchronized updates
960 */
961 flags = 0;
962 }
963 else
964 {
965 /* However, it is perfectly fine to change the declaration every now and then. We don't want a game that
966 * changes it every minute drop the VBO after VB_MAX_DECL_CHANGES minutes. So count draws without
967 * decl changes and reset the decl change count after a specific number of them
968 */
969 if (buffer->conversion_map && buffer_is_fully_dirty(buffer))
970 {
971 ++buffer->full_conversion_count;
972 if (buffer->full_conversion_count > VB_MAXFULLCONVERSIONS)
973 {
974 FIXME("Too many full buffer conversions, stopping converting.\n");
975 buffer_unload(&buffer->resource);
976 buffer->flags &= ~WINED3D_BUFFER_CREATEBO;
977 if (buffer->resource.bind_count)
978 device_invalidate_state(device, STATE_STREAMSRC);
979 return;
980 }
981 }
982 else
983 {
984 ++buffer->draw_count;
985 if (buffer->draw_count > VB_RESETDECLCHANGE)
986 buffer->decl_change_count = 0;
987 if (buffer->draw_count > VB_RESETFULLCONVS)
988 buffer->full_conversion_count = 0;
989 }
990 }
991
992 if (buffer->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
993 device_invalidate_state(device, STATE_INDEXBUFFER);
994
995 if (!buffer->conversion_map)
996 {
997 /* That means that there is nothing to fixup. Just upload from
998 * buffer->resource.allocatedMemory directly into the vbo. Do not
999 * free the system memory copy because drawPrimitive may need it if
1000 * the stride is 0, for instancing emulation, vertex blending
1001 * emulation or shader emulation. */
1002 TRACE("No conversion needed.\n");
1003
1004 /* Nothing to do because we locked directly into the vbo */
1005 if (!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER))
1006 {
1007 return;
1008 }
1009
1010 context = context_acquire(device, NULL);
1011 buffer_direct_upload(buffer, context->gl_info, flags);
1012
1013 context_release(context);
1014 return;
1015 }
1016
1017 context = context_acquire(device, NULL);
1018 gl_info = context->gl_info;
1019
1020 if(!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER))
1021 {
1022 buffer_get_sysmem(buffer, gl_info);
1023 }
1024
1025 /* Now for each vertex in the buffer that needs conversion */
1026 vertices = buffer->resource.size / buffer->stride;
1027
1028 data = HeapAlloc(GetProcessHeap(), 0, buffer->resource.size);
1029
1030 while(buffer->modified_areas)
1031 {
1032 buffer->modified_areas--;
1033 start = buffer->maps[buffer->modified_areas].offset;
1034 len = buffer->maps[buffer->modified_areas].size;
1035 end = start + len;
1036
1037#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
1038 if (buffer->offset > UINT32_MAX)
1039 {
1040 ERR("negative offset, unexpected!");
1041 }
1042
1043 conv_start = wined3d_buffer_stream_offset_left(start, buffer->offset, buffer->stride);
1044 if (conv_start <= start)
1045 {
1046 start = conv_start;
1047 }
1048 else
1049 {
1050 /* <- < 0, add stride */
1051 conv_start += buffer->stride;
1052 }
1053
1054 Assert(conv_start < UINT32_MAX/3);
1055
1056 conv_end = wined3d_buffer_stream_offset_right(end, buffer->offset, buffer->stride);
1057 if (conv_end > end)
1058 {
1059 if (conv_end <= buffer->resource.size)
1060 end = conv_end;
1061 else
1062 conv_end -= buffer->stride;
1063 }
1064 else if (conv_end < end)
1065 {
1066 ERR("unexpected!");
1067 }
1068 else
1069 {
1070 end = conv_end;
1071 }
1072#endif
1073 memcpy(data + start, buffer->resource.allocatedMemory + start, end - start);
1074
1075#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
1076 for (cur_ptr = data + conv_start; cur_ptr < data + conv_end; cur_ptr += buffer->stride)
1077#else
1078 for (i = start / buffer->stride; i < min((end / buffer->stride) + 1, vertices); ++i)
1079#endif
1080 {
1081 for (j = 0; j < buffer->stride; ++j)
1082 {
1083 switch (buffer->conversion_map[j])
1084 {
1085 case CONV_NONE:
1086 /* Done already */
1087 j += 3;
1088 break;
1089 case CONV_D3DCOLOR:
1090#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
1091 fixup_d3dcolor((DWORD *) (cur_ptr + j));
1092#else
1093 fixup_d3dcolor((DWORD *) (data + i * buffer->stride + j));
1094#endif
1095 j += 3;
1096 break;
1097
1098 case CONV_POSITIONT:
1099#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
1100 fixup_transformed_pos((float *) (cur_ptr + j));
1101#else
1102 fixup_transformed_pos((float *) (data + i * buffer->stride + j));
1103#endif
1104 j += 15;
1105 break;
1106 default:
1107 FIXME("Unimplemented conversion %d in shifted conversion\n", buffer->conversion_map[j]);
1108 }
1109 }
1110 }
1111
1112 GL_EXTCALL(glBindBufferARB(buffer->buffer_type_hint, buffer->buffer_object));
1113 checkGLcall("glBindBufferARB");
1114 GL_EXTCALL(glBufferSubDataARB(buffer->buffer_type_hint, start, len, data + start));
1115 checkGLcall("glBufferSubDataARB");
1116 }
1117
1118 HeapFree(GetProcessHeap(), 0, data);
1119 context_release(context);
1120}
1121
1122static DWORD buffer_sanitize_flags(const struct wined3d_buffer *buffer, DWORD flags)
1123{
1124 /* Not all flags make sense together, but Windows never returns an error.
1125 * Catch the cases that could cause issues. */
1126 if (flags & WINED3D_MAP_READONLY)
1127 {
1128 if (flags & WINED3D_MAP_DISCARD)
1129 {
1130 WARN("WINED3D_MAP_READONLY combined with WINED3D_MAP_DISCARD, ignoring flags.\n");
1131 return 0;
1132 }
1133 if (flags & WINED3D_MAP_NOOVERWRITE)
1134 {
1135 WARN("WINED3D_MAP_READONLY combined with WINED3D_MAP_NOOVERWRITE, ignoring flags.\n");
1136 return 0;
1137 }
1138 }
1139 else if ((flags & (WINED3D_MAP_DISCARD | WINED3D_MAP_NOOVERWRITE))
1140 == (WINED3D_MAP_DISCARD | WINED3D_MAP_NOOVERWRITE))
1141 {
1142 WARN("WINED3D_MAP_DISCARD and WINED3D_MAP_NOOVERWRITE used together, ignoring.\n");
1143 return 0;
1144 }
1145 else if (flags & (WINED3D_MAP_DISCARD | WINED3D_MAP_NOOVERWRITE)
1146 && !(buffer->resource.usage & WINED3DUSAGE_DYNAMIC))
1147 {
1148 WARN("DISCARD or NOOVERWRITE map on non-dynamic buffer, ignoring.\n");
1149 return 0;
1150 }
1151
1152 return flags;
1153}
1154
1155static GLbitfield buffer_gl_map_flags(DWORD d3d_flags)
1156{
1157 GLbitfield ret = 0;
1158
1159 if (!(d3d_flags & WINED3D_MAP_READONLY))
1160 ret |= GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
1161 if (!(d3d_flags & (WINED3D_MAP_DISCARD | WINED3D_MAP_NOOVERWRITE)))
1162 ret |= GL_MAP_READ_BIT;
1163
1164 if (d3d_flags & WINED3D_MAP_DISCARD)
1165 ret |= GL_MAP_INVALIDATE_BUFFER_BIT;
1166 if (d3d_flags & WINED3D_MAP_NOOVERWRITE)
1167 ret |= GL_MAP_UNSYNCHRONIZED_BIT;
1168
1169 return ret;
1170}
1171
1172struct wined3d_resource * CDECL wined3d_buffer_get_resource(struct wined3d_buffer *buffer)
1173{
1174 TRACE("buffer %p.\n", buffer);
1175
1176 return &buffer->resource;
1177}
1178
1179HRESULT CDECL wined3d_buffer_map(struct wined3d_buffer *buffer, UINT offset, UINT size, BYTE **data, DWORD flags)
1180{
1181 BOOL dirty = buffer_is_dirty(buffer);
1182 LONG count;
1183
1184 TRACE("buffer %p, offset %u, size %u, data %p, flags %#x\n", buffer, offset, size, data, flags);
1185
1186 flags = buffer_sanitize_flags(buffer, flags);
1187 if (flags & WINED3D_MAP_DISCARD)
1188 {
1189 /* DISCARD invalidates the entire buffer, regardless of the specified
1190 * offset and size. Some applications also depend on the entire buffer
1191 * being uploaded in that case. Two such applications are Port Royale
1192 * and Darkstar One. */
1193 if (!buffer_add_dirty_area(buffer, 0, 0))
1194 return E_OUTOFMEMORY;
1195 }
1196 else if (!(flags & WINED3D_MAP_READONLY))
1197 {
1198 if (!buffer_add_dirty_area(buffer, offset, size))
1199 return E_OUTOFMEMORY;
1200 }
1201
1202 count = ++buffer->resource.map_count;
1203
1204 if (buffer->buffer_object)
1205 {
1206 if (!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER))
1207 {
1208 if (count == 1)
1209 {
1210 struct wined3d_device *device = buffer->resource.device;
1211 struct wined3d_context *context;
1212 const struct wined3d_gl_info *gl_info;
1213
1214 context = context_acquire(device, NULL);
1215 gl_info = context->gl_info;
1216
1217 if (buffer->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
1218 context_invalidate_state(context, STATE_INDEXBUFFER);
1219 GL_EXTCALL(glBindBufferARB(buffer->buffer_type_hint, buffer->buffer_object));
1220
1221 if (gl_info->supported[ARB_MAP_BUFFER_RANGE])
1222 {
1223 GLbitfield mapflags = buffer_gl_map_flags(flags);
1224 buffer->resource.allocatedMemory = GL_EXTCALL(glMapBufferRange(buffer->buffer_type_hint,
1225 0, buffer->resource.size, mapflags));
1226 checkGLcall("glMapBufferRange");
1227 }
1228 else
1229 {
1230 if (buffer->flags & WINED3D_BUFFER_APPLESYNC)
1231 buffer_sync_apple(buffer, flags, gl_info);
1232 buffer->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(buffer->buffer_type_hint,
1233 GL_READ_WRITE_ARB));
1234 checkGLcall("glMapBufferARB");
1235 }
1236
1237 if (((DWORD_PTR)buffer->resource.allocatedMemory) & (RESOURCE_ALIGNMENT - 1))
1238 {
1239 WARN("Pointer %p is not %u byte aligned.\n", buffer->resource.allocatedMemory, RESOURCE_ALIGNMENT);
1240
1241 GL_EXTCALL(glUnmapBufferARB(buffer->buffer_type_hint));
1242 checkGLcall("glUnmapBufferARB");
1243 buffer->resource.allocatedMemory = NULL;
1244
1245 if (buffer->resource.usage & WINED3DUSAGE_DYNAMIC)
1246 {
1247 /* The extra copy is more expensive than not using VBOs at
1248 * all on the Nvidia Linux driver, which is the only driver
1249 * that returns unaligned pointers
1250 */
1251 TRACE("Dynamic buffer, dropping VBO\n");
1252 buffer_unload(&buffer->resource);
1253 buffer->flags &= ~WINED3D_BUFFER_CREATEBO;
1254 if (buffer->resource.bind_count)
1255 device_invalidate_state(device, STATE_STREAMSRC);
1256 }
1257 else
1258 {
1259 TRACE("Falling back to doublebuffered operation\n");
1260 buffer_get_sysmem(buffer, gl_info);
1261 }
1262 TRACE("New pointer is %p.\n", buffer->resource.allocatedMemory);
1263 }
1264 context_release(context);
1265 }
1266 }
1267 else
1268 {
1269 if (dirty)
1270 {
1271 if (buffer->flags & WINED3D_BUFFER_NOSYNC && !(flags & WINED3D_MAP_NOOVERWRITE))
1272 {
1273 buffer->flags &= ~WINED3D_BUFFER_NOSYNC;
1274 }
1275 }
1276 else if(flags & WINED3D_MAP_NOOVERWRITE)
1277 {
1278 buffer->flags |= WINED3D_BUFFER_NOSYNC;
1279 }
1280
1281 if (flags & WINED3D_MAP_DISCARD)
1282 {
1283 buffer->flags |= WINED3D_BUFFER_DISCARD;
1284 }
1285 }
1286 }
1287
1288 *data = buffer->resource.allocatedMemory + offset;
1289
1290 TRACE("Returning memory at %p (base %p, offset %u).\n", *data, buffer->resource.allocatedMemory, offset);
1291 /* TODO: check Flags compatibility with buffer->currentDesc.Usage (see MSDN) */
1292
1293 return WINED3D_OK;
1294}
1295
1296void CDECL wined3d_buffer_unmap(struct wined3d_buffer *buffer)
1297{
1298 ULONG i;
1299
1300 TRACE("buffer %p.\n", buffer);
1301
1302 /* In the case that the number of Unmap calls > the
1303 * number of Map calls, d3d returns always D3D_OK.
1304 * This is also needed to prevent Map from returning garbage on
1305 * the next call (this will happen if the lock_count is < 0). */
1306 if (!buffer->resource.map_count)
1307 {
1308 WARN("Unmap called without a previous map call.\n");
1309 return;
1310 }
1311
1312 if (--buffer->resource.map_count)
1313 {
1314 /* Delay loading the buffer until everything is unlocked */
1315 TRACE("Ignoring unmap.\n");
1316 return;
1317 }
1318
1319 if (!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER) && buffer->buffer_object)
1320 {
1321 struct wined3d_device *device = buffer->resource.device;
1322 const struct wined3d_gl_info *gl_info;
1323 struct wined3d_context *context;
1324
1325 context = context_acquire(device, NULL);
1326 gl_info = context->gl_info;
1327
1328 if (buffer->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
1329 context_invalidate_state(context, STATE_INDEXBUFFER);
1330 GL_EXTCALL(glBindBufferARB(buffer->buffer_type_hint, buffer->buffer_object));
1331
1332 if (gl_info->supported[ARB_MAP_BUFFER_RANGE])
1333 {
1334 for (i = 0; i < buffer->modified_areas; ++i)
1335 {
1336 GL_EXTCALL(glFlushMappedBufferRange(buffer->buffer_type_hint,
1337 buffer->maps[i].offset, buffer->maps[i].size));
1338 checkGLcall("glFlushMappedBufferRange");
1339 }
1340 }
1341 else if (buffer->flags & WINED3D_BUFFER_FLUSH)
1342 {
1343 for (i = 0; i < buffer->modified_areas; ++i)
1344 {
1345 GL_EXTCALL(glFlushMappedBufferRangeAPPLE(buffer->buffer_type_hint,
1346 buffer->maps[i].offset, buffer->maps[i].size));
1347 checkGLcall("glFlushMappedBufferRangeAPPLE");
1348 }
1349 }
1350
1351 GL_EXTCALL(glUnmapBufferARB(buffer->buffer_type_hint));
1352 if (wined3d_settings.strict_draw_ordering)
1353 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
1354 context_release(context);
1355
1356 buffer->resource.allocatedMemory = NULL;
1357 buffer_clear_dirty_areas(buffer);
1358 }
1359 else if (buffer->flags & WINED3D_BUFFER_HASDESC)
1360 {
1361 wined3d_buffer_preload(buffer);
1362 }
1363}
1364
1365static const struct wined3d_resource_ops buffer_resource_ops =
1366{
1367 buffer_unload,
1368};
1369
1370static HRESULT buffer_init(struct wined3d_buffer *buffer, struct wined3d_device *device,
1371 UINT size, DWORD usage, enum wined3d_format_id format_id, enum wined3d_pool pool, GLenum bind_hint,
1372 const char *data, void *parent, const struct wined3d_parent_ops *parent_ops)
1373{
1374 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1375 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
1376 HRESULT hr;
1377 BOOL dynamic_buffer_ok;
1378
1379 if (!size)
1380 {
1381 WARN("Size 0 requested, returning WINED3DERR_INVALIDCALL\n");
1382 return WINED3DERR_INVALIDCALL;
1383 }
1384
1385 hr = resource_init(&buffer->resource, device, WINED3D_RTYPE_BUFFER, format,
1386 WINED3D_MULTISAMPLE_NONE, 0, usage, pool, size, 1, 1, size,
1387 parent, parent_ops, &buffer_resource_ops
1388#ifdef VBOX_WITH_WDDM
1389 , NULL, NULL /* <- no need this info here so far */
1390#endif
1391 );
1392 if (FAILED(hr))
1393 {
1394 WARN("Failed to initialize resource, hr %#x\n", hr);
1395 return hr;
1396 }
1397 buffer->buffer_type_hint = bind_hint;
1398
1399 TRACE("size %#x, usage %#x, format %s, memory @ %p, iface @ %p.\n", buffer->resource.size, buffer->resource.usage,
1400 debug_d3dformat(buffer->resource.format->id), buffer->resource.allocatedMemory, buffer);
1401
1402 if (device->create_parms.flags & WINED3DCREATE_SOFTWARE_VERTEXPROCESSING)
1403 {
1404 /* SWvp always returns the same pointer in buffer maps and retains data in DISCARD maps.
1405 * Keep a system memory copy of the buffer to provide the same behavior to the application.
1406 * Still use a VBO to support OpenGL 3 core contexts. */
1407 TRACE("Using doublebuffer mode because of software vertex processing\n");
1408 buffer->flags |= WINED3D_BUFFER_DOUBLEBUFFER;
1409 }
1410
1411 dynamic_buffer_ok = gl_info->supported[APPLE_FLUSH_BUFFER_RANGE] || gl_info->supported[ARB_MAP_BUFFER_RANGE];
1412
1413 /* Observations show that drawStridedSlow is faster on dynamic VBs than converting +
1414 * drawStridedFast (half-life 2 and others).
1415 *
1416 * Basically converting the vertices in the buffer is quite expensive, and observations
1417 * show that drawStridedSlow is faster than converting + uploading + drawStridedFast.
1418 * Therefore do not create a VBO for WINED3DUSAGE_DYNAMIC buffers.
1419 */
1420 if (!gl_info->supported[ARB_VERTEX_BUFFER_OBJECT])
1421 {
1422 TRACE("Not creating a vbo because GL_ARB_vertex_buffer is not supported\n");
1423 }
1424#ifndef VBOX_WITH_WDDM /* @todo: extend this to VBOX in general */
1425 else if(buffer->resource.pool == WINED3D_POOL_SYSTEM_MEM)
1426 {
1427 TRACE("Not creating a vbo because the vertex buffer is in system memory\n");
1428 }
1429 else if(!dynamic_buffer_ok && (buffer->resource.usage & WINED3DUSAGE_DYNAMIC))
1430 {
1431 TRACE("Not creating a vbo because the buffer has dynamic usage and no GL support\n");
1432 }
1433#endif
1434 else
1435 {
1436 buffer->flags |= WINED3D_BUFFER_CREATEBO;
1437 }
1438
1439 if (data)
1440 {
1441 BYTE *ptr;
1442
1443 hr = wined3d_buffer_map(buffer, 0, size, &ptr, 0);
1444 if (FAILED(hr))
1445 {
1446 ERR("Failed to map buffer, hr %#x\n", hr);
1447 buffer_unload(&buffer->resource);
1448 resource_cleanup(&buffer->resource);
1449 return hr;
1450 }
1451
1452 memcpy(ptr, data, size);
1453
1454 wined3d_buffer_unmap(buffer);
1455 }
1456
1457 buffer->maps = HeapAlloc(GetProcessHeap(), 0, sizeof(*buffer->maps));
1458 if (!buffer->maps)
1459 {
1460 ERR("Out of memory\n");
1461 buffer_unload(&buffer->resource);
1462 resource_cleanup(&buffer->resource);
1463 return E_OUTOFMEMORY;
1464 }
1465 buffer->maps_size = 1;
1466
1467 return WINED3D_OK;
1468}
1469
1470HRESULT CDECL wined3d_buffer_create(struct wined3d_device *device, struct wined3d_buffer_desc *desc, const void *data,
1471 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer)
1472{
1473 struct wined3d_buffer *object;
1474 HRESULT hr;
1475
1476 TRACE("device %p, desc %p, data %p, parent %p, buffer %p\n", device, desc, data, parent, buffer);
1477
1478 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1479 if (!object)
1480 return E_OUTOFMEMORY;
1481
1482 FIXME("Ignoring access flags (pool)\n");
1483
1484 hr = buffer_init(object, device, desc->byte_width, desc->usage, WINED3DFMT_UNKNOWN,
1485 WINED3D_POOL_MANAGED, GL_ARRAY_BUFFER_ARB, data, parent, parent_ops);
1486 if (FAILED(hr))
1487 {
1488 WARN("Failed to initialize buffer, hr %#x.\n", hr);
1489 HeapFree(GetProcessHeap(), 0, object);
1490 return hr;
1491 }
1492 object->desc = *desc;
1493
1494 TRACE("Created buffer %p.\n", object);
1495
1496 *buffer = object;
1497
1498 return WINED3D_OK;
1499}
1500
1501HRESULT CDECL wined3d_buffer_create_vb(struct wined3d_device *device, UINT size, DWORD usage, enum wined3d_pool pool,
1502 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer)
1503{
1504 struct wined3d_buffer *object;
1505 HRESULT hr;
1506
1507 TRACE("device %p, size %u, usage %#x, pool %#x, parent %p, parent_ops %p, buffer %p.\n",
1508 device, size, usage, pool, parent, parent_ops, buffer);
1509
1510 if (pool == WINED3D_POOL_SCRATCH)
1511 {
1512 /* The d3d9 tests shows that this is not allowed. It doesn't make much
1513 * sense anyway, SCRATCH buffers wouldn't be usable anywhere. */
1514 WARN("Vertex buffer in WINED3D_POOL_SCRATCH requested, returning WINED3DERR_INVALIDCALL.\n");
1515 *buffer = NULL;
1516 return WINED3DERR_INVALIDCALL;
1517 }
1518
1519 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1520 if (!object)
1521 {
1522 *buffer = NULL;
1523 return WINED3DERR_OUTOFVIDEOMEMORY;
1524 }
1525
1526 hr = buffer_init(object, device, size, usage, WINED3DFMT_VERTEXDATA,
1527 pool, GL_ARRAY_BUFFER_ARB, NULL, parent, parent_ops);
1528 if (FAILED(hr))
1529 {
1530 WARN("Failed to initialize buffer, hr %#x.\n", hr);
1531 HeapFree(GetProcessHeap(), 0, object);
1532 return hr;
1533 }
1534
1535 TRACE("Created buffer %p.\n", object);
1536 *buffer = object;
1537
1538 return WINED3D_OK;
1539}
1540
1541HRESULT CDECL wined3d_buffer_create_ib(struct wined3d_device *device, UINT size, DWORD usage, enum wined3d_pool pool,
1542 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer)
1543{
1544 struct wined3d_buffer *object;
1545 HRESULT hr;
1546
1547 TRACE("device %p, size %u, usage %#x, pool %#x, parent %p, parent_ops %p, buffer %p.\n",
1548 device, size, usage, pool, parent, parent_ops, buffer);
1549
1550 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1551 if (!object)
1552 {
1553 *buffer = NULL;
1554 return WINED3DERR_OUTOFVIDEOMEMORY;
1555 }
1556
1557 hr = buffer_init(object, device, size, usage | WINED3DUSAGE_STATICDECL,
1558 WINED3DFMT_UNKNOWN, pool, GL_ELEMENT_ARRAY_BUFFER_ARB, NULL,
1559 parent, parent_ops);
1560 if (FAILED(hr))
1561 {
1562 WARN("Failed to initialize buffer, hr %#x\n", hr);
1563 HeapFree(GetProcessHeap(), 0, object);
1564 return hr;
1565 }
1566
1567 TRACE("Created buffer %p.\n", object);
1568 *buffer = object;
1569
1570 return WINED3D_OK;
1571}
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