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.wineD3DDevice->adapter->gl_info
34 #define VB_MAXDECLCHANGES 100 /* After that number we stop converting */
35 #define VB_RESETDECLCHANGE 1000 /* Reset the changecount after that number of draws */
37 static void buffer_create_buffer_object(struct wined3d_buffer
*This
)
39 GLenum error
, gl_usage
;
40 IWineD3DDeviceImpl
*device
= This
->resource
.wineD3DDevice
;
42 TRACE("Creating an OpenGL vertex buffer object for IWineD3DVertexBuffer %p Usage(%s)\n",
43 This
, debug_d3dusage(This
->resource
.usage
));
45 /* Make sure that a context is there. Needed in a multithreaded environment. Otherwise this call is a nop */
46 ActivateContext(device
, device
->lastActiveRenderTarget
, CTXUSAGE_RESOURCELOAD
);
49 /* Make sure that the gl error is cleared. Do not use checkGLcall
50 * here because checkGLcall just prints a fixme and continues. However,
51 * if an error during VBO creation occurs we can fall back to non-vbo operation
52 * with full functionality(but performance loss)
54 while (glGetError() != GL_NO_ERROR
);
56 /* Basically the FVF parameter passed to CreateVertexBuffer is no good
57 * It is the FVF set with IWineD3DDevice::SetFVF or the Vertex Declaration set with
58 * IWineD3DDevice::SetVertexDeclaration that decides how the vertices in the buffer
59 * look like. This means that on each DrawPrimitive call the vertex buffer has to be verified
60 * to check if the rhw and color values are in the correct format.
63 GL_EXTCALL(glGenBuffersARB(1, &This
->buffer_object
));
65 if (!This
->buffer_object
|| error
!= GL_NO_ERROR
)
67 ERR("Failed to create a VBO with error %s (%#x)\n", debug_glerror(error
), error
);
71 GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB
, This
->buffer_object
));
73 if (error
!= GL_NO_ERROR
)
75 ERR("Failed to bind the VBO with error %s (%#x)\n", debug_glerror(error
), error
);
79 /* Don't use static, because dx apps tend to update the buffer
80 * quite often even if they specify 0 usage. Because we always keep the local copy
81 * we never read from the vbo and can create a write only opengl buffer.
83 switch(This
->resource
.usage
& (WINED3DUSAGE_WRITEONLY
| WINED3DUSAGE_DYNAMIC
))
85 case WINED3DUSAGE_WRITEONLY
| WINED3DUSAGE_DYNAMIC
:
86 case WINED3DUSAGE_DYNAMIC
:
87 TRACE("Gl usage = GL_STREAM_DRAW\n");
88 gl_usage
= GL_STREAM_DRAW_ARB
;
91 case WINED3DUSAGE_WRITEONLY
:
93 TRACE("Gl usage = GL_DYNAMIC_DRAW\n");
94 gl_usage
= GL_DYNAMIC_DRAW_ARB
;
98 /* Reserve memory for the buffer. The amount of data won't change
99 * so we are safe with calling glBufferData once with a NULL ptr and
100 * calling glBufferSubData on updates
102 GL_EXTCALL(glBufferDataARB(GL_ARRAY_BUFFER_ARB
, This
->resource
.size
, NULL
, gl_usage
));
103 error
= glGetError();
104 if (error
!= GL_NO_ERROR
)
106 ERR("glBufferDataARB failed with error %s (%#x)\n", debug_glerror(error
), error
);
112 This
->buffer_object_size
= This
->resource
.size
;
113 This
->buffer_object_usage
= gl_usage
;
114 This
->dirty_start
= 0;
115 This
->dirty_end
= This
->resource
.size
;
116 This
->flags
|= WINED3D_BUFFER_DIRTY
;
121 /* Clean up all vbo init, but continue because we can work without a vbo :-) */
122 ERR("Failed to create a vertex buffer object. Continuing, but performance issues may occur\n");
123 if (This
->buffer_object
) GL_EXTCALL(glDeleteBuffersARB(1, &This
->buffer_object
));
124 This
->buffer_object
= 0;
130 static BOOL
buffer_process_converted_attribute(struct wined3d_buffer
*This
,
131 const enum wined3d_buffer_conversion_type conversion_type
,
132 const struct wined3d_stream_info_element
*attrib
, DWORD
*stride_this_run
, const DWORD type
)
137 DWORD offset
= This
->resource
.wineD3DDevice
->stateBlock
->streamOffset
[attrib
->stream_idx
];
140 /* Check for some valid situations which cause us pain. One is if the buffer is used for
141 * constant attributes(stride = 0), the other one is if the buffer is used on two streams
142 * with different strides. In the 2nd case we might have to drop conversion entirely,
143 * it is possible that the same bytes are once read as FLOAT2 and once as UBYTE4N.
147 FIXME("%s used with stride 0, let's hope we get the vertex stride from somewhere else\n",
148 debug_d3ddecltype(type
));
150 else if(attrib
->stride
!= *stride_this_run
&& *stride_this_run
)
152 FIXME("Got two concurrent strides, %d and %d\n", attrib
->stride
, *stride_this_run
);
156 *stride_this_run
= attrib
->stride
;
157 if (This
->stride
!= *stride_this_run
)
159 /* We rely that this happens only on the first converted attribute that is found,
160 * if at all. See above check
162 TRACE("Reconverting because converted attributes occur, and the stride changed\n");
163 This
->stride
= *stride_this_run
;
164 HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY
, This
->conversion_map
);
165 This
->conversion_map
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
166 sizeof(*This
->conversion_map
) * This
->stride
);
171 data
= (((DWORD_PTR
)attrib
->data
) + offset
) % This
->stride
;
172 attrib_size
= WINED3D_ATR_SIZE(type
) * WINED3D_ATR_TYPESIZE(type
);
173 for (i
= 0; i
< attrib_size
; ++i
)
175 if (This
->conversion_map
[data
+ i
] != conversion_type
)
177 TRACE("Byte %ld in vertex changed\n", i
+ data
);
178 TRACE("It was type %d, is %d now\n", This
->conversion_map
[data
+ i
], conversion_type
);
180 This
->conversion_map
[data
+ i
] = conversion_type
;
187 static BOOL
buffer_check_attribute(struct wined3d_buffer
*This
,
188 const struct wined3d_stream_info_element
*attrib
, const BOOL check_d3dcolor
, const BOOL is_ffp_position
,
189 const BOOL is_ffp_color
, DWORD
*stride_this_run
, BOOL
*float16_used
)
192 WINED3DDECLTYPE type
;
194 /* Ignore attributes that do not have our vbo. After that check we can be sure that the attribute is
195 * there, on nonexistent attribs the vbo is 0.
197 if (attrib
->buffer_object
!= This
->buffer_object
) return FALSE
;
199 type
= attrib
->d3d_type
;
200 /* Look for newly appeared conversion */
201 if (!GL_SUPPORT(NV_HALF_FLOAT
) && (type
== WINED3DDECLTYPE_FLOAT16_2
|| type
== WINED3DDECLTYPE_FLOAT16_4
))
203 ret
= buffer_process_converted_attribute(This
, CONV_FLOAT16_2
, attrib
, stride_this_run
, type
);
205 if (is_ffp_position
) FIXME("Test FLOAT16 fixed function processing positions\n");
206 else if (is_ffp_color
) FIXME("test FLOAT16 fixed function processing colors\n");
207 *float16_used
= TRUE
;
209 else if (check_d3dcolor
&& type
== WINED3DDECLTYPE_D3DCOLOR
)
211 ret
= buffer_process_converted_attribute(This
, CONV_D3DCOLOR
,
212 attrib
, stride_this_run
, WINED3DDECLTYPE_D3DCOLOR
);
214 if (!is_ffp_color
) FIXME("Test for non-color fixed function D3DCOLOR type\n");
216 else if (is_ffp_position
&& type
== WINED3DDECLTYPE_FLOAT4
)
218 ret
= buffer_process_converted_attribute(This
, CONV_POSITIONT
,
219 attrib
, stride_this_run
, WINED3DDECLTYPE_FLOAT4
);
221 else if (This
->conversion_map
)
223 ret
= buffer_process_converted_attribute(This
, CONV_NONE
,
224 attrib
, stride_this_run
, type
);
230 static UINT
*find_conversion_shift(struct wined3d_buffer
*This
,
231 const struct wined3d_stream_info
*strided
, UINT stride
)
233 UINT
*ret
, i
, j
, shift
, orig_type_size
;
242 This
->conversion_stride
= stride
;
243 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(DWORD
) * stride
);
244 for (i
= 0; i
< MAX_ATTRIBS
; ++i
)
246 if (strided
->elements
[i
].buffer_object
!= This
->buffer_object
) continue;
248 type
= strided
->elements
[i
].d3d_type
;
249 if (type
== WINED3DDECLTYPE_FLOAT16_2
)
253 else if (type
== WINED3DDECLTYPE_FLOAT16_4
)
256 /* Pre-shift the last 4 bytes in the FLOAT16_4 by 4 bytes - this makes FLOAT16_2 and FLOAT16_4 conversions
259 for (j
= 4; j
< 8; ++j
)
261 ret
[(DWORD_PTR
)strided
->elements
[i
].data
+ j
] += 4;
268 This
->conversion_stride
+= shift
;
272 orig_type_size
= WINED3D_ATR_TYPESIZE(type
) * WINED3D_ATR_SIZE(type
);
273 for (j
= (DWORD_PTR
)strided
->elements
[i
].data
+ orig_type_size
; j
< stride
; ++j
)
282 TRACE("Dumping conversion shift:\n");
283 for (i
= 0; i
< stride
; ++i
)
285 TRACE("[%d]", ret
[i
]);
293 static BOOL
buffer_find_decl(struct wined3d_buffer
*This
)
295 IWineD3DDeviceImpl
*device
= This
->resource
.wineD3DDevice
;
296 UINT stride_this_run
= 0;
297 BOOL float16_used
= FALSE
;
301 /* In d3d7 the vertex buffer declaration NEVER changes because it is stored in the d3d7 vertex buffer.
302 * Once we have our declaration there is no need to look it up again.
304 if (((IWineD3DImpl
*)device
->wineD3D
)->dxVersion
== 7 && This
->flags
& WINED3D_BUFFER_HASDESC
) return FALSE
;
306 TRACE("Finding vertex buffer conversion information\n");
307 /* Certain declaration types need some fixups before we can pass them to
308 * opengl. This means D3DCOLOR attributes with fixed function vertex
309 * processing, FLOAT4 POSITIONT with fixed function, and FLOAT16 if
310 * GL_NV_half_float is not supported.
312 * The vertex buffer FVF doesn't help with finding them, we have to use
313 * the decoded vertex declaration and pick the things that concern the
314 * current buffer. A problem with this is that this can change between
315 * draws, so we have to validate the information and reprocess the buffer
316 * if it changes, and avoid false positives for performance reasons.
318 * We have to distinguish between vertex shaders and fixed function to
319 * pick the way we access the strided vertex information.
321 * This code sets up a per-byte array with the size of the detected
322 * stride of the arrays in the buffer. For each byte we have a field
323 * that marks the conversion needed on this byte. For example, the
324 * following declaration with fixed function vertex processing:
332 * { POSITIONT }{ NORMAL }{ DIFFUSE }{SPECULAR }
333 * [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]
335 * Where in this example map P means 4 component position conversion, 0
336 * means no conversion, F means FLOAT16_2 conversion and C means D3DCOLOR
337 * conversion (red / blue swizzle).
339 * If we're doing conversion and the stride changes we have to reconvert
340 * the whole buffer. Note that we do not mind if the semantic changes,
341 * we only care for the conversion type. So if the NORMAL is replaced
342 * with a TEXCOORD, nothing has to be done, or if the DIFFUSE is replaced
343 * with a D3DCOLOR BLENDWEIGHT we can happily dismiss the change. Some
344 * conversion types depend on the semantic as well, for example a FLOAT4
345 * texcoord needs no conversion while a FLOAT4 positiont needs one
347 if (use_vs(device
->stateBlock
))
350 /* If the current vertex declaration is marked for no half float conversion don't bother to
351 * analyse the strided streams in depth, just set them up for no conversion. Return decl changed
352 * if we used conversion before
354 if (!((IWineD3DVertexDeclarationImpl
*) device
->stateBlock
->vertexDecl
)->half_float_conv_needed
)
356 if (This
->conversion_map
)
358 TRACE("Now using shaders without conversion, but conversion used before\n");
359 HeapFree(GetProcessHeap(), 0, This
->conversion_map
);
360 HeapFree(GetProcessHeap(), 0, This
->conversion_shift
);
361 This
->conversion_map
= NULL
;
363 This
->conversion_shift
= NULL
;
364 This
->conversion_stride
= 0;
372 for (i
= 0; i
< MAX_ATTRIBS
; ++i
)
374 ret
= buffer_check_attribute(This
, &device
->strided_streams
.elements
[i
],
375 FALSE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
378 /* Recalculate the conversion shift map if the declaration has changed,
379 * and we're using float16 conversion or used it on the last run
381 if (ret
&& (float16_used
|| This
->conversion_map
))
383 HeapFree(GetProcessHeap(), 0, This
->conversion_shift
);
384 This
->conversion_shift
= find_conversion_shift(This
, &device
->strided_streams
, This
->stride
);
389 /* Fixed function is a bit trickier. We have to take care for D3DCOLOR types, FLOAT4 positions and of course
390 * FLOAT16s if not supported. Also, we can't iterate over the array, so use macros to generate code for all
391 * the attributes that our current fixed function pipeline implementation cares for.
393 BOOL support_d3dcolor
= GL_SUPPORT(EXT_VERTEX_ARRAY_BGRA
);
394 ret
= buffer_check_attribute(This
, &device
->strided_streams
.elements
[WINED3D_FFP_POSITION
],
395 TRUE
, TRUE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
396 ret
= buffer_check_attribute(This
, &device
->strided_streams
.elements
[WINED3D_FFP_NORMAL
],
397 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
398 ret
= buffer_check_attribute(This
, &device
->strided_streams
.elements
[WINED3D_FFP_DIFFUSE
],
399 !support_d3dcolor
, FALSE
, TRUE
, &stride_this_run
, &float16_used
) || ret
;
400 ret
= buffer_check_attribute(This
, &device
->strided_streams
.elements
[WINED3D_FFP_SPECULAR
],
401 !support_d3dcolor
, FALSE
, TRUE
, &stride_this_run
, &float16_used
) || ret
;
402 ret
= buffer_check_attribute(This
, &device
->strided_streams
.elements
[WINED3D_FFP_TEXCOORD0
],
403 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
404 ret
= buffer_check_attribute(This
, &device
->strided_streams
.elements
[WINED3D_FFP_TEXCOORD1
],
405 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
406 ret
= buffer_check_attribute(This
, &device
->strided_streams
.elements
[WINED3D_FFP_TEXCOORD2
],
407 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
408 ret
= buffer_check_attribute(This
, &device
->strided_streams
.elements
[WINED3D_FFP_TEXCOORD3
],
409 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
410 ret
= buffer_check_attribute(This
, &device
->strided_streams
.elements
[WINED3D_FFP_TEXCOORD4
],
411 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
412 ret
= buffer_check_attribute(This
, &device
->strided_streams
.elements
[WINED3D_FFP_TEXCOORD5
],
413 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
414 ret
= buffer_check_attribute(This
, &device
->strided_streams
.elements
[WINED3D_FFP_TEXCOORD6
],
415 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
416 ret
= buffer_check_attribute(This
, &device
->strided_streams
.elements
[WINED3D_FFP_TEXCOORD7
],
417 TRUE
, FALSE
, FALSE
, &stride_this_run
, &float16_used
) || ret
;
419 if (float16_used
) FIXME("Float16 conversion used with fixed function vertex processing\n");
422 if (stride_this_run
== 0 && This
->conversion_map
)
425 if (!ret
) ERR("no converted attributes found, old conversion map exists, and no declaration change?\n");
426 HeapFree(GetProcessHeap(), 0, This
->conversion_map
);
427 This
->conversion_map
= NULL
;
431 if (ret
) TRACE("Conversion information changed\n");
436 static void buffer_check_buffer_object_size(struct wined3d_buffer
*This
)
438 UINT size
= This
->conversion_stride
?
439 This
->conversion_stride
* (This
->resource
.size
/ This
->stride
) : This
->resource
.size
;
440 if (This
->buffer_object_size
!= size
)
442 TRACE("Old size %u, creating new size %u\n", This
->buffer_object_size
, size
);
444 GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB
, This
->buffer_object
));
445 checkGLcall("glBindBufferARB");
446 GL_EXTCALL(glBufferDataARB(GL_ARRAY_BUFFER_ARB
, size
, NULL
, This
->buffer_object_usage
));
447 This
->buffer_object_size
= size
;
448 checkGLcall("glBufferDataARB");
453 static inline void fixup_d3dcolor(DWORD
*dst_color
)
455 DWORD src_color
= *dst_color
;
457 /* Color conversion like in drawStridedSlow. watch out for little endianity
458 * If we want that stuff to work on big endian machines too we have to consider more things
460 * 0xff000000: Alpha mask
461 * 0x00ff0000: Blue mask
462 * 0x0000ff00: Green mask
463 * 0x000000ff: Red mask
466 *dst_color
|= (src_color
& 0xff00ff00); /* Alpha Green */
467 *dst_color
|= (src_color
& 0x00ff0000) >> 16; /* Red */
468 *dst_color
|= (src_color
& 0x000000ff) << 16; /* Blue */
471 static inline void fixup_transformed_pos(float *p
)
475 /* rhw conversion like in drawStridedSlow */
476 if (p
[3] == 1.0 || ((p
[3] < eps
) && (p
[3] > -eps
)))
496 const BYTE
*buffer_get_memory(IWineD3DBuffer
*iface
, UINT offset
, GLuint
*buffer_object
)
498 struct wined3d_buffer
*This
= (struct wined3d_buffer
*)iface
;
500 *buffer_object
= This
->buffer_object
;
501 if (!This
->buffer_object
)
503 if (This
->flags
& WINED3D_BUFFER_CREATEBO
)
505 buffer_create_buffer_object(This
);
506 This
->flags
&= ~WINED3D_BUFFER_CREATEBO
;
507 if (This
->buffer_object
)
509 *buffer_object
= This
->buffer_object
;
510 return (const BYTE
*)offset
;
513 return This
->resource
.allocatedMemory
+ offset
;
517 return (const BYTE
*)offset
;
521 /* IUnknown methods */
523 static HRESULT STDMETHODCALLTYPE
buffer_QueryInterface(IWineD3DBuffer
*iface
,
524 REFIID riid
, void **object
)
526 TRACE("iface %p, riid %s, object %p\n", iface
, debugstr_guid(riid
), object
);
528 if (IsEqualGUID(riid
, &IID_IWineD3DBuffer
)
529 || IsEqualGUID(riid
, &IID_IWineD3DResource
)
530 || IsEqualGUID(riid
, &IID_IWineD3DBase
)
531 || IsEqualGUID(riid
, &IID_IUnknown
))
533 IUnknown_AddRef(iface
);
538 WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid
));
542 return E_NOINTERFACE
;
545 static ULONG STDMETHODCALLTYPE
buffer_AddRef(IWineD3DBuffer
*iface
)
547 struct wined3d_buffer
*This
= (struct wined3d_buffer
*)iface
;
548 ULONG refcount
= InterlockedIncrement(&This
->resource
.ref
);
550 TRACE("%p increasing refcount to %u\n", This
, refcount
);
555 static void STDMETHODCALLTYPE
buffer_UnLoad(IWineD3DBuffer
*iface
)
557 struct wined3d_buffer
*This
= (struct wined3d_buffer
*)iface
;
559 TRACE("iface %p\n", iface
);
561 /* This is easy: The whole content is shadowed in This->resource.allocatedMemory,
562 * so we only have to destroy the vbo. Only do it if we have a vbo, which implies
563 * that vbos are supported
565 if (This
->buffer_object
)
567 IWineD3DDeviceImpl
*device
= This
->resource
.wineD3DDevice
;
569 ActivateContext(device
, device
->lastActiveRenderTarget
, CTXUSAGE_RESOURCELOAD
);
571 GL_EXTCALL(glDeleteBuffersARB(1, &This
->buffer_object
));
572 checkGLcall("glDeleteBuffersARB");
574 This
->buffer_object
= 0;
575 This
->flags
|= WINED3D_BUFFER_CREATEBO
; /* Recreate the buffer object next load */
579 static ULONG STDMETHODCALLTYPE
buffer_Release(IWineD3DBuffer
*iface
)
581 struct wined3d_buffer
*This
= (struct wined3d_buffer
*)iface
;
582 ULONG refcount
= InterlockedDecrement(&This
->resource
.ref
);
584 TRACE("%p decreasing refcount to %u\n", This
, refcount
);
588 buffer_UnLoad(iface
);
589 resource_cleanup((IWineD3DResource
*)iface
);
590 HeapFree(GetProcessHeap(), 0, This
);
596 /* IWineD3DBase methods */
598 static HRESULT STDMETHODCALLTYPE
buffer_GetParent(IWineD3DBuffer
*iface
, IUnknown
**parent
)
600 return resource_get_parent((IWineD3DResource
*)iface
, parent
);
603 /* IWineD3DResource methods */
605 static HRESULT STDMETHODCALLTYPE
buffer_GetDevice(IWineD3DBuffer
*iface
, IWineD3DDevice
**device
)
607 return resource_get_device((IWineD3DResource
*)iface
, device
);
610 static HRESULT STDMETHODCALLTYPE
buffer_SetPrivateData(IWineD3DBuffer
*iface
,
611 REFGUID guid
, const void *data
, DWORD data_size
, DWORD flags
)
613 return resource_set_private_data((IWineD3DResource
*)iface
, guid
, data
, data_size
, flags
);
616 static HRESULT STDMETHODCALLTYPE
buffer_GetPrivateData(IWineD3DBuffer
*iface
,
617 REFGUID guid
, void *data
, DWORD
*data_size
)
619 return resource_get_private_data((IWineD3DResource
*)iface
, guid
, data
, data_size
);
622 static HRESULT STDMETHODCALLTYPE
buffer_FreePrivateData(IWineD3DBuffer
*iface
, REFGUID guid
)
624 return resource_free_private_data((IWineD3DResource
*)iface
, guid
);
627 static DWORD STDMETHODCALLTYPE
buffer_SetPriority(IWineD3DBuffer
*iface
, DWORD priority
)
629 return resource_set_priority((IWineD3DResource
*)iface
, priority
);
632 static DWORD STDMETHODCALLTYPE
buffer_GetPriority(IWineD3DBuffer
*iface
)
634 return resource_get_priority((IWineD3DResource
*)iface
);
637 static void STDMETHODCALLTYPE
buffer_PreLoad(IWineD3DBuffer
*iface
)
639 struct wined3d_buffer
*This
= (struct wined3d_buffer
*)iface
;
640 IWineD3DDeviceImpl
*device
= This
->resource
.wineD3DDevice
;
641 UINT start
= 0, end
= 0, vertices
;
642 BOOL decl_changed
= FALSE
;
646 TRACE("iface %p\n", iface
);
648 if (!This
->buffer_object
)
650 /* TODO: Make converting independent from VBOs */
651 if (This
->flags
& WINED3D_BUFFER_CREATEBO
)
653 buffer_create_buffer_object(This
);
654 This
->flags
&= ~WINED3D_BUFFER_CREATEBO
;
658 return; /* Not doing any conversion */
662 /* Reading the declaration makes only sense if the stateblock is finalized and the buffer bound to a stream */
663 if (device
->isInDraw
&& This
->bind_count
> 0)
665 decl_changed
= buffer_find_decl(This
);
666 This
->flags
|= WINED3D_BUFFER_HASDESC
;
669 if (!decl_changed
&& !(This
->flags
& WINED3D_BUFFER_HASDESC
&& This
->flags
& WINED3D_BUFFER_DIRTY
)) return;
671 /* If applications change the declaration over and over, reconverting all the time is a huge
672 * performance hit. So count the declaration changes and release the VBO if there are too many
673 * of them (and thus stop converting)
677 ++This
->conversion_count
;
678 This
->draw_count
= 0;
680 if (This
->conversion_count
> VB_MAXDECLCHANGES
)
682 FIXME("Too many declaration changes, stopping converting\n");
683 ActivateContext(device
, device
->lastActiveRenderTarget
, CTXUSAGE_RESOURCELOAD
);
685 GL_EXTCALL(glDeleteBuffersARB(1, &This
->buffer_object
));
686 checkGLcall("glDeleteBuffersARB");
688 This
->buffer_object
= 0;
689 HeapFree(GetProcessHeap(), 0, This
->conversion_shift
);
691 /* The stream source state handler might have read the memory of the vertex buffer already
692 * and got the memory in the vbo which is not valid any longer. Dirtify the stream source
693 * to force a reload. This happens only once per changed vertexbuffer and should occur rather
696 IWineD3DDeviceImpl_MarkStateDirty(device
, STATE_STREAMSRC
);
700 buffer_check_buffer_object_size(This
);
704 /* However, it is perfectly fine to change the declaration every now and then. We don't want a game that
705 * changes it every minute drop the VBO after VB_MAX_DECL_CHANGES minutes. So count draws without
706 * decl changes and reset the decl change count after a specific number of them
709 if (This
->draw_count
> VB_RESETDECLCHANGE
) This
->conversion_count
= 0;
714 /* The declaration changed, reload the whole buffer */
715 WARN("Reloading buffer because of decl change\n");
717 end
= This
->resource
.size
;
721 /* No decl change, but dirty data, reload the changed stuff */
722 if (This
->conversion_shift
)
724 if (This
->dirty_start
!= 0 || This
->dirty_end
!= 0)
726 FIXME("Implement partial buffer loading with shifted conversion\n");
729 start
= This
->dirty_start
;
730 end
= This
->dirty_end
;
733 /* Mark the buffer clean */
734 This
->flags
&= ~WINED3D_BUFFER_DIRTY
;
735 This
->dirty_start
= 0;
738 if (!This
->conversion_map
)
740 /* That means that there is nothing to fixup. Just upload from This->resource.allocatedMemory
741 * directly into the vbo. Do not free the system memory copy because drawPrimitive may need it if
742 * the stride is 0, for instancing emulation, vertex blending emulation or shader emulation.
744 TRACE("No conversion needed\n");
746 if (!device
->isInDraw
)
748 ActivateContext(device
, device
->lastActiveRenderTarget
, CTXUSAGE_RESOURCELOAD
);
751 GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB
, This
->buffer_object
));
752 checkGLcall("glBindBufferARB");
753 GL_EXTCALL(glBufferSubDataARB(GL_ARRAY_BUFFER_ARB
, start
, end
-start
, This
->resource
.allocatedMemory
+ start
));
754 checkGLcall("glBufferSubDataARB");
759 /* Now for each vertex in the buffer that needs conversion */
760 vertices
= This
->resource
.size
/ This
->stride
;
762 if (This
->conversion_shift
)
764 TRACE("Shifted conversion\n");
765 data
= HeapAlloc(GetProcessHeap(), 0, vertices
* This
->conversion_stride
);
767 for (i
= start
/ This
->stride
; i
< min((end
/ This
->stride
) + 1, vertices
); ++i
)
769 for (j
= 0; j
< This
->stride
; ++j
)
771 switch(This
->conversion_map
[j
])
774 data
[This
->conversion_stride
* i
+ j
+ This
->conversion_shift
[j
]]
775 = This
->resource
.allocatedMemory
[This
->stride
* i
+ j
];
780 float *out
= (float *)(&data
[This
->conversion_stride
* i
+ j
+ This
->conversion_shift
[j
]]);
781 const WORD
*in
= (WORD
*)(&This
->resource
.allocatedMemory
[i
* This
->stride
+ j
]);
783 out
[1] = float_16_to_32(in
+ 1);
784 out
[0] = float_16_to_32(in
+ 0);
785 j
+= 3; /* Skip 3 additional bytes,as a FLOAT16_2 has 4 bytes */
790 FIXME("Unimplemented conversion %d in shifted conversion\n", This
->conversion_map
[j
]);
797 GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB
, This
->buffer_object
));
798 checkGLcall("glBindBufferARB");
799 GL_EXTCALL(glBufferSubDataARB(GL_ARRAY_BUFFER_ARB
, 0, vertices
* This
->conversion_stride
, data
));
800 checkGLcall("glBufferSubDataARB");
805 data
= HeapAlloc(GetProcessHeap(), 0, This
->resource
.size
);
806 memcpy(data
+ start
, This
->resource
.allocatedMemory
+ start
, end
- start
);
807 for (i
= start
/ This
->stride
; i
< min((end
/ This
->stride
) + 1, vertices
); ++i
)
809 for (j
= 0; j
< This
->stride
; ++j
)
811 switch(This
->conversion_map
[j
])
818 fixup_d3dcolor((DWORD
*) (data
+ i
* This
->stride
+ j
));
823 fixup_transformed_pos((float *) (data
+ i
* This
->stride
+ j
));
828 ERR("Did not expect FLOAT16 conversion in unshifted conversion\n");
830 FIXME("Unimplemented conversion %d in shifted conversion\n", This
->conversion_map
[j
]);
836 GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB
, This
->buffer_object
));
837 checkGLcall("glBindBufferARB");
838 GL_EXTCALL(glBufferSubDataARB(GL_ARRAY_BUFFER_ARB
, start
, end
- start
, data
+ start
));
839 checkGLcall("glBufferSubDataARB");
843 HeapFree(GetProcessHeap(), 0, data
);
846 static WINED3DRESOURCETYPE STDMETHODCALLTYPE
buffer_GetType(IWineD3DBuffer
*iface
)
848 return resource_get_type((IWineD3DResource
*)iface
);
851 /* IWineD3DBuffer methods */
853 static HRESULT STDMETHODCALLTYPE
buffer_Map(IWineD3DBuffer
*iface
, UINT offset
, UINT size
, BYTE
**data
, DWORD flags
)
855 struct wined3d_buffer
*This
= (struct wined3d_buffer
*)iface
;
857 TRACE("iface %p, offset %u, size %u, data %p, flags %#x\n", iface
, offset
, size
, data
, flags
);
859 InterlockedIncrement(&This
->lock_count
);
861 if (This
->flags
& WINED3D_BUFFER_DIRTY
)
863 if (This
->dirty_start
> offset
) This
->dirty_start
= offset
;
867 if (This
->dirty_end
< offset
+ size
) This
->dirty_end
= offset
+ size
;
871 This
->dirty_end
= This
->resource
.size
;
876 This
->dirty_start
= offset
;
877 if (size
) This
->dirty_end
= offset
+ size
;
878 else This
->dirty_end
= This
->resource
.size
;
881 This
->flags
|= WINED3D_BUFFER_DIRTY
;
883 *data
= This
->resource
.allocatedMemory
+ offset
;
885 TRACE("Returning memory at %p (base %p, offset %u)\n", *data
, This
->resource
.allocatedMemory
, offset
);
886 /* TODO: check Flags compatibility with This->currentDesc.Usage (see MSDN) */
891 static HRESULT STDMETHODCALLTYPE
buffer_Unmap(IWineD3DBuffer
*iface
)
893 struct wined3d_buffer
*This
= (struct wined3d_buffer
*)iface
;
895 TRACE("(%p)\n", This
);
897 if (InterlockedDecrement(&This
->lock_count
))
899 /* Delay loading the buffer until everything is unlocked */
900 TRACE("Ignoring unlock\n");
904 if (This
->flags
& WINED3D_BUFFER_HASDESC
)
906 buffer_PreLoad(iface
);
912 static HRESULT STDMETHODCALLTYPE
buffer_GetDesc(IWineD3DBuffer
*iface
, WINED3DVERTEXBUFFER_DESC
*desc
)
914 struct wined3d_buffer
*This
= (struct wined3d_buffer
*)iface
;
916 TRACE("(%p)\n", This
);
918 desc
->Format
= This
->resource
.format_desc
->format
;
919 desc
->Type
= This
->resource
.resourceType
;
920 desc
->Usage
= This
->resource
.usage
;
921 desc
->Pool
= This
->resource
.pool
;
922 desc
->Size
= This
->resource
.size
;
923 desc
->FVF
= This
->fvf
;
928 const struct IWineD3DBufferVtbl wined3d_buffer_vtbl
=
930 /* IUnknown methods */
931 buffer_QueryInterface
,
934 /* IWineD3DBase methods */
936 /* IWineD3DResource methods */
938 buffer_SetPrivateData
,
939 buffer_GetPrivateData
,
940 buffer_FreePrivateData
,
946 /* IWineD3DBuffer methods */
952 /* IUnknown methods */
954 static HRESULT STDMETHODCALLTYPE
IWineD3DIndexBufferImpl_QueryInterface(IWineD3DIndexBuffer
*iface
,
955 REFIID riid
, void **object
)
957 TRACE("iface %p, riid %s, object %p\n", iface
, debugstr_guid(riid
), object
);
959 if (IsEqualGUID(riid
, &IID_IWineD3DIndexBuffer
)
960 || IsEqualGUID(riid
, &IID_IWineD3DResource
)
961 || IsEqualGUID(riid
, &IID_IWineD3DBase
)
962 || IsEqualGUID(riid
, &IID_IUnknown
))
964 IUnknown_AddRef(iface
);
969 WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid
));
973 return E_NOINTERFACE
;
976 static ULONG STDMETHODCALLTYPE
IWineD3DIndexBufferImpl_AddRef(IWineD3DIndexBuffer
*iface
)
978 IWineD3DIndexBufferImpl
*This
= (IWineD3DIndexBufferImpl
*)iface
;
979 ULONG refcount
= InterlockedIncrement(&This
->resource
.ref
);
981 TRACE("%p increasing refcount to %u\n", This
, refcount
);
986 static ULONG STDMETHODCALLTYPE
IWineD3DIndexBufferImpl_Release(IWineD3DIndexBuffer
*iface
)
988 IWineD3DIndexBufferImpl
*This
= (IWineD3DIndexBufferImpl
*)iface
;
989 ULONG refcount
= InterlockedDecrement(&This
->resource
.ref
);
991 TRACE("%p decreasing refcount to %u\n", This
, refcount
);
997 IWineD3DDeviceImpl
*device
= This
->resource
.wineD3DDevice
;
999 ActivateContext(device
, device
->lastActiveRenderTarget
, CTXUSAGE_RESOURCELOAD
);
1001 /* No need to manually unset the buffer. glDeleteBuffers unsets it for the current context,
1002 * but not for other contexts. However, because the d3d buffer is destroyed the app has to
1003 * unset it before doing the next draw, thus dirtifying the index buffer state and forcing
1004 * binding a new buffer
1006 GL_EXTCALL(glDeleteBuffersARB(1, &This
->vbo
));
1007 checkGLcall("glDeleteBuffersARB");
1011 resource_cleanup((IWineD3DResource
*)iface
);
1012 HeapFree(GetProcessHeap(), 0, This
);
1018 /* IWineD3DBase methods */
1020 static HRESULT STDMETHODCALLTYPE
IWineD3DIndexBufferImpl_GetParent(IWineD3DIndexBuffer
*iface
, IUnknown
**parent
)
1022 return resource_get_parent((IWineD3DResource
*)iface
, parent
);
1025 /* IWineD3DResource methods */
1027 static HRESULT STDMETHODCALLTYPE
IWineD3DIndexBufferImpl_GetDevice(IWineD3DIndexBuffer
*iface
, IWineD3DDevice
**device
)
1029 return resource_get_device((IWineD3DResource
*)iface
, device
);
1032 static HRESULT STDMETHODCALLTYPE
IWineD3DIndexBufferImpl_SetPrivateData(IWineD3DIndexBuffer
*iface
,
1033 REFGUID guid
, const void *data
, DWORD data_size
, DWORD flags
)
1035 return resource_set_private_data((IWineD3DResource
*)iface
, guid
, data
, data_size
, flags
);
1038 static HRESULT STDMETHODCALLTYPE
IWineD3DIndexBufferImpl_GetPrivateData(IWineD3DIndexBuffer
*iface
,
1039 REFGUID guid
, void *data
, DWORD
*data_size
)
1041 return resource_get_private_data((IWineD3DResource
*)iface
, guid
, data
, data_size
);
1044 static HRESULT STDMETHODCALLTYPE
IWineD3DIndexBufferImpl_FreePrivateData(IWineD3DIndexBuffer
*iface
, REFGUID guid
)
1046 return resource_free_private_data((IWineD3DResource
*)iface
, guid
);
1049 static DWORD STDMETHODCALLTYPE
IWineD3DIndexBufferImpl_SetPriority(IWineD3DIndexBuffer
*iface
, DWORD priority
)
1051 return resource_set_priority((IWineD3DResource
*)iface
, priority
);
1054 static DWORD STDMETHODCALLTYPE
IWineD3DIndexBufferImpl_GetPriority(IWineD3DIndexBuffer
*iface
)
1056 return resource_get_priority((IWineD3DResource
*)iface
);
1059 static void STDMETHODCALLTYPE
IWineD3DIndexBufferImpl_PreLoad(IWineD3DIndexBuffer
*iface
)
1061 TRACE("iface %p\n", iface
);
1064 static void STDMETHODCALLTYPE
IWineD3DIndexBufferImpl_UnLoad(IWineD3DIndexBuffer
*iface
)
1066 IWineD3DIndexBufferImpl
*This
= (IWineD3DIndexBufferImpl
*) iface
;
1067 IWineD3DDeviceImpl
*device
= This
->resource
.wineD3DDevice
;
1069 TRACE("(%p)\n", This
);
1071 /* This is easy: The whole content is shadowed in This->resource.allocatedMemory,
1072 * so we only have to destroy the vbo. Only do it if we have a vbo, which implies
1073 * that vbos are supported.
1074 * (TODO: Make a IWineD3DBuffer base class for index and vertex buffers and share
1075 * this code. Also needed for D3D10)
1079 ActivateContext(device
, device
->lastActiveRenderTarget
, CTXUSAGE_RESOURCELOAD
);
1081 GL_EXTCALL(glDeleteBuffersARB(1, &This
->vbo
));
1082 checkGLcall("glDeleteBuffersARB");
1088 static WINED3DRESOURCETYPE STDMETHODCALLTYPE
IWineD3DIndexBufferImpl_GetType(IWineD3DIndexBuffer
*iface
)
1090 return resource_get_type((IWineD3DResource
*)iface
);
1093 /* IWineD3DIndexBuffer methods */
1095 static HRESULT STDMETHODCALLTYPE
IWineD3DIndexBufferImpl_Lock(IWineD3DIndexBuffer
*iface
,
1096 UINT OffsetToLock
, UINT SizeToLock
, BYTE
**ppbData
, DWORD Flags
)
1098 IWineD3DIndexBufferImpl
*This
= (IWineD3DIndexBufferImpl
*)iface
;
1100 TRACE("(%p) : offset %d, size %d, Flags=%x\n", This
, OffsetToLock
, SizeToLock
, Flags
);
1102 InterlockedIncrement(&This
->lockcount
);
1103 *ppbData
= This
->resource
.allocatedMemory
+ OffsetToLock
;
1105 if (Flags
& (WINED3DLOCK_READONLY
| WINED3DLOCK_NO_DIRTY_UPDATE
) || !This
->vbo
)
1110 if (This
->dirtystart
!= This
->dirtyend
)
1112 if (This
->dirtystart
> OffsetToLock
) This
->dirtystart
= OffsetToLock
;
1115 if (This
->dirtyend
< OffsetToLock
+ SizeToLock
) This
->dirtyend
= OffsetToLock
+ SizeToLock
;
1119 This
->dirtyend
= This
->resource
.size
;
1124 This
->dirtystart
= OffsetToLock
;
1125 if (SizeToLock
) This
->dirtyend
= OffsetToLock
+ SizeToLock
;
1126 else This
->dirtyend
= This
->resource
.size
;
1132 static HRESULT STDMETHODCALLTYPE
IWineD3DIndexBufferImpl_Unlock(IWineD3DIndexBuffer
*iface
)
1134 IWineD3DIndexBufferImpl
*This
= (IWineD3DIndexBufferImpl
*)iface
;
1135 ULONG locks
= InterlockedDecrement(&This
->lockcount
);
1137 TRACE("(%p)\n", This
);
1139 /* For now load in unlock */
1140 if (!locks
&& This
->vbo
&& (This
->dirtyend
- This
->dirtystart
) > 0)
1142 IWineD3DDeviceImpl
*device
= This
->resource
.wineD3DDevice
;
1144 ActivateContext(device
, device
->lastActiveRenderTarget
, CTXUSAGE_RESOURCELOAD
);
1147 GL_EXTCALL(glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB
, This
->vbo
));
1148 checkGLcall("glBindBufferARB");
1149 GL_EXTCALL(glBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB
, This
->dirtystart
,
1150 This
->dirtyend
- This
->dirtystart
, This
->resource
.allocatedMemory
+ This
->dirtystart
));
1151 checkGLcall("glBufferSubDataARB");
1153 This
->dirtystart
= 0;
1155 /* TODO: Move loading into preload when the buffer is used, that avoids dirtifying the state */
1156 IWineD3DDeviceImpl_MarkStateDirty(device
, STATE_INDEXBUFFER
);
1162 static HRESULT STDMETHODCALLTYPE
IWineD3DIndexBufferImpl_GetDesc(IWineD3DIndexBuffer
*iface
,
1163 WINED3DINDEXBUFFER_DESC
*pDesc
)
1165 IWineD3DIndexBufferImpl
*This
= (IWineD3DIndexBufferImpl
*)iface
;
1167 TRACE("(%p)\n", This
);
1169 pDesc
->Format
= This
->resource
.format_desc
->format
;
1170 pDesc
->Type
= This
->resource
.resourceType
;
1171 pDesc
->Usage
= This
->resource
.usage
;
1172 pDesc
->Pool
= This
->resource
.pool
;
1173 pDesc
->Size
= This
->resource
.size
;
1178 const IWineD3DIndexBufferVtbl IWineD3DIndexBuffer_Vtbl
=
1180 /* IUnknown methods */
1181 IWineD3DIndexBufferImpl_QueryInterface
,
1182 IWineD3DIndexBufferImpl_AddRef
,
1183 IWineD3DIndexBufferImpl_Release
,
1184 /* IWineD3DBase methods */
1185 IWineD3DIndexBufferImpl_GetParent
,
1186 /* IWineD3DResource methods */
1187 IWineD3DIndexBufferImpl_GetDevice
,
1188 IWineD3DIndexBufferImpl_SetPrivateData
,
1189 IWineD3DIndexBufferImpl_GetPrivateData
,
1190 IWineD3DIndexBufferImpl_FreePrivateData
,
1191 IWineD3DIndexBufferImpl_SetPriority
,
1192 IWineD3DIndexBufferImpl_GetPriority
,
1193 IWineD3DIndexBufferImpl_PreLoad
,
1194 IWineD3DIndexBufferImpl_UnLoad
,
1195 IWineD3DIndexBufferImpl_GetType
,
1196 /* IWineD3DIndexBuffer methods */
1197 IWineD3DIndexBufferImpl_Lock
,
1198 IWineD3DIndexBufferImpl_Unlock
,
1199 IWineD3DIndexBufferImpl_GetDesc
,