wined3d: Use separate structures for ddraw style strided data and wined3d's internal...
[wine/multimedia.git] / dlls / wined3d / buffer.c
blobf11e3e21c53d0160723b87c1f5de9893a87664e6
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 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
25 #include "config.h"
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);
47 ENTER_GL();
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));
64 error = glGetError();
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);
68 goto fail;
71 GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB, This->buffer_object));
72 error = glGetError();
73 if (error != GL_NO_ERROR)
75 ERR("Failed to bind the VBO with error %s (%#x)\n", debug_glerror(error), error);
76 goto fail;
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;
89 break;
91 case WINED3DUSAGE_WRITEONLY:
92 default:
93 TRACE("Gl usage = GL_DYNAMIC_DRAW\n");
94 gl_usage = GL_DYNAMIC_DRAW_ARB;
95 break;
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);
107 goto fail;
110 LEAVE_GL();
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;
118 return;
120 fail:
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;
125 LEAVE_GL();
127 return;
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)
134 DWORD attrib_size;
135 BOOL ret = FALSE;
136 unsigned int i;
137 DWORD offset = This->resource.wineD3DDevice->stateBlock->streamOffset[attrib->stream_idx];
138 DWORD_PTR data;
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.
145 if (!attrib->stride)
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);
154 else
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);
167 ret = TRUE;
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);
179 ret = TRUE;
180 This->conversion_map[data + i] = conversion_type;
184 return ret;
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)
191 BOOL ret = FALSE;
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);
227 return ret;
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;
234 DWORD type;
236 if (!stride)
238 TRACE("No shift\n");
239 return NULL;
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)
251 shift = 4;
253 else if (type == WINED3DDECLTYPE_FLOAT16_4)
255 shift = 8;
256 /* Pre-shift the last 4 bytes in the FLOAT16_4 by 4 bytes - this makes FLOAT16_2 and FLOAT16_4 conversions
257 * compatible
259 for (j = 4; j < 8; ++j)
261 ret[(DWORD_PTR)strided->elements[i].data + j] += 4;
264 else
266 shift = 0;
268 This->conversion_stride += shift;
270 if (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)
275 ret[j] += shift;
280 if (TRACE_ON(d3d))
282 TRACE("Dumping conversion shift:\n");
283 for (i = 0; i < stride; ++i)
285 TRACE("[%d]", ret[i]);
287 TRACE("\n");
290 return ret;
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;
298 BOOL ret = FALSE;
299 unsigned int i;
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:
326 * POSITIONT, FLOAT4
327 * NORMAL, FLOAT3
328 * DIFFUSE, FLOAT16_4
329 * SPECULAR, D3DCOLOR
331 * Will result in
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))
349 TRACE("vshader\n");
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;
362 This->stride = 0;
363 This->conversion_shift = NULL;
364 This->conversion_stride = 0;
365 return TRUE;
367 else
369 return FALSE;
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);
387 else
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)
424 /* Sanity test */
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;
428 This->stride = 0;
431 if (ret) TRACE("Conversion information changed\n");
433 return ret;
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);
443 ENTER_GL();
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");
449 LEAVE_GL();
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
465 *dst_color = 0;
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)
473 float x, y, z, w;
475 /* rhw conversion like in drawStridedSlow */
476 if (p[3] == 1.0 || ((p[3] < eps) && (p[3] > -eps)))
478 x = p[0];
479 y = p[1];
480 z = p[2];
481 w = 1.0;
483 else
485 w = 1.0 / p[3];
486 x = p[0] * w;
487 y = p[1] * w;
488 z = p[2] * w;
490 p[0] = x;
491 p[1] = y;
492 p[2] = z;
493 p[3] = w;
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;
515 else
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);
534 *object = iface;
535 return S_OK;
538 WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid));
540 *object = NULL;
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);
552 return 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);
570 ENTER_GL();
571 GL_EXTCALL(glDeleteBuffersARB(1, &This->buffer_object));
572 checkGLcall("glDeleteBuffersARB");
573 LEAVE_GL();
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);
586 if (!refcount)
588 buffer_UnLoad(iface);
589 resource_cleanup((IWineD3DResource *)iface);
590 HeapFree(GetProcessHeap(), 0, This);
593 return refcount;
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;
643 unsigned int i, j;
644 BYTE *data;
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;
656 else
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)
675 if (decl_changed)
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);
684 ENTER_GL();
685 GL_EXTCALL(glDeleteBuffersARB(1, &This->buffer_object));
686 checkGLcall("glDeleteBuffersARB");
687 LEAVE_GL();
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
694 * rarely
696 IWineD3DDeviceImpl_MarkStateDirty(device, STATE_STREAMSRC);
698 return;
700 buffer_check_buffer_object_size(This);
702 else
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
708 ++This->draw_count;
709 if (This->draw_count > VB_RESETDECLCHANGE) This->conversion_count = 0;
712 if (decl_changed)
714 /* The declaration changed, reload the whole buffer */
715 WARN("Reloading buffer because of decl change\n");
716 start = 0;
717 end = This->resource.size;
719 else
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;
736 This->dirty_end = 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);
750 ENTER_GL();
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");
755 LEAVE_GL();
756 return;
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])
773 case CONV_NONE:
774 data[This->conversion_stride * i + j + This->conversion_shift[j]]
775 = This->resource.allocatedMemory[This->stride * i + j];
776 break;
778 case CONV_FLOAT16_2:
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 */
786 break;
789 default:
790 FIXME("Unimplemented conversion %d in shifted conversion\n", This->conversion_map[j]);
791 break;
796 ENTER_GL();
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");
801 LEAVE_GL();
803 else
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])
813 case CONV_NONE:
814 /* Done already */
815 j += 3;
816 break;
817 case CONV_D3DCOLOR:
818 fixup_d3dcolor((DWORD *) (data + i * This->stride + j));
819 j += 3;
820 break;
822 case CONV_POSITIONT:
823 fixup_transformed_pos((float *) (data + i * This->stride + j));
824 j += 15;
825 break;
827 case CONV_FLOAT16_2:
828 ERR("Did not expect FLOAT16 conversion in unshifted conversion\n");
829 default:
830 FIXME("Unimplemented conversion %d in shifted conversion\n", This->conversion_map[j]);
835 ENTER_GL();
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");
840 LEAVE_GL();
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;
865 if (size)
867 if (This->dirty_end < offset + size) This->dirty_end = offset + size;
869 else
871 This->dirty_end = This->resource.size;
874 else
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) */
888 return WINED3D_OK;
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");
901 return WINED3D_OK;
904 if (This->flags & WINED3D_BUFFER_HASDESC)
906 buffer_PreLoad(iface);
909 return WINED3D_OK;
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;
925 return WINED3D_OK;
928 const struct IWineD3DBufferVtbl wined3d_buffer_vtbl =
930 /* IUnknown methods */
931 buffer_QueryInterface,
932 buffer_AddRef,
933 buffer_Release,
934 /* IWineD3DBase methods */
935 buffer_GetParent,
936 /* IWineD3DResource methods */
937 buffer_GetDevice,
938 buffer_SetPrivateData,
939 buffer_GetPrivateData,
940 buffer_FreePrivateData,
941 buffer_SetPriority,
942 buffer_GetPriority,
943 buffer_PreLoad,
944 buffer_UnLoad,
945 buffer_GetType,
946 /* IWineD3DBuffer methods */
947 buffer_Map,
948 buffer_Unmap,
949 buffer_GetDesc,
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);
965 *object = iface;
966 return S_OK;
969 WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid));
971 *object = NULL;
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);
983 return 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);
993 if (!refcount)
995 if (This->vbo)
997 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
999 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1000 ENTER_GL();
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");
1008 LEAVE_GL();
1011 resource_cleanup((IWineD3DResource *)iface);
1012 HeapFree(GetProcessHeap(), 0, This);
1015 return refcount;
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)
1077 if (This->vbo)
1079 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1080 ENTER_GL();
1081 GL_EXTCALL(glDeleteBuffersARB(1, &This->vbo));
1082 checkGLcall("glDeleteBuffersARB");
1083 LEAVE_GL();
1084 This->vbo = 0;
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)
1107 return WINED3D_OK;
1110 if (This->dirtystart != This->dirtyend)
1112 if (This->dirtystart > OffsetToLock) This->dirtystart = OffsetToLock;
1113 if (SizeToLock)
1115 if (This->dirtyend < OffsetToLock + SizeToLock) This->dirtyend = OffsetToLock + SizeToLock;
1117 else
1119 This->dirtyend = This->resource.size;
1122 else
1124 This->dirtystart = OffsetToLock;
1125 if (SizeToLock) This->dirtyend = OffsetToLock + SizeToLock;
1126 else This->dirtyend = This->resource.size;
1129 return WINED3D_OK;
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);
1146 ENTER_GL();
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");
1152 LEAVE_GL();
1153 This->dirtystart = 0;
1154 This->dirtyend = 0;
1155 /* TODO: Move loading into preload when the buffer is used, that avoids dirtifying the state */
1156 IWineD3DDeviceImpl_MarkStateDirty(device, STATE_INDEXBUFFER);
1159 return WINED3D_OK;
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;
1175 return WINED3D_OK;
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,