.gitignore: Added wildcards to ignore generated resource files.
[wine] / dlls / wined3d / vertexbuffer.c
1 /*
2  * IWineD3DVertexBuffer Implementation
3  *
4  * Copyright 2002-2005 Jason Edmeades
5  *                     Raphael Junqueira
6  * Copyright 2004 Christian Costa
7  *
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.
12  *
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.
17  *
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
21  */
22
23 #include "config.h"
24 #include "wined3d_private.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
27 #define GLINFO_LOCATION ((IWineD3DImpl *)(((IWineD3DDeviceImpl *)This->resource.wineD3DDevice)->wineD3D))->gl_info
28
29 /* *******************************************
30    IWineD3DVertexBuffer IUnknown parts follow
31    ******************************************* */
32 static HRESULT WINAPI IWineD3DVertexBufferImpl_QueryInterface(IWineD3DVertexBuffer *iface, REFIID riid, LPVOID *ppobj)
33 {
34     IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *)iface;
35     TRACE("(%p)->(%s,%p)\n",This,debugstr_guid(riid),ppobj);
36     if (IsEqualGUID(riid, &IID_IUnknown)
37         || IsEqualGUID(riid, &IID_IWineD3DBase)
38         || IsEqualGUID(riid, &IID_IWineD3DResource)
39         || IsEqualGUID(riid, &IID_IWineD3DVertexBuffer)){
40         IUnknown_AddRef(iface);
41         *ppobj = This;
42         return S_OK;
43     }
44     *ppobj = NULL;
45     return E_NOINTERFACE;
46 }
47
48 static ULONG WINAPI IWineD3DVertexBufferImpl_AddRef(IWineD3DVertexBuffer *iface) {
49     IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *)iface;
50     ULONG ref = InterlockedIncrement(&This->resource.ref);
51     TRACE("(%p) : AddRef increasing from %ld\n", This, ref - 1);
52     return ref;
53 }
54
55 static ULONG WINAPI IWineD3DVertexBufferImpl_Release(IWineD3DVertexBuffer *iface) {
56     IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *)iface;
57     ULONG ref = InterlockedDecrement(&This->resource.ref);
58     TRACE("(%p) : Releasing from %ld\n", This, ref + 1);
59     if (ref == 0) {
60
61         if(This->vbo) {
62             ENTER_GL();
63             GL_EXTCALL(glDeleteBuffersARB(1, &This->vbo));
64             checkGLcall("glDeleteBuffersARB");
65             LEAVE_GL();
66         }
67
68         IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
69         HeapFree(GetProcessHeap(), 0, This);
70     }
71     return ref;
72 }
73
74 /* ****************************************************
75    IWineD3DVertexBuffer IWineD3DResource parts follow
76    **************************************************** */
77 static HRESULT WINAPI IWineD3DVertexBufferImpl_GetDevice(IWineD3DVertexBuffer *iface, IWineD3DDevice** ppDevice) {
78     return IWineD3DResourceImpl_GetDevice((IWineD3DResource *)iface, ppDevice);
79 }
80
81 static HRESULT WINAPI IWineD3DVertexBufferImpl_SetPrivateData(IWineD3DVertexBuffer *iface, REFGUID refguid, CONST void* pData, DWORD SizeOfData, DWORD Flags) {
82     return IWineD3DResourceImpl_SetPrivateData((IWineD3DResource *)iface, refguid, pData, SizeOfData, Flags);
83 }
84
85 static HRESULT WINAPI IWineD3DVertexBufferImpl_GetPrivateData(IWineD3DVertexBuffer *iface, REFGUID refguid, void* pData, DWORD* pSizeOfData) {
86     return IWineD3DResourceImpl_GetPrivateData((IWineD3DResource *)iface, refguid, pData, pSizeOfData);
87 }
88
89 static HRESULT WINAPI IWineD3DVertexBufferImpl_FreePrivateData(IWineD3DVertexBuffer *iface, REFGUID refguid) {
90     return IWineD3DResourceImpl_FreePrivateData((IWineD3DResource *)iface, refguid);
91 }
92
93 static DWORD    WINAPI IWineD3DVertexBufferImpl_SetPriority(IWineD3DVertexBuffer *iface, DWORD PriorityNew) {
94     return IWineD3DResourceImpl_SetPriority((IWineD3DResource *)iface, PriorityNew);
95 }
96
97 static DWORD    WINAPI IWineD3DVertexBufferImpl_GetPriority(IWineD3DVertexBuffer *iface) {
98     return IWineD3DResourceImpl_GetPriority((IWineD3DResource *)iface);
99 }
100
101 static void     WINAPI IWineD3DVertexBufferImpl_PreLoad(IWineD3DVertexBuffer *iface) {
102     IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *) iface;
103     TRACE("(%p)->()\n", This);
104
105     if(This->Flags & VBFLAG_LOAD) {
106         return; /* Already doing that stuff */
107     }
108
109     if(!This->resource.allocatedMemory) {
110         TRACE("Locking directly into VBO, nothing to do\n");
111         return; /* Direct lock into the VBO */
112     }
113
114     if(This->vbo) {
115         WineDirect3DVertexStridedData strided;
116         IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
117         BOOL useVertexShaderFunction = FALSE, fixup = FALSE;
118         BYTE *data;
119         UINT i;
120         UINT start = 0, end = 0, stride = 0;
121
122         if(This->Flags & VBFLAG_DIRTY) {
123             /* Update the old buffer on unlock, use the old desc */
124             start = This->dirtystart;
125             end = This->dirtyend;
126             memcpy(&strided, &This->strided, sizeof(strided));
127
128             if     (strided.u.s.position.dwStride) stride = strided.u.s.position.dwStride;
129             else if(strided.u.s.specular.dwStride) stride = strided.u.s.specular.dwStride;
130             else if(strided.u.s.diffuse.dwStride)  stride = strided.u.s.diffuse.dwStride;
131             else {
132                 /* That means that there is nothing to fixup, just override previously modified data */
133                 fixup = FALSE;
134             }
135             if(stride) fixup = TRUE;
136         } else {
137             /* Keep this in sync with drawPrimitive in drawprim.c */
138             if (device->stateBlock->vertexShader != NULL && wined3d_settings.vs_mode != VS_NONE 
139                     &&((IWineD3DVertexShaderImpl *)device->stateBlock->vertexShader)->baseShader.function != NULL
140                     && GL_SUPPORT(ARB_VERTEX_PROGRAM)) {
141                 /* Using shaders? No conversion needed, the shaders handle this */
142                 TRACE("Using vertex shaders, not doing any vertex conversion\n");
143                 ENTER_GL();
144                 GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB, This->vbo));
145                 checkGLcall("glBindBufferARB");
146                 GL_EXTCALL(glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
147                 checkGLcall("glBufferSubDataARB");
148                 LEAVE_GL();
149                 /* Lock directly into the VBO in the future */
150                 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
151                 This->resource.allocatedMemory = NULL;
152                 This->Flags &= ~VBFLAG_DIRTY;
153                 return;
154             }
155
156             /* The code below reads the FVF / Vertex Declaration to find out which bits we have to convert
157             * Basically I can't see any reason why it can't change from DrawPrimitive to DrawPrimitive call
158             * from the DX api, but I think no sane game will do that. Reading the vertex declaration is quite
159             * complex, and we should save as much CPU time as possible. So read it only once ans assume that
160             * it doesn't change silently. I expect Windows D3D drivers to depend on that too
161             */
162             if(This->Flags & VBFLAG_HASDESC) return;
163
164             /* Check against updated declarations */
165             memset(&strided, 0, sizeof(strided));
166
167             if(device->stateBlock->vertexDecl != NULL) {
168                 /* Check against the stream offset and make sure it is 0 */
169
170                 This->Flags |= VBFLAG_LOAD;
171                 primitiveDeclarationConvertToStridedData((IWineD3DDevice *) device,
172                                                         useVertexShaderFunction,
173                                                         &strided,
174                                                         0,
175                                                         &fixup);
176                 This->Flags &= ~VBFLAG_LOAD;
177
178                 /* Only take care for stuff that is in this buffer, well, only the stuff that is interesting */
179                 if(strided.u.s.position.VBO != This->vbo)    memset(&strided.u.s.position, 0, sizeof(strided.u.s.position));
180                 if(strided.u.s.diffuse.VBO != This->vbo)     memset(&strided.u.s.diffuse, 0, sizeof(strided.u.s.diffuse));
181                 if(strided.u.s.specular.VBO != This->vbo)    memset(&strided.u.s.specular, 0, sizeof(strided.u.s.specular));
182                 if(strided.u.s.position2.VBO != This->vbo)   memset(&strided.u.s.position2, 0, sizeof(strided.u.s.position2));
183             } else {
184                 if(!(This->Flags & VBFLAG_STREAM) ) {
185                     TRACE("No vertex decl used and buffer is not bound to a stream, nothing to do\n");
186                     return;
187                 }
188
189                 This->Flags |= VBFLAG_LOAD;
190                 primitiveConvertFVFtoOffset(device->stateBlock->fvf,
191                                             device->stateBlock->streamStride[This->stream],
192                                             NULL,
193                                             &strided,
194                                             This->vbo);
195                 This->Flags &= ~VBFLAG_LOAD;
196             }
197
198             /* If any data that needs conversion has changed we have to reload the whole buffer */
199             if( ( (This->strided.u.s.position_transformed || strided.u.s.position_transformed) &&
200                   This->strided.u.s.position.lpData != strided.u.s.position.lpData) ||
201                 !(This->strided.u.s.diffuse.lpData == strided.u.s.diffuse.lpData || strided.u.s.diffuse.VBO != This->vbo)   ||
202                 !(This->strided.u.s.specular.lpData == strided.u.s.specular.lpData || strided.u.s.specular.VBO != This->vbo) ) {
203
204                 start = 0;
205                 end = This->resource.size;
206                 fixup = TRUE;
207
208                 if     (strided.u.s.position.dwStride) stride = strided.u.s.position.dwStride;
209                 else if(strided.u.s.specular.dwStride) stride = strided.u.s.specular.dwStride;
210                 else if(strided.u.s.diffuse.dwStride)  stride = strided.u.s.diffuse.dwStride;
211                 else {
212                     /* That means that there is nothing to fixup, just override previously modified data */
213                     fixup = FALSE;
214                 }
215
216                 memcpy(&This->strided, &strided, sizeof(strided));
217             } else {
218                 TRACE("No declaration change\n");
219                 /* nothing to do - the old data is correct*/
220                 return;
221             }
222             This->Flags |= VBFLAG_HASDESC;
223         }
224
225         if(end == 0) {
226             TRACE("Buffer not dirty, nothing to do\n");
227             This->Flags &= ~VBFLAG_DIRTY;
228             return;
229         }
230
231         TRACE("Loading buffer\n");
232         if(fixup) {
233             data = HeapAlloc(GetProcessHeap(), 0, end-start);
234             if(!data) {
235                 ERR("Out of memory\n");
236                 return;
237             }
238             memcpy(data, This->resource.allocatedMemory + start, end - start);
239
240             for(i = 0; i < ( end - start) / stride; i++) {
241                 if(strided.u.s.position_transformed) {
242                     float *p = (float *) (((int) This->resource.allocatedMemory + 
243                        (int) strided.u.s.position.lpData) + start + i * stride);
244                     float x, y, z, w;
245
246                     /* rhw conversion like in drawStridedSlow */
247                     if(p[3] == 1.0 || ((p[3] < eps) && (p[3] > -eps))) {
248                         x = p[0];
249                         y = p[1];
250                         z = p[2];
251                         w = 1.0;
252                     } else {
253                         w = 1.0 / p[3];
254                         x = p[0] * w;
255                         y = p[1] * w;
256                         z = p[2] * w;
257                     }
258                     p = (float *) ((int) data + i * stride + (int) strided.u.s.position.lpData);
259                     p[0] = x;
260                     p[1] = y;
261                     p[2] = z;
262                     p[3] = w;
263                 }
264                 if(strided.u.s.diffuse.dwType == WINED3DDECLTYPE_SHORT4 || strided.u.s.diffuse.dwType == WINED3DDECLTYPE_D3DCOLOR) {
265                     DWORD srcColor, *dstColor = (DWORD *) (data + i * stride + (int) strided.u.s.diffuse.lpData);
266                     srcColor = * (DWORD *) ( ((int) This->resource.allocatedMemory + (int) strided.u.s.diffuse.lpData) + start + i * stride);
267
268                     /* Color conversion like in drawStridedSlow. watch out for little endianity
269                      * If we want that stuff to work on big endian machines too we have to consider more things
270                      *
271                      * 0xff000000: Alpha mask
272                      * 0x00ff0000: Blue mask
273                      * 0x0000ff00: Green mask
274                      * 0x000000ff: Red mask
275                      */
276
277                     *dstColor = 0;
278                     *dstColor |= (srcColor & 0xff00ff00)      ;   /* Alpha Green */
279                     *dstColor |= (srcColor & 0x00ff0000) >> 16;   /* Red */
280                     *dstColor |= (srcColor & 0x000000ff) << 16;   /* Blue */
281                 } else if (strided.u.s.diffuse.lpData != NULL) {
282                     FIXME("Type is %ld\n", strided.u.s.diffuse.dwType);
283                 }
284                 if(strided.u.s.specular.dwType == WINED3DDECLTYPE_SHORT4 || strided.u.s.specular.dwType == WINED3DDECLTYPE_D3DCOLOR) {
285                     DWORD srcColor, *dstColor = (DWORD *) (data + i * stride + (int) strided.u.s.specular.lpData);
286                     srcColor = * (DWORD *) ( ((int) This->resource.allocatedMemory + (int) strided.u.s.specular.lpData) + start + i * stride);
287
288                     /* Color conversion like in drawStridedSlow. watch out for little endianity
289                      * If we want that stuff to work on big endian machines too we have to consider more things
290                      */
291                     *dstColor = 0;
292                     *dstColor |= (srcColor & 0xff00ff00)      ;   /* Alpha Green */
293                     *dstColor |= (srcColor & 0x00ff0000) >> 16;   /* Red */
294                     *dstColor |= (srcColor & 0x000000ff) << 16;   /* Blue */
295                 }
296             }
297         } else {
298             data = This->resource.allocatedMemory + start;
299         }
300
301         ENTER_GL();
302         GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB, This->vbo));
303         checkGLcall("glBindBufferARB");
304         GL_EXTCALL(glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, start, end - start, data));
305         checkGLcall("glBufferSubDataARB");
306         LEAVE_GL();
307
308         if(fixup) {
309             HeapFree(GetProcessHeap(), 0, data);
310         } else if(This->Flags & VBFLAG_HASDESC) {
311             /* Free the allocated memory, then Lock will directly lock into the
312              * VBO the next time :-)
313              */
314             HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
315             This->resource.allocatedMemory = NULL;
316         }
317     }
318     This->Flags &= ~VBFLAG_DIRTY;
319 }
320
321 static WINED3DRESOURCETYPE WINAPI IWineD3DVertexBufferImpl_GetType(IWineD3DVertexBuffer *iface) {
322     return IWineD3DResourceImpl_GetType((IWineD3DResource *)iface);
323 }
324
325 static HRESULT WINAPI IWineD3DVertexBufferImpl_GetParent(IWineD3DVertexBuffer *iface, IUnknown **pParent) {
326     return IWineD3DResourceImpl_GetParent((IWineD3DResource *)iface, pParent);
327 }
328
329 /* ******************************************************
330    IWineD3DVertexBuffer IWineD3DVertexBuffer parts follow
331    ****************************************************** */
332 static HRESULT  WINAPI IWineD3DVertexBufferImpl_Lock(IWineD3DVertexBuffer *iface, UINT OffsetToLock, UINT SizeToLock, BYTE** ppbData, DWORD Flags) {
333     IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *)iface;
334     BYTE *data;
335     TRACE("(%p)->%d, %d, %p, %08lx\n", This, OffsetToLock, SizeToLock, ppbData, Flags);
336
337     InterlockedIncrement(&This->lockcount);
338
339     if(This->Flags & VBFLAG_DIRTY) {
340         if(This->dirtystart > OffsetToLock) This->dirtystart = OffsetToLock;
341         if(SizeToLock) {
342             if(This->dirtyend < OffsetToLock + SizeToLock) This->dirtyend = OffsetToLock + SizeToLock;
343         } else {
344             This->dirtyend = This->resource.size;
345         }
346     } else {
347         This->dirtystart = OffsetToLock;
348         if(SizeToLock)
349             This->dirtyend = OffsetToLock + SizeToLock;
350         else
351             This->dirtyend = OffsetToLock + This->resource.size;
352     }
353
354     if(This->resource.allocatedMemory) {
355           data = This->resource.allocatedMemory;
356           This->Flags |= VBFLAG_DIRTY;
357     } else {
358         GLenum mode = GL_READ_WRITE_ARB;
359         /* Return data to the VBO */
360
361         TRACE("Locking directly into the buffer\n");
362
363         if((This->resource.usage & WINED3DUSAGE_WRITEONLY) || ( Flags & D3DLOCK_DISCARD) ) {
364             mode = GL_WRITE_ONLY_ARB;
365         } else if( Flags & (D3DLOCK_READONLY | D3DLOCK_NO_DIRTY_UPDATE) ) {
366             mode = GL_READ_ONLY_ARB;
367         }
368
369         ENTER_GL();
370         GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB, This->vbo));
371         checkGLcall("glBindBufferARB");
372         data = GL_EXTCALL(glMapBufferARB(GL_ARRAY_BUFFER_ARB, mode));
373         LEAVE_GL();
374         if(!data) {
375             ERR("glMapBuffer failed\n");
376             return WINED3DERR_INVALIDCALL;
377         }
378     }
379     *ppbData = data + OffsetToLock;
380
381     TRACE("(%p) : returning memory of %p (base:%p,offset:%u)\n", This, data + OffsetToLock, data, OffsetToLock);
382     /* TODO: check Flags compatibility with This->currentDesc.Usage (see MSDN) */
383     return WINED3D_OK;
384 }
385 HRESULT  WINAPI IWineD3DVertexBufferImpl_Unlock(IWineD3DVertexBuffer *iface) {
386     IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *) iface;
387     LONG lockcount;
388     TRACE("(%p)\n", This);
389
390     lockcount = InterlockedDecrement(&This->lockcount);
391     if(lockcount > 0) {
392         /* Delay loading the buffer until everything is unlocked */
393         TRACE("Ignoring the unlock\n");
394         return D3D_OK;
395     }
396
397     if(!This->resource.allocatedMemory) {
398         ENTER_GL();
399         GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB, This->vbo));
400         checkGLcall("glBindBufferARB");
401         GL_EXTCALL(glUnmapBufferARB(GL_ARRAY_BUFFER_ARB));
402         checkGLcall("glUnmapBufferARB");
403         LEAVE_GL();
404     } else {
405         IWineD3DVertexBufferImpl_PreLoad(iface);
406     }
407     return WINED3D_OK;
408 }
409 static HRESULT  WINAPI IWineD3DVertexBufferImpl_GetDesc(IWineD3DVertexBuffer *iface, WINED3DVERTEXBUFFER_DESC *pDesc) {
410     IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *)iface;
411
412     TRACE("(%p)\n", This);
413     pDesc->Format = This->resource.format;
414     pDesc->Type   = This->resource.resourceType;
415     pDesc->Usage  = This->resource.usage;
416     pDesc->Pool   = This->resource.pool;
417     pDesc->Size   = This->resource.size;
418     pDesc->FVF    = This->fvf;
419     return WINED3D_OK;
420 }
421
422 const IWineD3DVertexBufferVtbl IWineD3DVertexBuffer_Vtbl =
423 {
424     /* IUnknown */
425     IWineD3DVertexBufferImpl_QueryInterface,
426     IWineD3DVertexBufferImpl_AddRef,
427     IWineD3DVertexBufferImpl_Release,
428     /* IWineD3DResource */
429     IWineD3DVertexBufferImpl_GetParent,
430     IWineD3DVertexBufferImpl_GetDevice,
431     IWineD3DVertexBufferImpl_SetPrivateData,
432     IWineD3DVertexBufferImpl_GetPrivateData,
433     IWineD3DVertexBufferImpl_FreePrivateData,
434     IWineD3DVertexBufferImpl_SetPriority,
435     IWineD3DVertexBufferImpl_GetPriority,
436     IWineD3DVertexBufferImpl_PreLoad,
437     IWineD3DVertexBufferImpl_GetType,
438     /* IWineD3DVertexBuffer */
439     IWineD3DVertexBufferImpl_Lock,
440     IWineD3DVertexBufferImpl_Unlock,
441     IWineD3DVertexBufferImpl_GetDesc
442 };
443
444 BYTE* WINAPI IWineD3DVertexBufferImpl_GetMemory(IWineD3DVertexBuffer* iface, DWORD iOffset, GLint *vbo) {
445     IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *)iface;
446
447     *vbo = This->vbo;
448     if(This->vbo == 0) {
449         return This->resource.allocatedMemory + iOffset;
450     } else {
451         return (BYTE *) iOffset;
452     }
453 }
454
455 HRESULT WINAPI IWineD3DVertexBufferImpl_ReleaseMemory(IWineD3DVertexBuffer* iface) {
456   return WINED3D_OK;
457 }