VirtualBox

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

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

wined3d: update to wine 1.6

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

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