VirtualBox

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

最後變更 在這個檔案從48345是 48345,由 vboxsync 提交於 12 年 前

header fixes

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 53.9 KB
 
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
662 TRACE("%p decreasing refcount to %u.\n", buffer, refcount);
663
664 if (!refcount)
665 {
666 buffer_unload(&buffer->resource);
667 resource_cleanup(&buffer->resource);
668 buffer->resource.parent_ops->wined3d_object_destroyed(buffer->resource.parent);
669 HeapFree(GetProcessHeap(), 0, buffer->maps);
670 HeapFree(GetProcessHeap(), 0, buffer);
671 }
672
673 return refcount;
674}
675
676void * CDECL wined3d_buffer_get_parent(const struct wined3d_buffer *buffer)
677{
678 TRACE("buffer %p.\n", buffer);
679
680 return buffer->resource.parent;
681}
682
683DWORD CDECL wined3d_buffer_set_priority(struct wined3d_buffer *buffer, DWORD priority)
684{
685 return resource_set_priority(&buffer->resource, priority);
686}
687
688DWORD CDECL wined3d_buffer_get_priority(const struct wined3d_buffer *buffer)
689{
690 return resource_get_priority(&buffer->resource);
691}
692
693/* The caller provides a context and binds the buffer */
694static void buffer_sync_apple(struct wined3d_buffer *This, DWORD flags, const struct wined3d_gl_info *gl_info)
695{
696 enum wined3d_event_query_result ret;
697
698 /* No fencing needs to be done if the app promises not to overwrite
699 * existing data. */
700 if (flags & WINED3D_MAP_NOOVERWRITE)
701 return;
702
703 if (flags & WINED3D_MAP_DISCARD)
704 {
705 GL_EXTCALL(glBufferDataARB(This->buffer_type_hint, This->resource.size, NULL, This->buffer_object_usage));
706 checkGLcall("glBufferDataARB\n");
707 return;
708 }
709
710 if(!This->query)
711 {
712 TRACE("Creating event query for buffer %p\n", This);
713
714 if (!wined3d_event_query_supported(gl_info))
715 {
716 FIXME("Event queries not supported, dropping async buffer locks.\n");
717 goto drop_query;
718 }
719
720 This->query = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This->query));
721 if (!This->query)
722 {
723 ERR("Failed to allocate event query memory, dropping async buffer locks.\n");
724 goto drop_query;
725 }
726
727 /* Since we don't know about old draws a glFinish is needed once */
728 gl_info->gl_ops.gl.p_glFinish();
729 return;
730 }
731 TRACE("Synchronizing buffer %p\n", This);
732 ret = wined3d_event_query_finish(This->query, This->resource.device);
733 switch(ret)
734 {
735 case WINED3D_EVENT_QUERY_NOT_STARTED:
736 case WINED3D_EVENT_QUERY_OK:
737 /* All done */
738 return;
739
740 case WINED3D_EVENT_QUERY_WRONG_THREAD:
741 WARN("Cannot synchronize buffer lock due to a thread conflict\n");
742 goto drop_query;
743
744 default:
745 ERR("wined3d_event_query_finish returned %u, dropping async buffer locks\n", ret);
746 goto drop_query;
747 }
748
749drop_query:
750 if(This->query)
751 {
752 wined3d_event_query_destroy(This->query);
753 This->query = NULL;
754 }
755
756 gl_info->gl_ops.gl.p_glFinish();
757 GL_EXTCALL(glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_TRUE));
758 checkGLcall("glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_TRUE)");
759 This->flags &= ~WINED3D_BUFFER_APPLESYNC;
760}
761
762/* The caller provides a GL context */
763static void buffer_direct_upload(struct wined3d_buffer *This, const struct wined3d_gl_info *gl_info, DWORD flags)
764{
765 BYTE *map;
766 UINT start = 0, len = 0;
767
768 /* This potentially invalidates the element array buffer binding, but the
769 * caller always takes care of this. */
770 GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
771 checkGLcall("glBindBufferARB");
772 if (gl_info->supported[ARB_MAP_BUFFER_RANGE])
773 {
774 GLbitfield mapflags;
775 mapflags = GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
776 if (flags & WINED3D_BUFFER_DISCARD)
777 mapflags |= GL_MAP_INVALIDATE_BUFFER_BIT;
778 if (flags & WINED3D_BUFFER_NOSYNC)
779 mapflags |= GL_MAP_UNSYNCHRONIZED_BIT;
780 map = GL_EXTCALL(glMapBufferRange(This->buffer_type_hint, 0,
781 This->resource.size, mapflags));
782 checkGLcall("glMapBufferRange");
783 }
784 else
785 {
786 if (This->flags & WINED3D_BUFFER_APPLESYNC)
787 {
788 DWORD syncflags = 0;
789 if (flags & WINED3D_BUFFER_DISCARD)
790 syncflags |= WINED3D_MAP_DISCARD;
791 if (flags & WINED3D_BUFFER_NOSYNC)
792 syncflags |= WINED3D_MAP_NOOVERWRITE;
793 buffer_sync_apple(This, syncflags, gl_info);
794 }
795 map = GL_EXTCALL(glMapBufferARB(This->buffer_type_hint, GL_WRITE_ONLY_ARB));
796 checkGLcall("glMapBufferARB");
797 }
798 if (!map)
799 {
800 ERR("Failed to map opengl buffer\n");
801 return;
802 }
803
804 while (This->modified_areas)
805 {
806 This->modified_areas--;
807 start = This->maps[This->modified_areas].offset;
808 len = This->maps[This->modified_areas].size;
809
810 memcpy(map + start, This->resource.allocatedMemory + start, len);
811
812 if (gl_info->supported[ARB_MAP_BUFFER_RANGE])
813 {
814 GL_EXTCALL(glFlushMappedBufferRange(This->buffer_type_hint, start, len));
815 checkGLcall("glFlushMappedBufferRange");
816 }
817 else if (This->flags & WINED3D_BUFFER_FLUSH)
818 {
819 GL_EXTCALL(glFlushMappedBufferRangeAPPLE(This->buffer_type_hint, start, len));
820 checkGLcall("glFlushMappedBufferRangeAPPLE");
821 }
822 }
823 GL_EXTCALL(glUnmapBufferARB(This->buffer_type_hint));
824 checkGLcall("glUnmapBufferARB");
825}
826
827#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
828static UINT wined3d_buffer_stream_offset_left(UINT x, UINT offset, UINT stride)
829{
830 UINT off = ((UINT)(RT_ABS((INT)(x - offset))) % stride);
831 if (!off)
832 return x;
833 if (x > offset)
834 return x - off;
835
836 /* x < offset */
837 return x - stride + off;
838}
839
840static UINT wined3d_buffer_stream_offset_right(UINT x, UINT offset, UINT stride)
841{
842 UINT left = wined3d_buffer_stream_offset_left(x, offset, stride);
843
844 if (left == x)
845 return x;
846 return left + stride;
847}
848#endif
849
850/* Do not call while under the GL lock. */
851void CDECL wined3d_buffer_preload(struct wined3d_buffer *buffer)
852{
853 DWORD flags = buffer->flags & (WINED3D_BUFFER_NOSYNC | WINED3D_BUFFER_DISCARD);
854 struct wined3d_device *device = buffer->resource.device;
855 UINT start = 0, end = 0, len = 0, vertices;
856 const struct wined3d_gl_info *gl_info;
857 struct wined3d_context *context;
858 BOOL decl_changed = FALSE;
859 unsigned int i, j;
860 BYTE *data;
861#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
862 BYTE *cur_ptr;
863 UINT conv_start, conv_end;
864#endif
865
866 TRACE("buffer %p.\n", buffer);
867
868 if (buffer->resource.map_count)
869 {
870 WARN("Buffer is mapped, skipping preload.\n");
871 return;
872 }
873
874 buffer->flags &= ~(WINED3D_BUFFER_NOSYNC | WINED3D_BUFFER_DISCARD);
875
876 if (!buffer->buffer_object)
877 {
878 /* TODO: Make converting independent from VBOs */
879 if (buffer->flags & WINED3D_BUFFER_CREATEBO)
880 {
881 context = context_acquire(device, NULL);
882 buffer_create_buffer_object(buffer, context->gl_info);
883 context_release(context);
884 buffer->flags &= ~WINED3D_BUFFER_CREATEBO;
885 }
886 else
887 {
888 /* Not doing any conversion */
889 return;
890 }
891 }
892
893 /* Reading the declaration makes only sense if the stateblock is finalized and the buffer bound to a stream */
894 if (device->isInDraw && buffer->resource.bind_count > 0)
895 {
896 decl_changed = buffer_find_decl(buffer);
897 buffer->flags |= WINED3D_BUFFER_HASDESC;
898 }
899
900 if (!decl_changed && !(buffer->flags & WINED3D_BUFFER_HASDESC && buffer_is_dirty(buffer)))
901 {
902 ++buffer->draw_count;
903 if (buffer->draw_count > VB_RESETDECLCHANGE)
904 buffer->decl_change_count = 0;
905 if (buffer->draw_count > VB_RESETFULLCONVS)
906 buffer->full_conversion_count = 0;
907 return;
908 }
909
910 /* If applications change the declaration over and over, reconverting all the time is a huge
911 * performance hit. So count the declaration changes and release the VBO if there are too many
912 * of them (and thus stop converting)
913 */
914 if (decl_changed)
915 {
916 ++buffer->decl_change_count;
917 buffer->draw_count = 0;
918
919 if (buffer->decl_change_count > VB_MAXDECLCHANGES
920 || (buffer->conversion_map && (buffer->resource.usage & WINED3DUSAGE_DYNAMIC)))
921 {
922 FIXME("Too many declaration changes or converting dynamic buffer, stopping converting\n");
923
924 buffer_unload(&buffer->resource);
925 buffer->flags &= ~WINED3D_BUFFER_CREATEBO;
926
927 /* The stream source state handler might have read the memory of
928 * the vertex buffer already and got the memory in the vbo which
929 * is not valid any longer. Dirtify the stream source to force a
930 * reload. This happens only once per changed vertexbuffer and
931 * should occur rather rarely. */
932 device_invalidate_state(device, STATE_STREAMSRC);
933 return;
934 }
935
936 /* The declaration changed, reload the whole buffer */
937 WARN("Reloading buffer because of decl change\n");
938 buffer_clear_dirty_areas(buffer);
939 if (!buffer_add_dirty_area(buffer, 0, 0))
940 {
941 ERR("buffer_add_dirty_area failed, this is not expected\n");
942 return;
943 }
944 /* Avoid unfenced updates, we might overwrite more areas of the buffer than the application
945 * cleared for unsynchronized updates
946 */
947 flags = 0;
948 }
949 else
950 {
951 /* However, it is perfectly fine to change the declaration every now and then. We don't want a game that
952 * changes it every minute drop the VBO after VB_MAX_DECL_CHANGES minutes. So count draws without
953 * decl changes and reset the decl change count after a specific number of them
954 */
955 if (buffer->conversion_map && buffer_is_fully_dirty(buffer))
956 {
957 ++buffer->full_conversion_count;
958 if (buffer->full_conversion_count > VB_MAXFULLCONVERSIONS)
959 {
960 FIXME("Too many full buffer conversions, stopping converting.\n");
961 buffer_unload(&buffer->resource);
962 buffer->flags &= ~WINED3D_BUFFER_CREATEBO;
963 if (buffer->resource.bind_count)
964 device_invalidate_state(device, STATE_STREAMSRC);
965 return;
966 }
967 }
968 else
969 {
970 ++buffer->draw_count;
971 if (buffer->draw_count > VB_RESETDECLCHANGE)
972 buffer->decl_change_count = 0;
973 if (buffer->draw_count > VB_RESETFULLCONVS)
974 buffer->full_conversion_count = 0;
975 }
976 }
977
978 if (buffer->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
979 device_invalidate_state(device, STATE_INDEXBUFFER);
980
981 if (!buffer->conversion_map)
982 {
983 /* That means that there is nothing to fixup. Just upload from
984 * buffer->resource.allocatedMemory directly into the vbo. Do not
985 * free the system memory copy because drawPrimitive may need it if
986 * the stride is 0, for instancing emulation, vertex blending
987 * emulation or shader emulation. */
988 TRACE("No conversion needed.\n");
989
990 /* Nothing to do because we locked directly into the vbo */
991 if (!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER))
992 {
993 return;
994 }
995
996 context = context_acquire(device, NULL);
997 buffer_direct_upload(buffer, context->gl_info, flags);
998
999 context_release(context);
1000 return;
1001 }
1002
1003 context = context_acquire(device, NULL);
1004 gl_info = context->gl_info;
1005
1006 if(!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER))
1007 {
1008 buffer_get_sysmem(buffer, gl_info);
1009 }
1010
1011 /* Now for each vertex in the buffer that needs conversion */
1012 vertices = buffer->resource.size / buffer->stride;
1013
1014 data = HeapAlloc(GetProcessHeap(), 0, buffer->resource.size);
1015
1016 while(buffer->modified_areas)
1017 {
1018 buffer->modified_areas--;
1019 start = buffer->maps[buffer->modified_areas].offset;
1020 len = buffer->maps[buffer->modified_areas].size;
1021 end = start + len;
1022
1023#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
1024 if (buffer->offset > UINT32_MAX)
1025 {
1026 ERR("negative offset, unexpected!");
1027 }
1028
1029 conv_start = wined3d_buffer_stream_offset_left(start, buffer->offset, buffer->stride);
1030 if (conv_start <= start)
1031 {
1032 start = conv_start;
1033 }
1034 else
1035 {
1036 /* <- < 0, add stride */
1037 conv_start += buffer->stride;
1038 }
1039
1040 Assert(conv_start < UINT32_MAX/3);
1041
1042 conv_end = wined3d_buffer_stream_offset_right(end, buffer->offset, buffer->stride);
1043 if (conv_end > end)
1044 {
1045 if (conv_end <= buffer->resource.size)
1046 end = conv_end;
1047 else
1048 conv_end -= buffer->stride;
1049 }
1050 else if (conv_end < end)
1051 {
1052 ERR("unexpected!");
1053 }
1054 else
1055 {
1056 end = conv_end;
1057 }
1058#endif
1059 memcpy(data + start, buffer->resource.allocatedMemory + start, end - start);
1060
1061#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
1062 for (cur_ptr = data + conv_start; cur_ptr < data + conv_end; cur_ptr += buffer->stride)
1063#else
1064 for (i = start / buffer->stride; i < min((end / buffer->stride) + 1, vertices); ++i)
1065#endif
1066 {
1067 for (j = 0; j < buffer->stride; ++j)
1068 {
1069 switch (buffer->conversion_map[j])
1070 {
1071 case CONV_NONE:
1072 /* Done already */
1073 j += 3;
1074 break;
1075 case CONV_D3DCOLOR:
1076#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
1077 fixup_d3dcolor((DWORD *) (cur_ptr + j));
1078#else
1079 fixup_d3dcolor((DWORD *) (data + i * buffer->stride + j));
1080#endif
1081 j += 3;
1082 break;
1083
1084 case CONV_POSITIONT:
1085#ifdef VBOX_WITH_WINE_FIX_BUFOFFSET
1086 fixup_transformed_pos((float *) (cur_ptr + j));
1087#else
1088 fixup_transformed_pos((float *) (data + i * buffer->stride + j));
1089#endif
1090 j += 15;
1091 break;
1092 default:
1093 FIXME("Unimplemented conversion %d in shifted conversion\n", buffer->conversion_map[j]);
1094 }
1095 }
1096 }
1097
1098 GL_EXTCALL(glBindBufferARB(buffer->buffer_type_hint, buffer->buffer_object));
1099 checkGLcall("glBindBufferARB");
1100 GL_EXTCALL(glBufferSubDataARB(buffer->buffer_type_hint, start, len, data + start));
1101 checkGLcall("glBufferSubDataARB");
1102 }
1103
1104 HeapFree(GetProcessHeap(), 0, data);
1105 context_release(context);
1106}
1107
1108static DWORD buffer_sanitize_flags(const struct wined3d_buffer *buffer, DWORD flags)
1109{
1110 /* Not all flags make sense together, but Windows never returns an error.
1111 * Catch the cases that could cause issues. */
1112 if (flags & WINED3D_MAP_READONLY)
1113 {
1114 if (flags & WINED3D_MAP_DISCARD)
1115 {
1116 WARN("WINED3D_MAP_READONLY combined with WINED3D_MAP_DISCARD, ignoring flags.\n");
1117 return 0;
1118 }
1119 if (flags & WINED3D_MAP_NOOVERWRITE)
1120 {
1121 WARN("WINED3D_MAP_READONLY combined with WINED3D_MAP_NOOVERWRITE, ignoring flags.\n");
1122 return 0;
1123 }
1124 }
1125 else if ((flags & (WINED3D_MAP_DISCARD | WINED3D_MAP_NOOVERWRITE))
1126 == (WINED3D_MAP_DISCARD | WINED3D_MAP_NOOVERWRITE))
1127 {
1128 WARN("WINED3D_MAP_DISCARD and WINED3D_MAP_NOOVERWRITE used together, ignoring.\n");
1129 return 0;
1130 }
1131 else if (flags & (WINED3D_MAP_DISCARD | WINED3D_MAP_NOOVERWRITE)
1132 && !(buffer->resource.usage & WINED3DUSAGE_DYNAMIC))
1133 {
1134 WARN("DISCARD or NOOVERWRITE map on non-dynamic buffer, ignoring.\n");
1135 return 0;
1136 }
1137
1138 return flags;
1139}
1140
1141static GLbitfield buffer_gl_map_flags(DWORD d3d_flags)
1142{
1143 GLbitfield ret = 0;
1144
1145 if (!(d3d_flags & WINED3D_MAP_READONLY))
1146 ret |= GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
1147 if (!(d3d_flags & (WINED3D_MAP_DISCARD | WINED3D_MAP_NOOVERWRITE)))
1148 ret |= GL_MAP_READ_BIT;
1149
1150 if (d3d_flags & WINED3D_MAP_DISCARD)
1151 ret |= GL_MAP_INVALIDATE_BUFFER_BIT;
1152 if (d3d_flags & WINED3D_MAP_NOOVERWRITE)
1153 ret |= GL_MAP_UNSYNCHRONIZED_BIT;
1154
1155 return ret;
1156}
1157
1158struct wined3d_resource * CDECL wined3d_buffer_get_resource(struct wined3d_buffer *buffer)
1159{
1160 TRACE("buffer %p.\n", buffer);
1161
1162 return &buffer->resource;
1163}
1164
1165HRESULT CDECL wined3d_buffer_map(struct wined3d_buffer *buffer, UINT offset, UINT size, BYTE **data, DWORD flags)
1166{
1167 BOOL dirty = buffer_is_dirty(buffer);
1168 LONG count;
1169
1170 TRACE("buffer %p, offset %u, size %u, data %p, flags %#x\n", buffer, offset, size, data, flags);
1171
1172 flags = buffer_sanitize_flags(buffer, flags);
1173 if (flags & WINED3D_MAP_DISCARD)
1174 {
1175 /* DISCARD invalidates the entire buffer, regardless of the specified
1176 * offset and size. Some applications also depend on the entire buffer
1177 * being uploaded in that case. Two such applications are Port Royale
1178 * and Darkstar One. */
1179 if (!buffer_add_dirty_area(buffer, 0, 0))
1180 return E_OUTOFMEMORY;
1181 }
1182 else if (!(flags & WINED3D_MAP_READONLY))
1183 {
1184 if (!buffer_add_dirty_area(buffer, offset, size))
1185 return E_OUTOFMEMORY;
1186 }
1187
1188 count = ++buffer->resource.map_count;
1189
1190 if (buffer->buffer_object)
1191 {
1192 if (!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER))
1193 {
1194 if (count == 1)
1195 {
1196 struct wined3d_device *device = buffer->resource.device;
1197 struct wined3d_context *context;
1198 const struct wined3d_gl_info *gl_info;
1199
1200 context = context_acquire(device, NULL);
1201 gl_info = context->gl_info;
1202
1203 if (buffer->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
1204 context_invalidate_state(context, STATE_INDEXBUFFER);
1205 GL_EXTCALL(glBindBufferARB(buffer->buffer_type_hint, buffer->buffer_object));
1206
1207 if (gl_info->supported[ARB_MAP_BUFFER_RANGE])
1208 {
1209 GLbitfield mapflags = buffer_gl_map_flags(flags);
1210 buffer->resource.allocatedMemory = GL_EXTCALL(glMapBufferRange(buffer->buffer_type_hint,
1211 0, buffer->resource.size, mapflags));
1212 checkGLcall("glMapBufferRange");
1213 }
1214 else
1215 {
1216 if (buffer->flags & WINED3D_BUFFER_APPLESYNC)
1217 buffer_sync_apple(buffer, flags, gl_info);
1218 buffer->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(buffer->buffer_type_hint,
1219 GL_READ_WRITE_ARB));
1220 checkGLcall("glMapBufferARB");
1221 }
1222
1223 if (((DWORD_PTR)buffer->resource.allocatedMemory) & (RESOURCE_ALIGNMENT - 1))
1224 {
1225 WARN("Pointer %p is not %u byte aligned.\n", buffer->resource.allocatedMemory, RESOURCE_ALIGNMENT);
1226
1227 GL_EXTCALL(glUnmapBufferARB(buffer->buffer_type_hint));
1228 checkGLcall("glUnmapBufferARB");
1229 buffer->resource.allocatedMemory = NULL;
1230
1231 if (buffer->resource.usage & WINED3DUSAGE_DYNAMIC)
1232 {
1233 /* The extra copy is more expensive than not using VBOs at
1234 * all on the Nvidia Linux driver, which is the only driver
1235 * that returns unaligned pointers
1236 */
1237 TRACE("Dynamic buffer, dropping VBO\n");
1238 buffer_unload(&buffer->resource);
1239 buffer->flags &= ~WINED3D_BUFFER_CREATEBO;
1240 if (buffer->resource.bind_count)
1241 device_invalidate_state(device, STATE_STREAMSRC);
1242 }
1243 else
1244 {
1245 TRACE("Falling back to doublebuffered operation\n");
1246 buffer_get_sysmem(buffer, gl_info);
1247 }
1248 TRACE("New pointer is %p.\n", buffer->resource.allocatedMemory);
1249 }
1250 context_release(context);
1251 }
1252 }
1253 else
1254 {
1255 if (dirty)
1256 {
1257 if (buffer->flags & WINED3D_BUFFER_NOSYNC && !(flags & WINED3D_MAP_NOOVERWRITE))
1258 {
1259 buffer->flags &= ~WINED3D_BUFFER_NOSYNC;
1260 }
1261 }
1262 else if(flags & WINED3D_MAP_NOOVERWRITE)
1263 {
1264 buffer->flags |= WINED3D_BUFFER_NOSYNC;
1265 }
1266
1267 if (flags & WINED3D_MAP_DISCARD)
1268 {
1269 buffer->flags |= WINED3D_BUFFER_DISCARD;
1270 }
1271 }
1272 }
1273
1274 *data = buffer->resource.allocatedMemory + offset;
1275
1276 TRACE("Returning memory at %p (base %p, offset %u).\n", *data, buffer->resource.allocatedMemory, offset);
1277 /* TODO: check Flags compatibility with buffer->currentDesc.Usage (see MSDN) */
1278
1279 return WINED3D_OK;
1280}
1281
1282void CDECL wined3d_buffer_unmap(struct wined3d_buffer *buffer)
1283{
1284 ULONG i;
1285
1286 TRACE("buffer %p.\n", buffer);
1287
1288 /* In the case that the number of Unmap calls > the
1289 * number of Map calls, d3d returns always D3D_OK.
1290 * This is also needed to prevent Map from returning garbage on
1291 * the next call (this will happen if the lock_count is < 0). */
1292 if (!buffer->resource.map_count)
1293 {
1294 WARN("Unmap called without a previous map call.\n");
1295 return;
1296 }
1297
1298 if (--buffer->resource.map_count)
1299 {
1300 /* Delay loading the buffer until everything is unlocked */
1301 TRACE("Ignoring unmap.\n");
1302 return;
1303 }
1304
1305 if (!(buffer->flags & WINED3D_BUFFER_DOUBLEBUFFER) && buffer->buffer_object)
1306 {
1307 struct wined3d_device *device = buffer->resource.device;
1308 const struct wined3d_gl_info *gl_info;
1309 struct wined3d_context *context;
1310
1311 context = context_acquire(device, NULL);
1312 gl_info = context->gl_info;
1313
1314 if (buffer->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
1315 context_invalidate_state(context, STATE_INDEXBUFFER);
1316 GL_EXTCALL(glBindBufferARB(buffer->buffer_type_hint, buffer->buffer_object));
1317
1318 if (gl_info->supported[ARB_MAP_BUFFER_RANGE])
1319 {
1320 for (i = 0; i < buffer->modified_areas; ++i)
1321 {
1322 GL_EXTCALL(glFlushMappedBufferRange(buffer->buffer_type_hint,
1323 buffer->maps[i].offset, buffer->maps[i].size));
1324 checkGLcall("glFlushMappedBufferRange");
1325 }
1326 }
1327 else if (buffer->flags & WINED3D_BUFFER_FLUSH)
1328 {
1329 for (i = 0; i < buffer->modified_areas; ++i)
1330 {
1331 GL_EXTCALL(glFlushMappedBufferRangeAPPLE(buffer->buffer_type_hint,
1332 buffer->maps[i].offset, buffer->maps[i].size));
1333 checkGLcall("glFlushMappedBufferRangeAPPLE");
1334 }
1335 }
1336
1337 GL_EXTCALL(glUnmapBufferARB(buffer->buffer_type_hint));
1338 if (wined3d_settings.strict_draw_ordering)
1339 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
1340 context_release(context);
1341
1342 buffer->resource.allocatedMemory = NULL;
1343 buffer_clear_dirty_areas(buffer);
1344 }
1345 else if (buffer->flags & WINED3D_BUFFER_HASDESC)
1346 {
1347 wined3d_buffer_preload(buffer);
1348 }
1349}
1350
1351static const struct wined3d_resource_ops buffer_resource_ops =
1352{
1353 buffer_unload,
1354};
1355
1356static HRESULT buffer_init(struct wined3d_buffer *buffer, struct wined3d_device *device,
1357 UINT size, DWORD usage, enum wined3d_format_id format_id, enum wined3d_pool pool, GLenum bind_hint,
1358 const char *data, void *parent, const struct wined3d_parent_ops *parent_ops)
1359{
1360 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1361 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
1362 HRESULT hr;
1363 BOOL dynamic_buffer_ok;
1364
1365 if (!size)
1366 {
1367 WARN("Size 0 requested, returning WINED3DERR_INVALIDCALL\n");
1368 return WINED3DERR_INVALIDCALL;
1369 }
1370
1371 hr = resource_init(&buffer->resource, device, WINED3D_RTYPE_BUFFER, format,
1372 WINED3D_MULTISAMPLE_NONE, 0, usage, pool, size, 1, 1, size,
1373 parent, parent_ops, &buffer_resource_ops
1374#ifdef VBOX_WITH_WDDM
1375 , NULL, NULL /* <- no need this info here so far */
1376#endif
1377 );
1378 if (FAILED(hr))
1379 {
1380 WARN("Failed to initialize resource, hr %#x\n", hr);
1381 return hr;
1382 }
1383 buffer->buffer_type_hint = bind_hint;
1384
1385 TRACE("size %#x, usage %#x, format %s, memory @ %p, iface @ %p.\n", buffer->resource.size, buffer->resource.usage,
1386 debug_d3dformat(buffer->resource.format->id), buffer->resource.allocatedMemory, buffer);
1387
1388 if (device->create_parms.flags & WINED3DCREATE_SOFTWARE_VERTEXPROCESSING)
1389 {
1390 /* SWvp always returns the same pointer in buffer maps and retains data in DISCARD maps.
1391 * Keep a system memory copy of the buffer to provide the same behavior to the application.
1392 * Still use a VBO to support OpenGL 3 core contexts. */
1393 TRACE("Using doublebuffer mode because of software vertex processing\n");
1394 buffer->flags |= WINED3D_BUFFER_DOUBLEBUFFER;
1395 }
1396
1397 dynamic_buffer_ok = gl_info->supported[APPLE_FLUSH_BUFFER_RANGE] || gl_info->supported[ARB_MAP_BUFFER_RANGE];
1398
1399 /* Observations show that drawStridedSlow is faster on dynamic VBs than converting +
1400 * drawStridedFast (half-life 2 and others).
1401 *
1402 * Basically converting the vertices in the buffer is quite expensive, and observations
1403 * show that drawStridedSlow is faster than converting + uploading + drawStridedFast.
1404 * Therefore do not create a VBO for WINED3DUSAGE_DYNAMIC buffers.
1405 */
1406 if (!gl_info->supported[ARB_VERTEX_BUFFER_OBJECT])
1407 {
1408 TRACE("Not creating a vbo because GL_ARB_vertex_buffer is not supported\n");
1409 }
1410#ifndef VBOX_WITH_WDDM /* @todo: extend this to VBOX in general */
1411 else if(buffer->resource.pool == WINED3D_POOL_SYSTEM_MEM)
1412 {
1413 TRACE("Not creating a vbo because the vertex buffer is in system memory\n");
1414 }
1415 else if(!dynamic_buffer_ok && (buffer->resource.usage & WINED3DUSAGE_DYNAMIC))
1416 {
1417 TRACE("Not creating a vbo because the buffer has dynamic usage and no GL support\n");
1418 }
1419#endif
1420 else
1421 {
1422 buffer->flags |= WINED3D_BUFFER_CREATEBO;
1423 }
1424
1425 if (data)
1426 {
1427 BYTE *ptr;
1428
1429 hr = wined3d_buffer_map(buffer, 0, size, &ptr, 0);
1430 if (FAILED(hr))
1431 {
1432 ERR("Failed to map buffer, hr %#x\n", hr);
1433 buffer_unload(&buffer->resource);
1434 resource_cleanup(&buffer->resource);
1435 return hr;
1436 }
1437
1438 memcpy(ptr, data, size);
1439
1440 wined3d_buffer_unmap(buffer);
1441 }
1442
1443 buffer->maps = HeapAlloc(GetProcessHeap(), 0, sizeof(*buffer->maps));
1444 if (!buffer->maps)
1445 {
1446 ERR("Out of memory\n");
1447 buffer_unload(&buffer->resource);
1448 resource_cleanup(&buffer->resource);
1449 return E_OUTOFMEMORY;
1450 }
1451 buffer->maps_size = 1;
1452
1453 return WINED3D_OK;
1454}
1455
1456HRESULT CDECL wined3d_buffer_create(struct wined3d_device *device, struct wined3d_buffer_desc *desc, const void *data,
1457 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer)
1458{
1459 struct wined3d_buffer *object;
1460 HRESULT hr;
1461
1462 TRACE("device %p, desc %p, data %p, parent %p, buffer %p\n", device, desc, data, parent, buffer);
1463
1464 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1465 if (!object)
1466 return E_OUTOFMEMORY;
1467
1468 FIXME("Ignoring access flags (pool)\n");
1469
1470 hr = buffer_init(object, device, desc->byte_width, desc->usage, WINED3DFMT_UNKNOWN,
1471 WINED3D_POOL_MANAGED, GL_ARRAY_BUFFER_ARB, data, parent, parent_ops);
1472 if (FAILED(hr))
1473 {
1474 WARN("Failed to initialize buffer, hr %#x.\n", hr);
1475 HeapFree(GetProcessHeap(), 0, object);
1476 return hr;
1477 }
1478 object->desc = *desc;
1479
1480 TRACE("Created buffer %p.\n", object);
1481
1482 *buffer = object;
1483
1484 return WINED3D_OK;
1485}
1486
1487HRESULT CDECL wined3d_buffer_create_vb(struct wined3d_device *device, UINT size, DWORD usage, enum wined3d_pool pool,
1488 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer)
1489{
1490 struct wined3d_buffer *object;
1491 HRESULT hr;
1492
1493 TRACE("device %p, size %u, usage %#x, pool %#x, parent %p, parent_ops %p, buffer %p.\n",
1494 device, size, usage, pool, parent, parent_ops, buffer);
1495
1496 if (pool == WINED3D_POOL_SCRATCH)
1497 {
1498 /* The d3d9 tests shows that this is not allowed. It doesn't make much
1499 * sense anyway, SCRATCH buffers wouldn't be usable anywhere. */
1500 WARN("Vertex buffer in WINED3D_POOL_SCRATCH requested, returning WINED3DERR_INVALIDCALL.\n");
1501 *buffer = NULL;
1502 return WINED3DERR_INVALIDCALL;
1503 }
1504
1505 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1506 if (!object)
1507 {
1508 *buffer = NULL;
1509 return WINED3DERR_OUTOFVIDEOMEMORY;
1510 }
1511
1512 hr = buffer_init(object, device, size, usage, WINED3DFMT_VERTEXDATA,
1513 pool, GL_ARRAY_BUFFER_ARB, NULL, parent, parent_ops);
1514 if (FAILED(hr))
1515 {
1516 WARN("Failed to initialize buffer, hr %#x.\n", hr);
1517 HeapFree(GetProcessHeap(), 0, object);
1518 return hr;
1519 }
1520
1521 TRACE("Created buffer %p.\n", object);
1522 *buffer = object;
1523
1524 return WINED3D_OK;
1525}
1526
1527HRESULT CDECL wined3d_buffer_create_ib(struct wined3d_device *device, UINT size, DWORD usage, enum wined3d_pool pool,
1528 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_buffer **buffer)
1529{
1530 struct wined3d_buffer *object;
1531 HRESULT hr;
1532
1533 TRACE("device %p, size %u, usage %#x, pool %#x, parent %p, parent_ops %p, buffer %p.\n",
1534 device, size, usage, pool, parent, parent_ops, buffer);
1535
1536 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1537 if (!object)
1538 {
1539 *buffer = NULL;
1540 return WINED3DERR_OUTOFVIDEOMEMORY;
1541 }
1542
1543 hr = buffer_init(object, device, size, usage | WINED3DUSAGE_STATICDECL,
1544 WINED3DFMT_UNKNOWN, pool, GL_ELEMENT_ARRAY_BUFFER_ARB, NULL,
1545 parent, parent_ops);
1546 if (FAILED(hr))
1547 {
1548 WARN("Failed to initialize buffer, hr %#x\n", hr);
1549 HeapFree(GetProcessHeap(), 0, object);
1550 return hr;
1551 }
1552
1553 TRACE("Created buffer %p.\n", object);
1554 *buffer = object;
1555
1556 return WINED3D_OK;
1557}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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