wined3d: Implement half float vertex buffer conversion.
[wine.git] / dlls / wined3d / vertexbuffer.c
blob32d475741d3cbf71d51739f1321a60c8bc6be3ec
1 /*
2 * IWineD3DVertexBuffer Implementation
4 * Copyright 2002-2005 Jason Edmeades
5 * Raphael Junqueira
6 * Copyright 2004 Christian Costa
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "config.h"
24 #include "wined3d_private.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
27 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
29 #define VB_MAXDECLCHANGES 100 /* After that number we stop converting */
30 #define VB_RESETDECLCHANGE 1000 /* Reset the changecount after that number of draws */
32 /* *******************************************
33 IWineD3DVertexBuffer IUnknown parts follow
34 ******************************************* */
35 static HRESULT WINAPI IWineD3DVertexBufferImpl_QueryInterface(IWineD3DVertexBuffer *iface, REFIID riid, LPVOID *ppobj)
37 IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *)iface;
38 TRACE("(%p)->(%s,%p)\n",This,debugstr_guid(riid),ppobj);
39 if (IsEqualGUID(riid, &IID_IUnknown)
40 || IsEqualGUID(riid, &IID_IWineD3DBase)
41 || IsEqualGUID(riid, &IID_IWineD3DResource)
42 || IsEqualGUID(riid, &IID_IWineD3DVertexBuffer)){
43 IUnknown_AddRef(iface);
44 *ppobj = This;
45 return S_OK;
47 *ppobj = NULL;
48 return E_NOINTERFACE;
51 static ULONG WINAPI IWineD3DVertexBufferImpl_AddRef(IWineD3DVertexBuffer *iface) {
52 IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *)iface;
53 ULONG ref = InterlockedIncrement(&This->resource.ref);
54 TRACE("(%p) : AddRef increasing from %d\n", This, ref - 1);
55 return ref;
58 static ULONG WINAPI IWineD3DVertexBufferImpl_Release(IWineD3DVertexBuffer *iface) {
59 IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *)iface;
60 ULONG ref = InterlockedDecrement(&This->resource.ref);
61 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
62 if (ref == 0) {
64 if(This->vbo) {
65 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
67 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
68 ENTER_GL();
69 GL_EXTCALL(glDeleteBuffersARB(1, &This->vbo));
70 checkGLcall("glDeleteBuffersARB");
71 LEAVE_GL();
74 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
75 HeapFree(GetProcessHeap(), 0, This);
77 return ref;
80 /* ****************************************************
81 IWineD3DVertexBuffer IWineD3DResource parts follow
82 **************************************************** */
83 static HRESULT WINAPI IWineD3DVertexBufferImpl_GetDevice(IWineD3DVertexBuffer *iface, IWineD3DDevice** ppDevice) {
84 return IWineD3DResourceImpl_GetDevice((IWineD3DResource *)iface, ppDevice);
87 static HRESULT WINAPI IWineD3DVertexBufferImpl_SetPrivateData(IWineD3DVertexBuffer *iface, REFGUID refguid, CONST void* pData, DWORD SizeOfData, DWORD Flags) {
88 return IWineD3DResourceImpl_SetPrivateData((IWineD3DResource *)iface, refguid, pData, SizeOfData, Flags);
91 static HRESULT WINAPI IWineD3DVertexBufferImpl_GetPrivateData(IWineD3DVertexBuffer *iface, REFGUID refguid, void* pData, DWORD* pSizeOfData) {
92 return IWineD3DResourceImpl_GetPrivateData((IWineD3DResource *)iface, refguid, pData, pSizeOfData);
95 static HRESULT WINAPI IWineD3DVertexBufferImpl_FreePrivateData(IWineD3DVertexBuffer *iface, REFGUID refguid) {
96 return IWineD3DResourceImpl_FreePrivateData((IWineD3DResource *)iface, refguid);
99 static DWORD WINAPI IWineD3DVertexBufferImpl_SetPriority(IWineD3DVertexBuffer *iface, DWORD PriorityNew) {
100 return IWineD3DResourceImpl_SetPriority((IWineD3DResource *)iface, PriorityNew);
103 static DWORD WINAPI IWineD3DVertexBufferImpl_GetPriority(IWineD3DVertexBuffer *iface) {
104 return IWineD3DResourceImpl_GetPriority((IWineD3DResource *)iface);
107 static inline void fixup_d3dcolor(DWORD *pos) {
108 DWORD srcColor = *pos;
110 /* Color conversion like in drawStridedSlow. watch out for little endianity
111 * If we want that stuff to work on big endian machines too we have to consider more things
113 * 0xff000000: Alpha mask
114 * 0x00ff0000: Blue mask
115 * 0x0000ff00: Green mask
116 * 0x000000ff: Red mask
118 *pos = 0;
119 *pos |= (srcColor & 0xff00ff00) ; /* Alpha Green */
120 *pos |= (srcColor & 0x00ff0000) >> 16; /* Red */
121 *pos |= (srcColor & 0x000000ff) << 16; /* Blue */
124 static inline void fixup_transformed_pos(float *p) {
125 float x, y, z, w;
127 /* rhw conversion like in drawStridedSlow */
128 if(p[3] == 1.0 || ((p[3] < eps) && (p[3] > -eps))) {
129 x = p[0];
130 y = p[1];
131 z = p[2];
132 w = 1.0;
133 } else {
134 w = 1.0 / p[3];
135 x = p[0] * w;
136 y = p[1] * w;
137 z = p[2] * w;
139 p[0] = x;
140 p[1] = y;
141 p[2] = z;
142 p[3] = w;
145 DWORD *find_conversion_shift(IWineD3DVertexBufferImpl *This, WineDirect3DVertexStridedData *strided, DWORD stride) {
146 DWORD *ret, i, shift, j, type;
147 DWORD orig_type_size;
149 if(!stride) {
150 TRACE("No shift\n");
151 return NULL;
154 This->conv_stride = stride;
155 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DWORD) * stride);
156 for(i = 0; i < MAX_ATTRIBS; i++) {
157 type = strided->u.input[i].dwType;
158 if(type == WINED3DDECLTYPE_FLOAT16_2) {
159 shift = 4;
160 } else if(type == WINED3DDECLTYPE_FLOAT16_4) {
161 shift = 8;
162 } else {
163 shift = 0;
165 This->conv_stride += shift;
167 if(shift) {
168 orig_type_size = WINED3D_ATR_TYPESIZE(type) * WINED3D_ATR_SIZE(type);
169 for(j = (DWORD_PTR) strided->u.input[i].lpData + orig_type_size; j < stride; j++) {
170 ret[j] += shift;
175 if(TRACE_ON(d3d)) {
176 TRACE("Dumping conversion shift:\n");
177 for(i = 0; i < stride; i++) {
178 TRACE("[%d]", ret[i]);
180 TRACE("\n");
182 return ret;
185 inline BOOL WINAPI IWineD3DVertexBufferImpl_FindDecl(IWineD3DVertexBufferImpl *This)
187 WineDirect3DVertexStridedData strided;
188 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
189 BOOL ret = FALSE;
190 DWORD type_old, type_new, stride;
191 int i;
193 /* In d3d7 the vertex buffer declaration NEVER changes because it is stored in the d3d7 vertex buffer.
194 * Once we have our declaration there is no need to look it up again.
196 if(((IWineD3DImpl *)device->wineD3D)->dxVersion == 7 && This->Flags & VBFLAG_HASDESC) {
197 return FALSE;
200 /* There are certain vertex data types that need to be fixed up. The Vertex Buffers FVF doesn't
201 * help finding them, only the vertex declaration or the device FVF can determine that at drawPrim
202 * time. Rules are as follows:
204 * -> Fix up position1 and position 2 if they are XYZRHW
205 * -> Fix up types that are D3DDECLTYPE_D3DCOLOR if no shader is used
206 * -> TODO: Convert FLOAT16 to FLOAT32 if not supported natively by gl
208 * The Declaration is only known at drawing time, and it can change from draw to draw. If any converted values
209 * are changed, the whole buffer has to be reconverted and reloaded. (Converting is SLOW, so if this happens too
210 * often PreLoad stops converting entirely and falls back to drawStridedSlow).
212 * Reconvert if:
213 * -> New semantics that have to be converted appear
214 * -> The position of semantics that have to be converted changes
215 * -> The stride of the vertex changed AND there is stuff that needs conversion
216 * -> (If a vertex shader is bound and in use assume that nothing that needs conversion is there)
218 * Return values:
219 * TRUE: Reload is needed
220 * FALSE: otherwise
223 if (use_vs(device)) {
224 if(!This->last_was_vshader && This->last_was_converted) {
225 /* Reload if we're switching from converted fixed function to vertex shaders.
226 * This isn't strictly needed, e.g. a FLOAT16 attribute could stay at the same
227 * place. It is always needed for the FLOAT4 and D3DCOLOR conversions however, and
228 * it is unlikely that any game uses FLOAT16s with fixed function vertex processing.
230 TRACE("Reconverting because switching from converted fixed function drawing to shaders\n");
231 ret = TRUE;
233 This->last_was_vshader = TRUE;
235 /* The vertex declaration is so nice to carry a flag for this. It takes the gl info into account,
236 * but check that separately to avoid the memcmp over the structure
238 if(GL_SUPPORT(NV_HALF_FLOAT)) {
239 memset(&strided, 0, sizeof(strided));
240 ret = FALSE;
241 } else if(((IWineD3DVertexDeclarationImpl *)device->stateBlock->vertexDecl)->half_float_conv_needed) {
242 memcpy(&strided, &device->strided_streams, sizeof(strided));
244 stride = 0;
245 for(i = 0; i < MAX_ATTRIBS; i++) {
246 if(strided.u.input[i].VBO != This->vbo) {
247 /* Ignore attributes from a different vbo */
248 memset(&strided.u.input[i], 0, sizeof(strided.u.input[i]));
249 continue;
252 type_old = This->strided.u.input[i].dwType;
253 type_new = strided.u.input[i].dwType;
254 if(type_old != type_new) {
255 if(type_old == WINED3DDECLTYPE_FLOAT16_2 || type_new == WINED3DDECLTYPE_FLOAT16_2 ||
256 type_old == WINED3DDECLTYPE_FLOAT16_4 || type_new == WINED3DDECLTYPE_FLOAT16_4) {
257 TRACE("Reloading because attribute %i was %s before and is %s now\n", i,
258 debug_d3ddecltype(type_old), debug_d3ddecltype(type_new));
259 stride = strided.u.input[i].dwStride;
260 ret = TRUE;
262 } else if(type_new == WINED3DDECLTYPE_FLOAT16_2 || type_new == WINED3DDECLTYPE_FLOAT16_4) {
263 if(This->strided.u.input[i].lpData != strided.u.input[i].lpData) {
264 TRACE("Reconverting buffer because attribute %d has type %s and moved from offset %p to %p\n",
265 i, debug_d3ddecltype(type_new), This->strided.u.s.position.lpData, strided.u.s.position.lpData);
266 memcpy(&This->strided, &strided, sizeof(strided));
267 ret = TRUE;
268 stride = strided.u.input[i].dwStride;
272 if(!(type_new == WINED3DDECLTYPE_FLOAT16_2 || type_new == WINED3DDECLTYPE_FLOAT16_4)) {
273 /* Nuke unconverted attributes */
274 memset(&strided.u.input[i], 0, sizeof(strided.u.input[i]));
277 if(ret) {
278 memcpy(&This->strided, &strided, sizeof(strided));
279 HeapFree(GetProcessHeap(), 0, This->conv_shift);
280 This->conv_shift = find_conversion_shift(This, &This->strided, stride);
282 } else {
283 /* No conversion*/
284 memset(&strided, 0, sizeof(strided));
285 ret = (memcmp(&strided, &This->strided, sizeof(strided)) != 0);
286 if(ret) {
287 HeapFree(GetProcessHeap(), 0, This->conv_shift);
288 This->conv_shift = NULL;
291 } else {
292 /* This will need modifications of the loading code as well - not handled right now */
293 if(((IWineD3DVertexDeclarationImpl *)device->stateBlock->vertexDecl)->half_float_conv_needed) {
294 FIXME("Implement half float fixup with fixed function vertex processing\n");
297 /* we need a copy because we modify some params */
298 memcpy(&strided, &device->strided_streams, sizeof(strided));
300 /* Kill things that does not exist in our fixed function pipeline implementation */
301 memset(&strided.u.s.blendWeights, 0, sizeof(strided.u.s.blendWeights));
302 memset(&strided.u.s.blendMatrixIndices, 0, sizeof(strided.u.s.blendMatrixIndices));
303 memset(&strided.u.s.pSize, 0, sizeof(strided.u.s.pSize));
304 memset(&strided.u.s.position2, 0, sizeof(strided.u.s.position2));
305 memset(&strided.u.s.normal2, 0, sizeof(strided.u.s.normal2));
306 memset(&strided.u.s.tangent, 0, sizeof(strided.u.s.tangent));
307 memset(&strided.u.s.binormal, 0, sizeof(strided.u.s.binormal));
308 memset(&strided.u.s.tessFactor, 0, sizeof(strided.u.s.tessFactor));
309 memset(&strided.u.s.fog, 0, sizeof(strided.u.s.fog));
310 memset(&strided.u.s.depth, 0, sizeof(strided.u.s.depth));
311 memset(&strided.u.s.sample, 0, sizeof(strided.u.s.sample));
313 /* Filter out data that does not come from this VBO. No need to repeat that on
314 * the attributes filtered above.
316 #define FILTER_OTHER_VBO(name) if(strided.u.s.name.VBO != This->vbo) {memset(&strided.u.s.name, 0, sizeof(strided.u.s.name));}
317 FILTER_OTHER_VBO(position);
318 /*FILTER_OTHER_VBO(blendWeights);*/
319 /*FILTER_OTHER_VBO(blendMatrixIndices);*/
320 FILTER_OTHER_VBO(normal);
321 /*FILTER_OTHER_VBO(pSize);*/
322 FILTER_OTHER_VBO(diffuse);
323 FILTER_OTHER_VBO(specular);
324 FILTER_OTHER_VBO(texCoords[0]);
325 FILTER_OTHER_VBO(texCoords[1]);
326 FILTER_OTHER_VBO(texCoords[2]);
327 FILTER_OTHER_VBO(texCoords[3]);
328 FILTER_OTHER_VBO(texCoords[4]);
329 FILTER_OTHER_VBO(texCoords[5]);
330 FILTER_OTHER_VBO(texCoords[6]);
331 FILTER_OTHER_VBO(texCoords[7]);
332 /*FILTER_OTHER_VBO(position2);*/
333 /*FILTER_OTHER_VBO(normal2);*/
334 /*FILTER_OTHER_VBO(tangent);*/
335 /*FILTER_OTHER_VBO(binormal);*/
336 /*FILTER_OTHER_VBO(tessFactor);*/
337 /*FILTER_OTHER_VBO(fog);*/
338 /*FILTER_OTHER_VBO(depth);*/
339 /*FILTER_OTHER_VBO(sample);*/
340 #undef FILTER_OTHER_VBO
342 /* Now find out if anything has changed. compared to the last run. Keep a few things in mind:
344 * 1) We do not mind if types change that do not need conversion
346 * 2) If some data exists cannot be found out by the data field alone. Data can appear at offset 0,
347 * so the main identification is the type. Note that D3DDECLTYPE_FLOAT1 is defined to 0, watch out
348 * for this if any semantic needs conversion if it is declared with that
350 * 3) If the type is the same, and it needs conversion, make sure it stayed at the same place. Moving
351 * converted bytes requires a reconversion of the whole buffer
353 * 4) The stride is not a reliable indicator for existance, as it can be 0 if an attribute stays static
355 * 5) If we used a vertex buffer before, and we aren't using one now(wouldn't be in this codepath
356 * otherwise) then we cannot compare the strided structures. Vertex shaders use numbered attributes,
357 * fixed function pipeline uses named once. For example, a vertex shader could have used converted
358 * tessFactor data which the code below ignores entirely. So if we used a vertex shader before, and
359 * used conversion before, assume a decl change
361 if(This->last_was_vshader && This->last_was_converted) {
362 TRACE("Reconverting because a vertex shader with conversion was used before\n");
363 ret = TRUE;
364 /* Still have to run through the code below to find the fixed function attribs that need
365 * conversion
368 do {
369 /* Position: We have to convert FLOAT4 data because opengl divides the vertex by 1 / w, or
370 * of course if the position has type D3DCOLOR
372 type_old = This->strided.u.s.position.dwType;
373 type_new = strided.u.s.position.dwType;
374 if(type_old != type_new) {
375 if(type_old == WINED3DDECLTYPE_FLOAT4 || type_new == WINED3DDECLTYPE_FLOAT4 ||
376 type_old == WINED3DDECLTYPE_D3DCOLOR || type_new == WINED3DDECLTYPE_D3DCOLOR) {
377 TRACE("Reconverting buffer because POSITION type changed from %s to %s\n",
378 debug_d3ddecltype(type_old), debug_d3ddecltype(type_new));
379 memcpy(&This->strided, &strided, sizeof(strided));
380 ret = TRUE;
381 break;
383 } else if(type_new == WINED3DDECLTYPE_D3DCOLOR || type_new == WINED3DDECLTYPE_FLOAT4) {
384 if(This->strided.u.s.position.lpData != strided.u.s.position.lpData) {
385 TRACE("Reconverting buffer because POSITION has type %s and moved from offset %p to %p\n",
386 debug_d3ddecltype(type_new), This->strided.u.s.position.lpData, strided.u.s.position.lpData);
387 memcpy(&This->strided, &strided, sizeof(strided));
388 ret = TRUE;
389 break;
393 /* Others: D3DCOLOR needs conversion */
394 #define CHECK_D3DCOLOR_CONV(name) \
395 type_old = This->strided.u.s.name.dwType; \
396 type_new = strided.u.s.name.dwType; \
397 if(type_old != type_new) { \
398 if(type_old == WINED3DDECLTYPE_D3DCOLOR || type_new == WINED3DDECLTYPE_D3DCOLOR) { \
399 TRACE("Reconverting buffer because %s type changed from %s to %s\n", \
400 #name, debug_d3ddecltype(type_old), debug_d3ddecltype(type_new)); \
401 memcpy(&This->strided, &strided, sizeof(strided)); \
402 ret = TRUE; \
403 break; \
405 } else if(type_new == WINED3DDECLTYPE_D3DCOLOR) { \
406 if(This->strided.u.s.name.lpData != strided.u.s.name.lpData) { \
407 TRACE("Reconverting buffer because DIFFUSE has type %s and moved from offset %p to %p\n", \
408 debug_d3ddecltype(type_new), This->strided.u.s.name.lpData, strided.u.s.name.lpData); \
409 memcpy(&This->strided, &strided, sizeof(strided)); \
410 ret = TRUE; \
411 break; \
414 CHECK_D3DCOLOR_CONV(normal);
415 CHECK_D3DCOLOR_CONV(diffuse);
416 CHECK_D3DCOLOR_CONV(specular);
417 CHECK_D3DCOLOR_CONV(texCoords[0]);
418 CHECK_D3DCOLOR_CONV(texCoords[1]);
419 CHECK_D3DCOLOR_CONV(texCoords[2]);
420 CHECK_D3DCOLOR_CONV(texCoords[3]);
421 CHECK_D3DCOLOR_CONV(texCoords[4]);
422 CHECK_D3DCOLOR_CONV(texCoords[5]);
423 CHECK_D3DCOLOR_CONV(texCoords[6]);
424 CHECK_D3DCOLOR_CONV(texCoords[7]);
425 #undef CHECK_D3DCOLOR_CONV
427 /* No conversion */
428 } while(0);
429 This->last_was_vshader = FALSE;
431 /* We have a declaration now in the buffer */
432 This->Flags |= VBFLAG_HASDESC;
434 return ret;
437 static void WINAPI IWineD3DVertexBufferImpl_PreLoad(IWineD3DVertexBuffer *iface) {
438 IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *) iface;
439 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
440 BYTE *data;
441 UINT start = 0, end = 0, stride = 0, vertices;
442 BOOL declChanged = FALSE;
443 int i, j;
444 TRACE("(%p)->()\n", This);
446 if(This->Flags & VBFLAG_LOAD) {
447 return; /* Already doing that stuff */
450 if(!This->vbo) {
451 /* TODO: Make converting independent from VBOs */
452 return; /* Not doing any conversion */
455 /* Reading the declaration makes only sense if the stateblock is finalized and the buffer bound to a stream */
456 if(device->isInDraw && This->bindCount > 0) {
457 declChanged = IWineD3DVertexBufferImpl_FindDecl(This);
458 } else if(This->Flags & VBFLAG_HASDESC) {
459 /* Reuse the declaration stored in the buffer. It will most likely not change, and if it does
460 * the stream source state handler will call PreLoad again and the change will be cought
462 } else {
463 /* Cannot get a declaration, and no declaration is stored in the buffer. It is pointless to preload
464 * now. When the buffer is used, PreLoad will be called by the stream source state handler and a valid
465 * declaration for the buffer can be found
467 return;
470 /* If applications change the declaration over and over, reconverting all the time is a huge
471 * performance hit. So count the declaration changes and release the VBO if there are too much
472 * of them(and thus stop converting)
474 if(declChanged) {
475 WineDirect3DVertexStridedData zero;
476 This->declChanges++;
477 This->draws = 0;
479 memset(&zero, 0, sizeof(zero));
480 zero.u.s.position_transformed = This->strided.u.s.position_transformed;
481 if(memcmp(&zero, &This->strided, sizeof(zero)) == 0) {
482 This->last_was_converted = FALSE;
483 } else {
484 This->last_was_converted = TRUE;
487 if(This->declChanges > VB_MAXDECLCHANGES) {
488 FIXME("Too much declaration changes, stopping converting\n");
489 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
490 ENTER_GL();
491 GL_EXTCALL(glDeleteBuffersARB(1, &This->vbo));
492 checkGLcall("glDeleteBuffersARB");
493 LEAVE_GL();
494 This->vbo = 0;
495 HeapFree(GetProcessHeap(), 0, This->conv_shift);
497 /* The stream source state handler might have read the memory of the vertex buffer already
498 * and got the memory in the vbo which is not valid any longer. Dirtify the stream source
499 * to force a reload. This happens only once per changed vertexbuffer and should occur rather
500 * rarely
502 IWineD3DDeviceImpl_MarkStateDirty(device, STATE_STREAMSRC);
504 return;
506 } else {
507 /* However, it is perfectly fine to change the declaration every now and then. We don't want a game that
508 * changes it every minute drop the VBO after VB_MAX_DECL_CHANGES minutes. So count draws without
509 * decl changes and reset the decl change count after a specific number of them
511 This->draws++;
512 if(This->draws > VB_RESETDECLCHANGE) This->declChanges = 0;
515 if(declChanged) {
516 /* The declaration changed, reload the whole buffer */
517 WARN("Reloading buffer because of decl change\n");
518 start = 0;
519 end = This->resource.size;
520 } else if(This->Flags & VBFLAG_DIRTY) {
521 /* No decl change, but dirty data, reload the changed stuff */
522 start = This->dirtystart;
523 end = This->dirtyend;
524 } else {
525 /* Desc not changed, buffer not dirty, nothing to do :-) */
526 return;
529 /* Mark the buffer clean */
530 This->Flags &= ~VBFLAG_DIRTY;
531 This->dirtystart = 0;
532 This->dirtyend = 0;
534 if(!This->last_was_converted) {
535 /* That means that there is nothing to fixup. Just upload from This->resource.allocatedMemory
536 * directly into the vbo. Do not free the system memory copy because drawPrimitive may need it if
537 * the stride is 0, for instancing emulation, vertex blending emulation or shader emulation.
539 TRACE("No conversion needed\n");
541 if(!device->isInDraw) {
542 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
544 ENTER_GL();
545 GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB, This->vbo));
546 checkGLcall("glBindBufferARB");
547 GL_EXTCALL(glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, start, end-start, This->resource.allocatedMemory + start));
548 checkGLcall("glBufferSubDataARB");
549 LEAVE_GL();
550 return;
553 /* OK, we have the original data from the app, the description of the buffer and the dirty area.
554 * so convert the stuff
556 if(This->last_was_vshader) {
557 TRACE("vertex-shadered conversion\n");
558 /* TODO: Improve that */
559 for(i = 0; i < MAX_ATTRIBS; i++) {
560 if(This->strided.u.input[i].dwStride) {
561 stride = This->strided.u.input[i].dwStride;
564 TRACE("Found input stride %d, output stride %d\n", stride, This->conv_stride);
565 /* For now reconvert the entire buffer */
566 start = 0;
567 end = This->resource.size;
569 vertices = This->resource.size / stride;
570 TRACE("%d vertices in buffer\n", vertices);
571 if(This->vbo_size != vertices * This->conv_stride) {
572 TRACE("Old size %d, creating new size %d\n", This->vbo_size, vertices * This->conv_stride);
573 ENTER_GL();
574 GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB, This->vbo));
575 checkGLcall("glBindBufferARB");
576 GL_EXTCALL(glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertices * This->conv_stride, NULL, This->vbo_usage));
577 This->vbo_size = vertices * This->conv_stride;
578 checkGLcall("glBufferDataARB");
579 LEAVE_GL();
581 data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, vertices * This->conv_stride);
582 if(!data) {
583 ERR("Out of memory\n");
584 return;
587 /* Now for each vertex in the buffer */
588 for(i = 0; i < vertices; i++) {
589 /* Copy the vertex over, taking the shifts into account */
590 for(j = 0; j < stride; j++) {
591 data[This->conv_stride * i + j + This->conv_shift[j]] = This->resource.allocatedMemory[i * stride + j];
593 /* And convert FLOAT16s */
594 for(j = 0; j < MAX_ATTRIBS; j++) {
595 DWORD_PTR offset = (DWORD_PTR) This->strided.u.input[j].lpData;
596 float *dest = (float *) (&data[This->conv_stride * i + offset + This->conv_shift[offset]]);
597 WORD *in = (WORD *) (&This->resource.allocatedMemory[i * stride + offset]);
599 switch(This->strided.u.input[j].dwType) {
600 case WINED3DDECLTYPE_FLOAT16_4:
601 dest[3] = float_16_to_32(in + 3);
602 dest[2] = float_16_to_32(in + 2);
603 /* drop through */
604 case WINED3DDECLTYPE_FLOAT16_2:
605 dest[1] = float_16_to_32(in + 1);
606 dest[0] = float_16_to_32(in + 0);
607 break;
608 default:
609 break;
613 if(!device->isInDraw) {
614 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
616 ENTER_GL();
617 GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB, This->vbo));
618 checkGLcall("glBindBufferARB");
619 GL_EXTCALL(glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, This->vbo_size, data));
620 checkGLcall("glBufferSubDataARB");
621 LEAVE_GL();
622 } else {
623 data = HeapAlloc(GetProcessHeap(), 0, end-start);
624 if(!data) {
625 ERR("Out of memory\n");
626 return;
628 memcpy(data, This->resource.allocatedMemory + start, end - start);
630 if (This->strided.u.s.position.dwStride) stride = This->strided.u.s.position.dwStride;
631 else if(This->strided.u.s.specular.dwStride) stride = This->strided.u.s.specular.dwStride;
632 else if(This->strided.u.s.diffuse.dwStride) stride = This->strided.u.s.diffuse.dwStride;
633 else if(This->strided.u.s.normal.dwStride) stride = This->strided.u.s.normal.dwStride;
634 else if(This->strided.u.s.texCoords[0].dwStride) stride = This->strided.u.s.texCoords[0].dwStride;
635 else if(This->strided.u.s.texCoords[1].dwStride) stride = This->strided.u.s.texCoords[1].dwStride;
636 else if(This->strided.u.s.texCoords[2].dwStride) stride = This->strided.u.s.texCoords[2].dwStride;
637 else if(This->strided.u.s.texCoords[3].dwStride) stride = This->strided.u.s.texCoords[3].dwStride;
638 else if(This->strided.u.s.texCoords[4].dwStride) stride = This->strided.u.s.texCoords[4].dwStride;
639 else if(This->strided.u.s.texCoords[5].dwStride) stride = This->strided.u.s.texCoords[5].dwStride;
640 else if(This->strided.u.s.texCoords[6].dwStride) stride = This->strided.u.s.texCoords[6].dwStride;
641 else if(This->strided.u.s.texCoords[7].dwStride) stride = This->strided.u.s.texCoords[7].dwStride;
643 for(i = (( end - start) / stride) - 1; i >= 0; i--) {
644 if(This->strided.u.s.position.dwType == WINED3DDECLTYPE_FLOAT4) {
645 fixup_transformed_pos((float *)((DWORD_PTR) data + ((DWORD_PTR) This->strided.u.s.position.lpData) + i * stride));
646 } else if(This->strided.u.s.position.dwType == WINED3DDECLTYPE_D3DCOLOR) {
647 FIXME("Write a test for D3DCOLOR position\n");
649 #define CONVERT_D3DCOLOR_ATTRIB(name) \
650 if(This->strided.u.s.name.dwType == WINED3DDECLTYPE_D3DCOLOR) { \
651 fixup_d3dcolor((DWORD *)((DWORD_PTR) data + ((DWORD_PTR) This->strided.u.s.name.lpData) + i * stride)); \
653 CONVERT_D3DCOLOR_ATTRIB(position);
654 CONVERT_D3DCOLOR_ATTRIB(specular);
655 CONVERT_D3DCOLOR_ATTRIB(diffuse);
656 CONVERT_D3DCOLOR_ATTRIB(normal);
657 CONVERT_D3DCOLOR_ATTRIB(texCoords[0]);
658 CONVERT_D3DCOLOR_ATTRIB(texCoords[1]);
659 CONVERT_D3DCOLOR_ATTRIB(texCoords[2]);
660 CONVERT_D3DCOLOR_ATTRIB(texCoords[3]);
661 CONVERT_D3DCOLOR_ATTRIB(texCoords[4]);
662 CONVERT_D3DCOLOR_ATTRIB(texCoords[5]);
663 CONVERT_D3DCOLOR_ATTRIB(texCoords[6]);
664 CONVERT_D3DCOLOR_ATTRIB(texCoords[7]);
665 #undef CONVERT_D3DCOLOR_ATTRIB
667 if(!device->isInDraw) {
668 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
670 ENTER_GL();
671 GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB, This->vbo));
672 checkGLcall("glBindBufferARB");
673 GL_EXTCALL(glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, start, end - start, data));
674 checkGLcall("glBufferSubDataARB");
675 LEAVE_GL();
678 HeapFree(GetProcessHeap(), 0, data);
681 static WINED3DRESOURCETYPE WINAPI IWineD3DVertexBufferImpl_GetType(IWineD3DVertexBuffer *iface) {
682 return IWineD3DResourceImpl_GetType((IWineD3DResource *)iface);
685 static HRESULT WINAPI IWineD3DVertexBufferImpl_GetParent(IWineD3DVertexBuffer *iface, IUnknown **pParent) {
686 return IWineD3DResourceImpl_GetParent((IWineD3DResource *)iface, pParent);
689 /* ******************************************************
690 IWineD3DVertexBuffer IWineD3DVertexBuffer parts follow
691 ****************************************************** */
692 static HRESULT WINAPI IWineD3DVertexBufferImpl_Lock(IWineD3DVertexBuffer *iface, UINT OffsetToLock, UINT SizeToLock, BYTE** ppbData, DWORD Flags) {
693 IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *)iface;
694 BYTE *data;
695 TRACE("(%p)->%d, %d, %p, %08x\n", This, OffsetToLock, SizeToLock, ppbData, Flags);
697 InterlockedIncrement(&This->lockcount);
699 if(This->Flags & VBFLAG_DIRTY) {
700 if(This->dirtystart > OffsetToLock) This->dirtystart = OffsetToLock;
701 if(SizeToLock) {
702 if(This->dirtyend < OffsetToLock + SizeToLock) This->dirtyend = OffsetToLock + SizeToLock;
703 } else {
704 This->dirtyend = This->resource.size;
706 } else {
707 This->dirtystart = OffsetToLock;
708 if(SizeToLock)
709 This->dirtyend = OffsetToLock + SizeToLock;
710 else
711 This->dirtyend = This->resource.size;
714 data = This->resource.allocatedMemory;
715 This->Flags |= VBFLAG_DIRTY;
716 *ppbData = data + OffsetToLock;
718 TRACE("(%p) : returning memory of %p (base:%p,offset:%u)\n", This, data + OffsetToLock, data, OffsetToLock);
719 /* TODO: check Flags compatibility with This->currentDesc.Usage (see MSDN) */
720 return WINED3D_OK;
722 HRESULT WINAPI IWineD3DVertexBufferImpl_Unlock(IWineD3DVertexBuffer *iface) {
723 IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *) iface;
724 LONG lockcount;
725 TRACE("(%p)\n", This);
727 lockcount = InterlockedDecrement(&This->lockcount);
728 if(lockcount > 0) {
729 /* Delay loading the buffer until everything is unlocked */
730 TRACE("Ignoring the unlock\n");
731 return WINED3D_OK;
734 if(This->Flags & VBFLAG_HASDESC) {
735 IWineD3DVertexBufferImpl_PreLoad(iface);
737 return WINED3D_OK;
739 static HRESULT WINAPI IWineD3DVertexBufferImpl_GetDesc(IWineD3DVertexBuffer *iface, WINED3DVERTEXBUFFER_DESC *pDesc) {
740 IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *)iface;
742 TRACE("(%p)\n", This);
743 pDesc->Format = This->resource.format;
744 pDesc->Type = This->resource.resourceType;
745 pDesc->Usage = This->resource.usage;
746 pDesc->Pool = This->resource.pool;
747 pDesc->Size = This->resource.size;
748 pDesc->FVF = This->fvf;
749 return WINED3D_OK;
752 const IWineD3DVertexBufferVtbl IWineD3DVertexBuffer_Vtbl =
754 /* IUnknown */
755 IWineD3DVertexBufferImpl_QueryInterface,
756 IWineD3DVertexBufferImpl_AddRef,
757 IWineD3DVertexBufferImpl_Release,
758 /* IWineD3DResource */
759 IWineD3DVertexBufferImpl_GetParent,
760 IWineD3DVertexBufferImpl_GetDevice,
761 IWineD3DVertexBufferImpl_SetPrivateData,
762 IWineD3DVertexBufferImpl_GetPrivateData,
763 IWineD3DVertexBufferImpl_FreePrivateData,
764 IWineD3DVertexBufferImpl_SetPriority,
765 IWineD3DVertexBufferImpl_GetPriority,
766 IWineD3DVertexBufferImpl_PreLoad,
767 IWineD3DVertexBufferImpl_GetType,
768 /* IWineD3DVertexBuffer */
769 IWineD3DVertexBufferImpl_Lock,
770 IWineD3DVertexBufferImpl_Unlock,
771 IWineD3DVertexBufferImpl_GetDesc
774 BYTE* WINAPI IWineD3DVertexBufferImpl_GetMemory(IWineD3DVertexBuffer* iface, DWORD iOffset, GLint *vbo) {
775 IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *)iface;
777 *vbo = This->vbo;
778 if(This->vbo == 0) {
779 return This->resource.allocatedMemory + iOffset;
780 } else {
781 return (BYTE *) iOffset;
785 HRESULT WINAPI IWineD3DVertexBufferImpl_ReleaseMemory(IWineD3DVertexBuffer* iface) {
786 return WINED3D_OK;