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 Stefan Dösinger for CodeWeavers
7 * Copyright 2009 Henri Verbeet for CodeWeavers
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.
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.
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
26 #include "wine/port.h"
28 #include "wined3d_private.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(d3d
);
32 #define GLINFO_LOCATION This->resource.device->adapter->gl_info
34 #define VB_MAXDECLCHANGES 100 /* After that number of decl changes we stop converting */
35 #define VB_RESETDECLCHANGE 1000 /* Reset the decl changecount after that number of draws */
36 #define VB_MAXFULLCONVERSIONS 5 /* Number of full conversions before we stop converting */
37 #define VB_RESETFULLCONVS 20 /* Reset full conversion counts after that number of draws */
39 static inline BOOL
buffer_add_dirty_area(struct wined3d_buffer
*This
, UINT offset
, UINT size
)
41 if (!This
->buffer_object
) return TRUE
;
43 if (This
->maps_size
<= This
->modified_areas
)
45 void *new = HeapReAlloc(GetProcessHeap(), 0, This
->maps
,
46 This
->maps_size
* 2 * sizeof(*This
->maps
));
49 ERR("Out of memory\n");
61 size
= This
->resource
.size
;
64 This
->maps
[This
->modified_areas
].offset
= offset
;
65 This
->maps
[This
->modified_areas
].size
= size
;
66 This
->modified_areas
++;
70 static inline void buffer_clear_dirty_areas(struct wined3d_buffer
*This
)
72 This
->modified_areas
= 0;
75 static inline BOOL
buffer_is_dirty(struct wined3d_buffer
*This
)
77 return This
->modified_areas
!= 0;
80 static inline BOOL
buffer_is_fully_dirty(struct wined3d_buffer
*This
)
84 for(i
= 0; i
< This
->modified_areas
; i
++)
86 if(This
->maps
[i
].offset
== 0 && This
->maps
[i
].size
== This
->resource
.size
)
94 /* Context activation is done by the caller. */
95 static void buffer_create_buffer_object(struct wined3d_buffer
*This
)
97 GLenum error
, gl_usage
;
98 const struct wined3d_gl_info
*gl_info
= &This
->resource
.device
->adapter
->gl_info
;
100 TRACE("Creating an OpenGL vertex buffer object for IWineD3DVertexBuffer %p Usage(%s)\n",
101 This
, debug_d3dusage(This
->resource
.usage
));
105 /* Make sure that the gl error is cleared. Do not use checkGLcall
106 * here because checkGLcall just prints a fixme and continues. However,
107 * if an error during VBO creation occurs we can fall back to non-vbo operation
108 * with full functionality(but performance loss)
110 while (glGetError() != GL_NO_ERROR
);
112 /* Basically the FVF parameter passed to CreateVertexBuffer is no good
113 * It is the FVF set with IWineD3DDevice::SetFVF or the Vertex Declaration set with
114 * IWineD3DDevice::SetVertexDeclaration that decides how the vertices in the buffer
115 * look like. This means that on each DrawPrimitive call the vertex buffer has to be verified
116 * to check if the rhw and color values are in the correct format.
119 GL_EXTCALL(glGenBuffersARB(1, &This
->buffer_object
));
120 error
= glGetError();
121 if (!This
->buffer_object
|| error
!= GL_NO_ERROR
)
123 ERR("Failed to create a VBO with error %s (%#x)\n", debug_glerror(error
), error
);
128 if(This
->buffer_type_hint
== GL_ELEMENT_ARRAY_BUFFER_ARB
)
130 IWineD3DDeviceImpl_MarkStateDirty(This
->resource
.device
, STATE_INDEXBUFFER
);
132 GL_EXTCALL(glBindBufferARB(This
->buffer_type_hint
, This
->buffer_object
));
133 error
= glGetError();
134 if (error
!= GL_NO_ERROR
)
136 ERR("Failed to bind the VBO with error %s (%#x)\n", debug_glerror(error
), error
);
141 /* Don't use static, because dx apps tend to update the buffer
142 * quite often even if they specify 0 usage.
144 if(This
->resource
.usage
& WINED3DUSAGE_DYNAMIC
)
146 TRACE("Gl usage = GL_STREAM_DRAW_ARB\n");
147 gl_usage
= GL_STREAM_DRAW_ARB
;
151 TRACE("Gl usage = GL_DYNAMIC_DRAW_ARB\n");
152 gl_usage
= GL_DYNAMIC_DRAW_ARB
;
154 if(gl_info
->supported
[APPLE_FLUSH_BUFFER_RANGE
])
156 GL_EXTCALL(glBufferParameteriAPPLE(This
->buffer_type_hint
, GL_BUFFER_FLUSHING_UNMAP_APPLE
, GL_FALSE
));
157 checkGLcall("glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE)");
158 This
->flags
|= WINED3D_BUFFER_FLUSH
;
162 /* Reserve memory for the buffer. The amount of data won't change
163 * so we are safe with calling glBufferData once and
164 * calling glBufferSubData on updates. Upload the actual data in case
165 * we're not double buffering, so we can release the heap mem afterwards
167 GL_EXTCALL(glBufferDataARB(This
->buffer_type_hint
, This
->resource
.size
, This
->resource
.allocatedMemory
, gl_usage
));
168 error
= glGetError();
170 if (error
!= GL_NO_ERROR
)
172 ERR("glBufferDataARB failed with error %s (%#x)\n", debug_glerror(error
), error
);
176 This
->buffer_object_size
= This
->resource
.size
;
177 This
->buffer_object_usage
= gl_usage
;
179 if(This
->flags
& WINED3D_BUFFER_DOUBLEBUFFER
)
181 if(!buffer_add_dirty_area(This
, 0, 0))
183 ERR("buffer_add_dirty_area failed, this is not expected\n");
189 HeapFree(GetProcessHeap(), 0, This
->resource
.heapMemory
);
190 This
->resource
.allocatedMemory
= NULL
;
191 This
->resource
.heapMemory
= NULL
;
197 /* Clean up all vbo init, but continue because we can work without a vbo :-) */
198 ERR("Failed to create a vertex buffer object. Continuing, but performance issues may occur\n");
199 if (This
->buffer_object
)
202 GL_EXTCALL(glDeleteBuffersARB(1, &This
->buffer_object
));
205 This
->buffer_object
= 0;
206 buffer_clear_dirty_areas(This
);
209 static BOOL
buffer_process_converted_attribute(struct wined3d_buffer
*This
,
210 const enum wined3d_buffer_conversion_type conversion_type
,
211 const struct wined3d_stream_info_element
*attrib
, DWORD
*stride_this_run
)
216 DWORD offset
= This
->resource
.device
->stateBlock
->streamOffset
[attrib
->stream_idx
];
219 /* Check for some valid situations which cause us pain. One is if the buffer is used for
220 * constant attributes(stride = 0), the other one is if the buffer is used on two streams
221 * with different strides. In the 2nd case we might have to drop conversion entirely,
222 * it is possible that the same bytes are once read as FLOAT2 and once as UBYTE4N.
226 FIXME("%s used with stride 0, let's hope we get the vertex stride from somewhere else\n",
227 debug_d3dformat(attrib
->format_desc
->format
));
229 else if(attrib
->stride
!= *stride_this_run
&& *stride_this_run
)
231 FIXME("Got two concurrent strides, %d and %d\n", attrib
->stride
, *stride_this_run
);
235 *stride_this_run
= attrib
->stride
;
236 if (This
->stride
!= *stride_this_run
)
238 /* We rely that this happens only on the first converted attribute that is found,
239 * if at all. See above check
241 TRACE("Reconverting because converted attributes occur, and the stride changed\n");
242 This
->stride
= *stride_this_run
;
243 HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY
, This
->conversion_map
);
244 This
->conversion_map
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
245 sizeof(*This
->conversion_map
) * This
->stride
);
250 data
= (((DWORD_PTR
)attrib
->data
) + offset
) % This
->stride
;
251 attrib_size
= attrib
->format_desc
->component_count
* attrib
->format_desc
->component_size
;
252 for (i
= 0; i
< attrib_size
; ++i
)
254 if (This
->conversion_map
[data
+ i
] != conversion_type
)
256 TRACE("Byte %ld in vertex changed\n", i
+ data
);
257 TRACE("It was type %d, is %d now\n", This
->conversion_map
[data
+ i
], conversion_type
);
259 This
->conversion_map
[data
+ i
] = conversion_type
;
266 static BOOL
buffer_check_attribute(struct wined3d_buffer
*This
, const struct wined3d_stream_info
*si
,
267 UINT attrib_idx
, const BOOL check_d3dcolor
, const BOOL is_ffp_position
, const BOOL is_ffp_color
,
268 DWORD
*stride_this_run
, BOOL
*float16_used
)
270 const struct wined3d_stream_info_element
*attrib
= &si
->elements
[attrib_idx
];
271 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
272 const struct wined3d_gl_info
*gl_info
= &device
->adapter
->gl_info
;
274 WINED3DFORMAT format
;
276 /* Ignore attributes that do not have our vbo. After that check we can be sure that the attribute is
277 * there, on nonexistent attribs the vbo is 0.
279 if (!(si
->use_map
& (1 << attrib_idx
))
280 || attrib
->buffer_object
!= This
->buffer_object
)
283 format
= attrib
->format_desc
->format
;
284 /* Look for newly appeared conversion */
285 if (!gl_info
->supported
[ARB_HALF_FLOAT_VERTEX
]
286 && (format
== WINED3DFMT_R16G16_FLOAT
|| format
== WINED3DFMT_R16G16B16A16_FLOAT
))
288 ret
= buffer_process_converted_attribute(This
, CONV_FLOAT16_2
, attrib
, stride_this_run
);
290 if (is_ffp_position
) FIXME("Test FLOAT16 fixed function processing positions\n");
291 else if (is_ffp_color
) FIXME("test FLOAT16 fixed function processing colors\n");
292 *float16_used
= TRUE
;
294 else if (check_d3dcolor
&& format
== WINED3DFMT_B8G8R8A8_UNORM
)
296 ret
= buffer_process_converted_attribute(This
, CONV_D3DCOLOR
, attrib
, stride_this_run
);
298 if (!is_ffp_color
) FIXME("Test for non-color fixed function WINED3DFMT_B8G8R8A8_UNORM format\n");
300 else if (is_ffp_position
&& format
== WINED3DFMT_R32G32B32A32_FLOAT
)
302 ret
= buffer_process_converted_attribute(This
, CONV_POSITIONT
, attrib
, stride_this_run
);
304 else if (This
->conversion_map
)
306 ret
= buffer_process_converted_attribute(This
, CONV_NONE
, attrib
, stride_this_run
);
312 static UINT
*find_conversion_shift(struct wined3d_buffer
*This
,
313 const struct wined3d_stream_info
*strided
, UINT stride
)
315 UINT
*ret
, i
, j
, shift
, orig_type_size
;
323 This
->conversion_stride
= stride
;
324 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(DWORD
) * stride
);
325 for (i
= 0; i
< MAX_ATTRIBS
; ++i
)
327 WINED3DFORMAT format
;
329 if (!(strided
->use_map
& (1 << i
)) || strided
->elements
[i
].buffer_object
!= This
->buffer_object
) continue;
331 format
= strided
->elements
[i
].format_desc
->format
;
332 if (format
== WINED3DFMT_R16G16_FLOAT
)
336 else if (format
== WINED3DFMT_R16G16B16A16_FLOAT
)
339 /* Pre-shift the last 4 bytes in the FLOAT16_4 by 4 bytes - this makes FLOAT16_2 and FLOAT16_4 conversions
342 for (j
= 4; j
< 8; ++j
)
344 ret
[(DWORD_PTR
)strided
->elements
[i
].data
+ j
] += 4;
351 This
->conversion_stride
+= shift
;
355 orig_type_size
= strided
->elements
[i
].format_desc
->component_count
356 * strided
->elements
[i
].format_desc
->component_size
;
357 for (j
= (DWORD_PTR
)strided
->elements
[i
].data
+ orig_type_size
; j
< stride
; ++j
)
366 TRACE("Dumping conversion shift:\n");
367 for (i
= 0; i
< stride
; ++i
)
369 TRACE("[%d]", ret
[i
]);
377 static BOOL
buffer_find_decl(struct wined3d_buffer
*This
)
379 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
380 const struct wined3d_gl_info
*gl_info
= &device
->adapter
->gl_info
;
381 const struct wined3d_stream_info
*si
= &device
->strided_streams
;
382 UINT stride_this_run
= 0;
383 BOOL float16_used
= FALSE
;
387 /* In d3d7 the vertex buffer declaration NEVER changes because it is stored in the d3d7 vertex buffer.
388 * Once we have our declaration there is no need to look it up again. Index buffers also never need
389 * conversion, so once the (empty) conversion structure is created don't bother checking again
391 if (This
->flags
& WINED3D_BUFFER_HASDESC
)
393 if(This
->resource
.usage
& WINED3DUSAGE_STATICDECL
) return FALSE
;
396 TRACE("Finding vertex buffer conversion information\n");
397 /* Certain declaration types need some fixups before we can pass them to
398 * opengl. This means D3DCOLOR attributes with fixed function vertex
399 * processing, FLOAT4 POSITIONT with fixed function, and FLOAT16 if
400 * GL_ARB_half_float_vertex is not supported.
402 * Note for d3d8 and d3d9:
403 * The vertex buffer FVF doesn't help with finding them, we have to use
404 * the decoded vertex declaration and pick the things that concern the
405 * current buffer. A problem with this is that this can change between
406 * draws, so we have to validate the information and reprocess the buffer
407 * if it changes, and avoid false positives for performance reasons.
408 * WineD3D doesn't even know the vertex buffer any more, it is managed
409 * by the client libraries and passed to SetStreamSource and ProcessVertices
412 * We have to distinguish between vertex shaders and fixed function to
413 * pick the way we access the strided vertex information.
415 * This code sets up a per-byte array with the size of the detected
416 * stride of the arrays in the buffer. For each byte we have a field
417 * that marks the conversion needed on this byte. For example, the
418 * following declaration with fixed function vertex processing:
426 * { POSITIONT }{ NORMAL }{ DIFFUSE }{SPECULAR }
427 * [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]
429 * Where in this example map P means 4 component position conversion, 0
430 * means no conversion, F means FLOAT16_2 conversion and C means D3DCOLOR
431 * conversion (red / blue swizzle).
433 * If we're doing conversion and the stride changes we have to reconvert
434 * the whole buffer. Note that we do not mind if the semantic changes,
435 * we only care for the conversion type. So if the NORMAL is replaced
436 * with a TEXCOORD, nothing has to be done, or if the DIFFUSE is replaced
437 * with a D3DCOLOR BLENDWEIGHT we can happily dismiss the change. Some
438 * conversion types depend on the semantic as well, for example a FLOAT4
439 * texcoord needs no conversion while a FLOAT4 positiont needs one
441 if (use_vs(device
->stateBlock
))
444 /* If the current vertex declaration is marked for no half float conversion don't bother to
445 * analyse the strided streams in depth, just set them up for no conversion. Return decl changed
446 * if we used conversion before
448 if (!((IWineD3DVertexDeclarationImpl
*) device
->stateBlock
->vertexDecl
)->half_float_conv_needed
)
450 if (This
->conversion_map
)
452 TRACE("Now using shaders without conversion, but conversion used before\n");
453 HeapFree(GetProcessHeap(), 0, This
->conversion_map
);
454 HeapFree(GetProcessHeap(), 0, This
->conversion_shift
);
455 This
->conversion_map
= NULL
;
457 This
->conversion_shift
= NULL
;
458 This
->conversion_stride
= 0;
466 for (i
= 0; i
< MAX_ATTRIBS
; ++i
)
468 ret
= buffer_check_attribute(This
, si
, i
, FALSE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
471 /* Recalculate the conversion shift map if the declaration has changed,
472 * and we're using float16 conversion or used it on the last run
474 if (ret
&& (float16_used
|| This
->conversion_map
))
476 HeapFree(GetProcessHeap(), 0, This
->conversion_shift
);
477 This
->conversion_shift
= find_conversion_shift(This
, si
, This
->stride
);
482 /* Fixed function is a bit trickier. We have to take care for D3DCOLOR types, FLOAT4 positions and of course
483 * FLOAT16s if not supported. Also, we can't iterate over the array, so use macros to generate code for all
484 * the attributes that our current fixed function pipeline implementation cares for.
486 BOOL support_d3dcolor
= gl_info
->supported
[EXT_VERTEX_ARRAY_BGRA
];
487 ret
= buffer_check_attribute(This
, si
, WINED3D_FFP_POSITION
,
488 TRUE
, TRUE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
489 ret
= buffer_check_attribute(This
, si
, WINED3D_FFP_NORMAL
,
490 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
491 ret
= buffer_check_attribute(This
, si
, WINED3D_FFP_DIFFUSE
,
492 !support_d3dcolor
, FALSE
, TRUE
, &stride_this_run
, &float16_used
) || ret
;
493 ret
= buffer_check_attribute(This
, si
, WINED3D_FFP_SPECULAR
,
494 !support_d3dcolor
, FALSE
, TRUE
, &stride_this_run
, &float16_used
) || ret
;
495 ret
= buffer_check_attribute(This
, si
, WINED3D_FFP_TEXCOORD0
,
496 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
497 ret
= buffer_check_attribute(This
, si
, WINED3D_FFP_TEXCOORD1
,
498 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
499 ret
= buffer_check_attribute(This
, si
, WINED3D_FFP_TEXCOORD2
,
500 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
501 ret
= buffer_check_attribute(This
, si
, WINED3D_FFP_TEXCOORD3
,
502 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
503 ret
= buffer_check_attribute(This
, si
, WINED3D_FFP_TEXCOORD4
,
504 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
505 ret
= buffer_check_attribute(This
, si
, WINED3D_FFP_TEXCOORD5
,
506 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
507 ret
= buffer_check_attribute(This
, si
, WINED3D_FFP_TEXCOORD6
,
508 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
509 ret
= buffer_check_attribute(This
, si
, WINED3D_FFP_TEXCOORD7
,
510 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
512 if (float16_used
) FIXME("Float16 conversion used with fixed function vertex processing\n");
515 if (stride_this_run
== 0 && This
->conversion_map
)
518 if (!ret
) ERR("no converted attributes found, old conversion map exists, and no declaration change?\n");
519 HeapFree(GetProcessHeap(), 0, This
->conversion_map
);
520 This
->conversion_map
= NULL
;
524 if (ret
) TRACE("Conversion information changed\n");
529 /* Context activation is done by the caller. */
530 static void buffer_check_buffer_object_size(struct wined3d_buffer
*This
)
532 UINT size
= This
->conversion_stride
?
533 This
->conversion_stride
* (This
->resource
.size
/ This
->stride
) : This
->resource
.size
;
534 if (This
->buffer_object_size
!= size
)
536 TRACE("Old size %u, creating new size %u\n", This
->buffer_object_size
, size
);
538 if(This
->buffer_type_hint
== GL_ELEMENT_ARRAY_BUFFER_ARB
)
540 IWineD3DDeviceImpl_MarkStateDirty(This
->resource
.device
, STATE_INDEXBUFFER
);
543 /* Rescue the data before resizing the buffer object if we do not have our backup copy */
544 if(!(This
->flags
& WINED3D_BUFFER_DOUBLEBUFFER
))
546 buffer_get_sysmem(This
);
550 GL_EXTCALL(glBindBufferARB(This
->buffer_type_hint
, This
->buffer_object
));
551 checkGLcall("glBindBufferARB");
552 GL_EXTCALL(glBufferDataARB(This
->buffer_type_hint
, size
, NULL
, This
->buffer_object_usage
));
553 This
->buffer_object_size
= size
;
554 checkGLcall("glBufferDataARB");
559 static inline void fixup_d3dcolor(DWORD
*dst_color
)
561 DWORD src_color
= *dst_color
;
563 /* Color conversion like in drawStridedSlow. watch out for little endianity
564 * If we want that stuff to work on big endian machines too we have to consider more things
566 * 0xff000000: Alpha mask
567 * 0x00ff0000: Blue mask
568 * 0x0000ff00: Green mask
569 * 0x000000ff: Red mask
572 *dst_color
|= (src_color
& 0xff00ff00); /* Alpha Green */
573 *dst_color
|= (src_color
& 0x00ff0000) >> 16; /* Red */
574 *dst_color
|= (src_color
& 0x000000ff) << 16; /* Blue */
577 static inline void fixup_transformed_pos(float *p
)
579 /* rhw conversion like in position_float4(). */
580 if (p
[3] != 1.0f
&& p
[3] != 0.0f
)
582 float w
= 1.0f
/ p
[3];
590 /* Context activation is done by the caller. */
591 const BYTE
*buffer_get_memory(IWineD3DBuffer
*iface
, UINT offset
, GLuint
*buffer_object
)
593 struct wined3d_buffer
*This
= (struct wined3d_buffer
*)iface
;
595 *buffer_object
= This
->buffer_object
;
596 if (!This
->buffer_object
)
598 if (This
->flags
& WINED3D_BUFFER_CREATEBO
)
600 buffer_create_buffer_object(This
);
601 This
->flags
&= ~WINED3D_BUFFER_CREATEBO
;
602 if (This
->buffer_object
)
604 *buffer_object
= This
->buffer_object
;
605 return (const BYTE
*)offset
;
608 return This
->resource
.allocatedMemory
+ offset
;
612 return (const BYTE
*)offset
;
616 /* IUnknown methods */
618 static HRESULT STDMETHODCALLTYPE
buffer_QueryInterface(IWineD3DBuffer
*iface
,
619 REFIID riid
, void **object
)
621 TRACE("iface %p, riid %s, object %p\n", iface
, debugstr_guid(riid
), object
);
623 if (IsEqualGUID(riid
, &IID_IWineD3DBuffer
)
624 || IsEqualGUID(riid
, &IID_IWineD3DResource
)
625 || IsEqualGUID(riid
, &IID_IWineD3DBase
)
626 || IsEqualGUID(riid
, &IID_IUnknown
))
628 IUnknown_AddRef(iface
);
633 WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid
));
637 return E_NOINTERFACE
;
640 static ULONG STDMETHODCALLTYPE
buffer_AddRef(IWineD3DBuffer
*iface
)
642 struct wined3d_buffer
*This
= (struct wined3d_buffer
*)iface
;
643 ULONG refcount
= InterlockedIncrement(&This
->resource
.ref
);
645 TRACE("%p increasing refcount to %u\n", This
, refcount
);
650 /* Context activation is done by the caller. */
651 BYTE
*buffer_get_sysmem(struct wined3d_buffer
*This
)
653 /* AllocatedMemory exists if the buffer is double buffered or has no buffer object at all */
654 if(This
->resource
.allocatedMemory
) return This
->resource
.allocatedMemory
;
656 This
->resource
.heapMemory
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, This
->resource
.size
+ RESOURCE_ALIGNMENT
);
657 This
->resource
.allocatedMemory
= (BYTE
*)(((ULONG_PTR
)This
->resource
.heapMemory
+ (RESOURCE_ALIGNMENT
- 1)) & ~(RESOURCE_ALIGNMENT
- 1));
659 GL_EXTCALL(glBindBufferARB(This
->buffer_type_hint
, This
->buffer_object
));
660 GL_EXTCALL(glGetBufferSubDataARB(This
->buffer_type_hint
, 0, This
->resource
.size
, This
->resource
.allocatedMemory
));
662 This
->flags
|= WINED3D_BUFFER_DOUBLEBUFFER
;
664 return This
->resource
.allocatedMemory
;
667 static void STDMETHODCALLTYPE
buffer_UnLoad(IWineD3DBuffer
*iface
)
669 struct wined3d_buffer
*This
= (struct wined3d_buffer
*)iface
;
671 TRACE("iface %p\n", iface
);
673 if (This
->buffer_object
)
675 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
676 struct wined3d_context
*context
;
678 context
= context_acquire(device
, NULL
, CTXUSAGE_RESOURCELOAD
);
680 /* Download the buffer, but don't permanently enable double buffering */
681 if(!(This
->flags
& WINED3D_BUFFER_DOUBLEBUFFER
))
683 buffer_get_sysmem(This
);
684 This
->flags
&= ~WINED3D_BUFFER_DOUBLEBUFFER
;
688 GL_EXTCALL(glDeleteBuffersARB(1, &This
->buffer_object
));
689 checkGLcall("glDeleteBuffersARB");
691 This
->buffer_object
= 0;
692 This
->flags
|= WINED3D_BUFFER_CREATEBO
; /* Recreate the buffer object next load */
693 buffer_clear_dirty_areas(This
);
695 context_release(context
);
697 HeapFree(GetProcessHeap(), 0, This
->conversion_shift
);
698 This
->conversion_shift
= NULL
;
699 HeapFree(GetProcessHeap(), 0, This
->conversion_map
);
700 This
->conversion_map
= NULL
;
702 This
->conversion_stride
= 0;
703 This
->flags
&= ~WINED3D_BUFFER_HASDESC
;
707 static ULONG STDMETHODCALLTYPE
buffer_Release(IWineD3DBuffer
*iface
)
709 struct wined3d_buffer
*This
= (struct wined3d_buffer
*)iface
;
710 ULONG refcount
= InterlockedDecrement(&This
->resource
.ref
);
712 TRACE("%p decreasing refcount to %u\n", This
, refcount
);
716 buffer_UnLoad(iface
);
717 resource_cleanup((IWineD3DResource
*)iface
);
718 This
->resource
.parent_ops
->wined3d_object_destroyed(This
->resource
.parent
);
719 HeapFree(GetProcessHeap(), 0, This
->maps
);
720 HeapFree(GetProcessHeap(), 0, This
);
726 /* IWineD3DBase methods */
728 static HRESULT STDMETHODCALLTYPE
buffer_GetParent(IWineD3DBuffer
*iface
, IUnknown
**parent
)
730 return resource_get_parent((IWineD3DResource
*)iface
, parent
);
733 /* IWineD3DResource methods */
735 static HRESULT STDMETHODCALLTYPE
buffer_SetPrivateData(IWineD3DBuffer
*iface
,
736 REFGUID guid
, const void *data
, DWORD data_size
, DWORD flags
)
738 return resource_set_private_data((IWineD3DResource
*)iface
, guid
, data
, data_size
, flags
);
741 static HRESULT STDMETHODCALLTYPE
buffer_GetPrivateData(IWineD3DBuffer
*iface
,
742 REFGUID guid
, void *data
, DWORD
*data_size
)
744 return resource_get_private_data((IWineD3DResource
*)iface
, guid
, data
, data_size
);
747 static HRESULT STDMETHODCALLTYPE
buffer_FreePrivateData(IWineD3DBuffer
*iface
, REFGUID guid
)
749 return resource_free_private_data((IWineD3DResource
*)iface
, guid
);
752 static DWORD STDMETHODCALLTYPE
buffer_SetPriority(IWineD3DBuffer
*iface
, DWORD priority
)
754 return resource_set_priority((IWineD3DResource
*)iface
, priority
);
757 static DWORD STDMETHODCALLTYPE
buffer_GetPriority(IWineD3DBuffer
*iface
)
759 return resource_get_priority((IWineD3DResource
*)iface
);
762 static void STDMETHODCALLTYPE
buffer_PreLoad(IWineD3DBuffer
*iface
)
764 struct wined3d_buffer
*This
= (struct wined3d_buffer
*)iface
;
765 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
766 UINT start
= 0, end
= 0, len
= 0, vertices
;
767 struct wined3d_context
*context
;
768 BOOL decl_changed
= FALSE
;
772 TRACE("iface %p\n", iface
);
774 context
= context_acquire(device
, NULL
, CTXUSAGE_RESOURCELOAD
);
776 if (!This
->buffer_object
)
778 /* TODO: Make converting independent from VBOs */
779 if (This
->flags
& WINED3D_BUFFER_CREATEBO
)
781 buffer_create_buffer_object(This
);
782 This
->flags
&= ~WINED3D_BUFFER_CREATEBO
;
786 /* Not doing any conversion */
791 /* Reading the declaration makes only sense if the stateblock is finalized and the buffer bound to a stream */
792 if (device
->isInDraw
&& This
->bind_count
> 0)
794 decl_changed
= buffer_find_decl(This
);
795 This
->flags
|= WINED3D_BUFFER_HASDESC
;
798 if (!decl_changed
&& !(This
->flags
& WINED3D_BUFFER_HASDESC
&& buffer_is_dirty(This
)))
800 context_release(context
);
802 if (This
->draw_count
> VB_RESETDECLCHANGE
) This
->decl_change_count
= 0;
803 if (This
->draw_count
> VB_RESETFULLCONVS
) This
->full_conversion_count
= 0;
807 /* If applications change the declaration over and over, reconverting all the time is a huge
808 * performance hit. So count the declaration changes and release the VBO if there are too many
809 * of them (and thus stop converting)
813 ++This
->decl_change_count
;
814 This
->draw_count
= 0;
816 if (This
->decl_change_count
> VB_MAXDECLCHANGES
||
817 (This
->conversion_map
&& (This
->resource
.usage
& WINED3DUSAGE_DYNAMIC
)))
819 FIXME("Too many declaration changes or converting dynamic buffer, stopping converting\n");
821 IWineD3DBuffer_UnLoad(iface
);
822 This
->flags
&= ~WINED3D_BUFFER_CREATEBO
;
824 /* The stream source state handler might have read the memory of the vertex buffer already
825 * and got the memory in the vbo which is not valid any longer. Dirtify the stream source
826 * to force a reload. This happens only once per changed vertexbuffer and should occur rather
829 IWineD3DDeviceImpl_MarkStateDirty(device
, STATE_STREAMSRC
);
832 buffer_check_buffer_object_size(This
);
836 /* However, it is perfectly fine to change the declaration every now and then. We don't want a game that
837 * changes it every minute drop the VBO after VB_MAX_DECL_CHANGES minutes. So count draws without
838 * decl changes and reset the decl change count after a specific number of them
840 if(buffer_is_fully_dirty(This
))
842 ++This
->full_conversion_count
;
843 if(This
->full_conversion_count
> VB_MAXFULLCONVERSIONS
)
845 FIXME("Too many full buffer conversions, stopping converting\n");
846 IWineD3DBuffer_UnLoad(iface
);
847 This
->flags
&= ~WINED3D_BUFFER_CREATEBO
;
848 IWineD3DDeviceImpl_MarkStateDirty(device
, STATE_STREAMSRC
);
855 if (This
->draw_count
> VB_RESETDECLCHANGE
) This
->decl_change_count
= 0;
856 if (This
->draw_count
> VB_RESETFULLCONVS
) This
->full_conversion_count
= 0;
862 /* The declaration changed, reload the whole buffer */
863 WARN("Reloading buffer because of decl change\n");
864 buffer_clear_dirty_areas(This
);
865 if(!buffer_add_dirty_area(This
, 0, 0))
867 ERR("buffer_add_dirty_area failed, this is not expected\n");
872 if(This
->buffer_type_hint
== GL_ELEMENT_ARRAY_BUFFER_ARB
)
874 IWineD3DDeviceImpl_MarkStateDirty(This
->resource
.device
, STATE_INDEXBUFFER
);
877 if (!This
->conversion_map
)
879 /* That means that there is nothing to fixup. Just upload from This->resource.allocatedMemory
880 * directly into the vbo. Do not free the system memory copy because drawPrimitive may need it if
881 * the stride is 0, for instancing emulation, vertex blending emulation or shader emulation.
883 TRACE("No conversion needed\n");
885 /* Nothing to do because we locked directly into the vbo */
886 if (!(This
->flags
& WINED3D_BUFFER_DOUBLEBUFFER
))
888 context_release(context
);
893 GL_EXTCALL(glBindBufferARB(This
->buffer_type_hint
, This
->buffer_object
));
894 checkGLcall("glBindBufferARB");
895 while(This
->modified_areas
)
897 This
->modified_areas
--;
898 start
= This
->maps
[This
->modified_areas
].offset
;
899 len
= This
->maps
[This
->modified_areas
].size
;
900 GL_EXTCALL(glBufferSubDataARB(This
->buffer_type_hint
, start
, len
, This
->resource
.allocatedMemory
+ start
));
901 checkGLcall("glBufferSubDataARB");
905 context_release(context
);
909 if(!(This
->flags
& WINED3D_BUFFER_DOUBLEBUFFER
))
911 buffer_get_sysmem(This
);
914 /* Now for each vertex in the buffer that needs conversion */
915 vertices
= This
->resource
.size
/ This
->stride
;
917 if (This
->conversion_shift
)
919 TRACE("Shifted conversion\n");
920 data
= HeapAlloc(GetProcessHeap(), 0, vertices
* This
->conversion_stride
);
923 len
= This
->resource
.size
;
926 if (This
->maps
[0].offset
|| This
->maps
[0].size
!= This
->resource
.size
)
928 FIXME("Implement partial buffer load with shifted conversion\n");
931 for (i
= start
/ This
->stride
; i
< min((end
/ This
->stride
) + 1, vertices
); ++i
)
933 for (j
= 0; j
< This
->stride
; ++j
)
935 switch(This
->conversion_map
[j
])
938 data
[This
->conversion_stride
* i
+ j
+ This
->conversion_shift
[j
]]
939 = This
->resource
.allocatedMemory
[This
->stride
* i
+ j
];
944 float *out
= (float *)(&data
[This
->conversion_stride
* i
+ j
+ This
->conversion_shift
[j
]]);
945 const WORD
*in
= (WORD
*)(&This
->resource
.allocatedMemory
[i
* This
->stride
+ j
]);
947 out
[1] = float_16_to_32(in
+ 1);
948 out
[0] = float_16_to_32(in
+ 0);
949 j
+= 3; /* Skip 3 additional bytes,as a FLOAT16_2 has 4 bytes */
954 FIXME("Unimplemented conversion %d in shifted conversion\n", This
->conversion_map
[j
]);
961 GL_EXTCALL(glBindBufferARB(This
->buffer_type_hint
, This
->buffer_object
));
962 checkGLcall("glBindBufferARB");
963 GL_EXTCALL(glBufferSubDataARB(This
->buffer_type_hint
, 0, vertices
* This
->conversion_stride
, data
));
964 checkGLcall("glBufferSubDataARB");
969 data
= HeapAlloc(GetProcessHeap(), 0, This
->resource
.size
);
971 while(This
->modified_areas
)
973 This
->modified_areas
--;
974 start
= This
->maps
[This
->modified_areas
].offset
;
975 len
= This
->maps
[This
->modified_areas
].size
;
978 memcpy(data
+ start
, This
->resource
.allocatedMemory
+ start
, end
- start
);
979 for (i
= start
/ This
->stride
; i
< min((end
/ This
->stride
) + 1, vertices
); ++i
)
981 for (j
= 0; j
< This
->stride
; ++j
)
983 switch(This
->conversion_map
[j
])
990 fixup_d3dcolor((DWORD
*) (data
+ i
* This
->stride
+ j
));
995 fixup_transformed_pos((float *) (data
+ i
* This
->stride
+ j
));
1000 ERR("Did not expect FLOAT16 conversion in unshifted conversion\n");
1002 FIXME("Unimplemented conversion %d in shifted conversion\n", This
->conversion_map
[j
]);
1008 GL_EXTCALL(glBindBufferARB(This
->buffer_type_hint
, This
->buffer_object
));
1009 checkGLcall("glBindBufferARB");
1010 GL_EXTCALL(glBufferSubDataARB(This
->buffer_type_hint
, start
, len
, data
+ start
));
1011 checkGLcall("glBufferSubDataARB");
1016 HeapFree(GetProcessHeap(), 0, data
);
1019 context_release(context
);
1022 static WINED3DRESOURCETYPE STDMETHODCALLTYPE
buffer_GetType(IWineD3DBuffer
*iface
)
1024 return resource_get_type((IWineD3DResource
*)iface
);
1027 /* IWineD3DBuffer methods */
1029 static HRESULT STDMETHODCALLTYPE
buffer_Map(IWineD3DBuffer
*iface
, UINT offset
, UINT size
, BYTE
**data
, DWORD flags
)
1031 struct wined3d_buffer
*This
= (struct wined3d_buffer
*)iface
;
1034 TRACE("iface %p, offset %u, size %u, data %p, flags %#x\n", iface
, offset
, size
, data
, flags
);
1036 if (!buffer_add_dirty_area(This
, offset
, size
)) return E_OUTOFMEMORY
;
1038 count
= InterlockedIncrement(&This
->lock_count
);
1040 if(!(This
->flags
& WINED3D_BUFFER_DOUBLEBUFFER
) && This
->buffer_object
)
1044 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
1045 struct wined3d_context
*context
;
1047 if(This
->buffer_type_hint
== GL_ELEMENT_ARRAY_BUFFER_ARB
)
1049 IWineD3DDeviceImpl_MarkStateDirty(This
->resource
.device
, STATE_INDEXBUFFER
);
1052 context
= context_acquire(device
, NULL
, CTXUSAGE_RESOURCELOAD
);
1054 GL_EXTCALL(glBindBufferARB(This
->buffer_type_hint
, This
->buffer_object
));
1055 This
->resource
.allocatedMemory
= GL_EXTCALL(glMapBufferARB(This
->buffer_type_hint
, GL_READ_WRITE_ARB
));
1057 context_release(context
);
1061 *data
= This
->resource
.allocatedMemory
+ offset
;
1063 TRACE("Returning memory at %p (base %p, offset %u)\n", *data
, This
->resource
.allocatedMemory
, offset
);
1064 /* TODO: check Flags compatibility with This->currentDesc.Usage (see MSDN) */
1069 static HRESULT STDMETHODCALLTYPE
buffer_Unmap(IWineD3DBuffer
*iface
)
1071 struct wined3d_buffer
*This
= (struct wined3d_buffer
*)iface
;
1074 TRACE("(%p)\n", This
);
1076 /* In the case that the number of Unmap calls > the
1077 * number of Map calls, d3d returns always D3D_OK.
1078 * This is also needed to prevent Map from returning garbage on
1079 * the next call (this will happen if the lock_count is < 0). */
1080 if(This
->lock_count
== 0)
1082 TRACE("Unmap called without a previous Map call!\n");
1086 if (InterlockedDecrement(&This
->lock_count
))
1088 /* Delay loading the buffer until everything is unlocked */
1089 TRACE("Ignoring unlock\n");
1093 if(!(This
->flags
& WINED3D_BUFFER_DOUBLEBUFFER
) && This
->buffer_object
)
1095 IWineD3DDeviceImpl
*device
= This
->resource
.device
;
1096 struct wined3d_context
*context
;
1098 if(This
->buffer_type_hint
== GL_ELEMENT_ARRAY_BUFFER_ARB
)
1100 IWineD3DDeviceImpl_MarkStateDirty(This
->resource
.device
, STATE_INDEXBUFFER
);
1103 context
= context_acquire(device
, NULL
, CTXUSAGE_RESOURCELOAD
);
1105 GL_EXTCALL(glBindBufferARB(This
->buffer_type_hint
, This
->buffer_object
));
1107 if(This
->flags
& WINED3D_BUFFER_FLUSH
)
1109 for(i
= 0; i
< This
->modified_areas
; i
++)
1111 GL_EXTCALL(glFlushMappedBufferRangeAPPLE(This
->buffer_type_hint
,
1112 This
->maps
[i
].offset
,
1113 This
->maps
[i
].size
));
1114 checkGLcall("glFlushMappedBufferRangeAPPLE");
1118 GL_EXTCALL(glUnmapBufferARB(This
->buffer_type_hint
));
1120 context_release(context
);
1122 This
->resource
.allocatedMemory
= NULL
;
1123 buffer_clear_dirty_areas(This
);
1125 else if (This
->flags
& WINED3D_BUFFER_HASDESC
)
1127 buffer_PreLoad(iface
);
1133 static HRESULT STDMETHODCALLTYPE
buffer_GetDesc(IWineD3DBuffer
*iface
, WINED3DBUFFER_DESC
*desc
)
1135 struct wined3d_buffer
*This
= (struct wined3d_buffer
*)iface
;
1137 TRACE("(%p)\n", This
);
1139 desc
->Type
= This
->resource
.resourceType
;
1140 desc
->Usage
= This
->resource
.usage
;
1141 desc
->Pool
= This
->resource
.pool
;
1142 desc
->Size
= This
->resource
.size
;
1147 static const struct IWineD3DBufferVtbl wined3d_buffer_vtbl
=
1149 /* IUnknown methods */
1150 buffer_QueryInterface
,
1153 /* IWineD3DBase methods */
1155 /* IWineD3DResource methods */
1156 buffer_SetPrivateData
,
1157 buffer_GetPrivateData
,
1158 buffer_FreePrivateData
,
1164 /* IWineD3DBuffer methods */
1170 HRESULT
buffer_init(struct wined3d_buffer
*buffer
, IWineD3DDeviceImpl
*device
,
1171 UINT size
, DWORD usage
, WINED3DFORMAT format
, WINED3DPOOL pool
, GLenum bind_hint
,
1172 const char *data
, IUnknown
*parent
, const struct wined3d_parent_ops
*parent_ops
)
1174 const struct GlPixelFormatDesc
*format_desc
= getFormatDescEntry(format
, &device
->adapter
->gl_info
);
1176 const struct wined3d_gl_info
*gl_info
= &device
->adapter
->gl_info
;
1177 BOOL dynamic_buffer_ok
;
1181 WARN("Size 0 requested, returning WINED3DERR_INVALIDCALL\n");
1182 return WINED3DERR_INVALIDCALL
;
1185 buffer
->vtbl
= &wined3d_buffer_vtbl
;
1187 hr
= resource_init((IWineD3DResource
*)buffer
, WINED3DRTYPE_BUFFER
,
1188 device
, size
, usage
, format_desc
, pool
, parent
, parent_ops
);
1191 WARN("Failed to initialize resource, hr %#x\n", hr
);
1194 buffer
->buffer_type_hint
= bind_hint
;
1196 TRACE("size %#x, usage %#x, format %s, memory @ %p, iface @ %p.\n", buffer
->resource
.size
, buffer
->resource
.usage
,
1197 debug_d3dformat(buffer
->resource
.format_desc
->format
), buffer
->resource
.allocatedMemory
, buffer
);
1199 /* TODO: GL_ARB_map_buffer_range */
1200 dynamic_buffer_ok
= gl_info
->supported
[APPLE_FLUSH_BUFFER_RANGE
];
1202 /* Observations show that drawStridedSlow is faster on dynamic VBs than converting +
1203 * drawStridedFast (half-life 2 and others).
1205 * Basically converting the vertices in the buffer is quite expensive, and observations
1206 * show that drawStridedSlow is faster than converting + uploading + drawStridedFast.
1207 * Therefore do not create a VBO for WINED3DUSAGE_DYNAMIC buffers.
1209 if (!gl_info
->supported
[ARB_VERTEX_BUFFER_OBJECT
])
1211 TRACE("Not creating a vbo because GL_ARB_vertex_buffer is not supported\n");
1213 else if(buffer
->resource
.pool
== WINED3DPOOL_SYSTEMMEM
)
1215 TRACE("Not creating a vbo because the vertex buffer is in system memory\n");
1217 else if(!dynamic_buffer_ok
&& (buffer
->resource
.usage
& WINED3DUSAGE_DYNAMIC
))
1219 TRACE("Not creating a vbo because the buffer has dynamic usage and no GL support\n");
1223 buffer
->flags
|= WINED3D_BUFFER_CREATEBO
;
1230 hr
= IWineD3DBuffer_Map((IWineD3DBuffer
*)buffer
, 0, size
, &ptr
, 0);
1233 ERR("Failed to map buffer, hr %#x\n", hr
);
1234 buffer_UnLoad((IWineD3DBuffer
*)buffer
);
1235 resource_cleanup((IWineD3DResource
*)buffer
);
1239 memcpy(ptr
, data
, size
);
1241 hr
= IWineD3DBuffer_Unmap((IWineD3DBuffer
*)buffer
);
1244 ERR("Failed to unmap buffer, hr %#x\n", hr
);
1245 buffer_UnLoad((IWineD3DBuffer
*)buffer
);
1246 resource_cleanup((IWineD3DResource
*)buffer
);
1251 buffer
->maps
= HeapAlloc(GetProcessHeap(), 0, sizeof(*buffer
->maps
));
1254 ERR("Out of memory\n");
1255 buffer_UnLoad((IWineD3DBuffer
*)buffer
);
1256 resource_cleanup((IWineD3DResource
*)buffer
);
1257 return E_OUTOFMEMORY
;
1259 buffer
->maps_size
= 1;