2 * IWineD3DVertexBuffer Implementation
4 * Copyright 2002-2005 Jason Edmeades
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
24 #include "wined3d_private.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
27 #define GLINFO_LOCATION ((IWineD3DImpl *)(((IWineD3DDeviceImpl *)This->resource.wineD3DDevice)->wineD3D))->gl_info
29 /* *******************************************
30 IWineD3DVertexBuffer IUnknown parts follow
31 ******************************************* */
32 static HRESULT WINAPI IWineD3DVertexBufferImpl_QueryInterface(IWineD3DVertexBuffer *iface, REFIID riid, LPVOID *ppobj)
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);
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);
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);
63 GL_EXTCALL(glDeleteBuffersARB(1, &This->vbo));
64 checkGLcall("glDeleteBuffersARB");
68 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
69 HeapFree(GetProcessHeap(), 0, This);
74 /* ****************************************************
75 IWineD3DVertexBuffer IWineD3DResource parts follow
76 **************************************************** */
77 static HRESULT WINAPI IWineD3DVertexBufferImpl_GetDevice(IWineD3DVertexBuffer *iface, IWineD3DDevice** ppDevice) {
78 return IWineD3DResourceImpl_GetDevice((IWineD3DResource *)iface, ppDevice);
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);
85 static HRESULT WINAPI IWineD3DVertexBufferImpl_GetPrivateData(IWineD3DVertexBuffer *iface, REFGUID refguid, void* pData, DWORD* pSizeOfData) {
86 return IWineD3DResourceImpl_GetPrivateData((IWineD3DResource *)iface, refguid, pData, pSizeOfData);
89 static HRESULT WINAPI IWineD3DVertexBufferImpl_FreePrivateData(IWineD3DVertexBuffer *iface, REFGUID refguid) {
90 return IWineD3DResourceImpl_FreePrivateData((IWineD3DResource *)iface, refguid);
93 static DWORD WINAPI IWineD3DVertexBufferImpl_SetPriority(IWineD3DVertexBuffer *iface, DWORD PriorityNew) {
94 return IWineD3DResourceImpl_SetPriority((IWineD3DResource *)iface, PriorityNew);
97 static DWORD WINAPI IWineD3DVertexBufferImpl_GetPriority(IWineD3DVertexBuffer *iface) {
98 return IWineD3DResourceImpl_GetPriority((IWineD3DResource *)iface);
101 static void fixup_vertices(BYTE *src, BYTE *dst, int stride, int num, BYTE *pos, BOOL haspos, BYTE *diffuse, BOOL hasdiffuse, BYTE *specular, BOOL hasspecular) {
105 for(i = num - 1; i >= 0; i--) {
107 float *p = (float *) (((int) src + (int) pos) + i * stride);
109 /* rhw conversion like in drawStridedSlow */
110 if(p[3] == 1.0 || ((p[3] < eps) && (p[3] > -eps))) {
121 p = (float *) ((int) dst + i * stride + (int) pos);
128 DWORD srcColor, *dstColor = (DWORD *) (dst + i * stride + (int) diffuse);
129 srcColor = * (DWORD *) ( ((int) src + (int) diffuse) + i * stride);
131 /* Color conversion like in drawStridedSlow. watch out for little endianity
132 * If we want that stuff to work on big endian machines too we have to consider more things
134 * 0xff000000: Alpha mask
135 * 0x00ff0000: Blue mask
136 * 0x0000ff00: Green mask
137 * 0x000000ff: Red mask
141 *dstColor |= (srcColor & 0xff00ff00) ; /* Alpha Green */
142 *dstColor |= (srcColor & 0x00ff0000) >> 16; /* Red */
143 *dstColor |= (srcColor & 0x000000ff) << 16; /* Blue */
146 DWORD srcColor, *dstColor = (DWORD *) (dst + i * stride + (int) specular);
147 srcColor = * (DWORD *) ( ((int) src + (int) specular) + i * stride);
149 /* Simmilar to diffuse
150 * TODO: Write the alpha value out for fog coords
153 *dstColor |= (srcColor & 0xff00ff00) ; /* Alpha Green */
154 *dstColor |= (srcColor & 0x00ff0000) >> 16; /* Red */
155 *dstColor |= (srcColor & 0x000000ff) << 16; /* Blue */
160 static void WINAPI IWineD3DVertexBufferImpl_PreLoad(IWineD3DVertexBuffer *iface) {
161 IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *) iface;
163 UINT start = 0, end = 0, stride = 0;
164 BOOL useVertexShaderFunction = FALSE, fixup = FALSE;
165 TRACE("(%p)->()\n", This);
167 if(This->Flags & VBFLAG_LOAD) {
168 return; /* Already doing that stuff */
171 if(!This->resource.allocatedMemory) {
172 TRACE("Locking directly into VBO, nothing to do\n");
173 return; /* Direct lock into the VBO */
177 WineDirect3DVertexStridedData strided;
178 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
180 if(This->Flags & VBFLAG_DIRTY) {
181 /* Update the old buffer on unlock, use the old desc */
182 start = This->dirtystart;
183 end = This->dirtyend;
184 memcpy(&strided, &This->strided, sizeof(strided));
186 if (strided.u.s.position.dwStride) stride = strided.u.s.position.dwStride;
187 else if(strided.u.s.specular.dwStride) stride = strided.u.s.specular.dwStride;
188 else if(strided.u.s.diffuse.dwStride) stride = strided.u.s.diffuse.dwStride;
190 /* That means that there is nothing to fixup, just override previously modified data */
193 if(stride) fixup = TRUE;
195 /* Keep this in sync with drawPrimitive in drawprim.c */
196 if (device->stateBlock->vertexShader != NULL && wined3d_settings.vs_mode != VS_NONE
197 &&((IWineD3DVertexShaderImpl *)device->stateBlock->vertexShader)->baseShader.function != NULL
198 && GL_SUPPORT(ARB_VERTEX_PROGRAM)) {
199 /* Using shaders? No conversion needed, the shaders handle this */
200 TRACE("Using vertex shaders, not doing any vertex conversion\n");
202 GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB, This->vbo));
203 checkGLcall("glBindBufferARB");
204 GL_EXTCALL(glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
205 checkGLcall("glBufferSubDataARB");
207 /* Lock directly into the VBO in the future */
208 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
209 This->resource.allocatedMemory = NULL;
210 This->Flags &= ~VBFLAG_DIRTY;
214 /* The code below reads the FVF / Vertex Declaration to find out which bits we have to convert
215 * Basically I can't see any reason why it can't change from DrawPrimitive to DrawPrimitive call
216 * from the DX api, but I think no sane game will do that. Reading the vertex declaration is quite
217 * complex, and we should save as much CPU time as possible. So read it only once ans assume that
218 * it doesn't change silently. I expect Windows D3D drivers to depend on that too
220 if(This->Flags & VBFLAG_HASDESC) return;
222 /* Check against updated declarations */
223 memset(&strided, 0, sizeof(strided));
225 if(device->stateBlock->vertexDecl || device->stateBlock->vertexShader) {
226 /* Check against the stream offset and make sure it is 0 */
228 This->Flags |= VBFLAG_LOAD;
229 primitiveDeclarationConvertToStridedData((IWineD3DDevice *) device,
230 useVertexShaderFunction,
234 This->Flags &= ~VBFLAG_LOAD;
236 /* Only take care for stuff that is in this buffer, well, only the stuff that is interesting */
237 if(strided.u.s.position.VBO != This->vbo) memset(&strided.u.s.position, 0, sizeof(strided.u.s.position));
238 if(strided.u.s.diffuse.VBO != This->vbo) memset(&strided.u.s.diffuse, 0, sizeof(strided.u.s.diffuse));
239 if(strided.u.s.specular.VBO != This->vbo) memset(&strided.u.s.specular, 0, sizeof(strided.u.s.specular));
240 if(strided.u.s.position2.VBO != This->vbo) memset(&strided.u.s.position2, 0, sizeof(strided.u.s.position2));
242 if(!(This->Flags & VBFLAG_STREAM) ) {
243 TRACE("No vertex decl used and buffer is not bound to a stream, nothing to do\n");
247 This->Flags |= VBFLAG_LOAD;
248 primitiveConvertFVFtoOffset(device->stateBlock->fvf,
249 device->stateBlock->streamStride[This->stream],
253 This->Flags &= ~VBFLAG_LOAD;
256 /* If any data that needs conversion has changed we have to reload the whole buffer */
257 if( ( (This->strided.u.s.position_transformed || strided.u.s.position_transformed) &&
258 This->strided.u.s.position.lpData != strided.u.s.position.lpData) ||
259 !((This->strided.u.s.diffuse.lpData == strided.u.s.diffuse.lpData && This->strided.u.s.diffuse.dwType == strided.u.s.diffuse.dwType) || strided.u.s.diffuse.VBO != This->vbo) ||
260 !((This->strided.u.s.specular.lpData == strided.u.s.specular.lpData && This->strided.u.s.specular.dwType == strided.u.s.specular.dwType)|| strided.u.s.specular.VBO != This->vbo) ) {
263 end = This->resource.size;
266 if (strided.u.s.position.dwStride) stride = strided.u.s.position.dwStride;
267 else if(strided.u.s.specular.dwStride) stride = strided.u.s.specular.dwStride;
268 else if(strided.u.s.diffuse.dwStride) stride = strided.u.s.diffuse.dwStride;
270 /* That means that there is nothing to fixup, just override previously modified data */
274 memcpy(&This->strided, &strided, sizeof(strided));
276 TRACE("No declaration change\n");
277 /* nothing to do - the old data is correct*/
280 This->Flags |= VBFLAG_HASDESC;
284 TRACE("Buffer not dirty, nothing to do\n");
285 This->Flags &= ~VBFLAG_DIRTY;
289 TRACE("Loading buffer\n");
291 data = HeapAlloc(GetProcessHeap(), 0, end-start);
293 ERR("Out of memory\n");
296 memcpy(data, This->resource.allocatedMemory + start, end - start);
298 fixup_vertices(data, data, stride, ( end - start) / stride,
299 strided.u.s.position.lpData, strided.u.s.position_transformed,
300 strided.u.s.diffuse.lpData, strided.u.s.diffuse.dwType == WINED3DDECLTYPE_SHORT4 || strided.u.s.diffuse.dwType == WINED3DDECLTYPE_D3DCOLOR,
301 strided.u.s.specular.lpData, strided.u.s.specular.dwType == WINED3DDECLTYPE_SHORT4 || strided.u.s.specular.dwType == WINED3DDECLTYPE_D3DCOLOR);
303 data = This->resource.allocatedMemory + start;
307 GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB, This->vbo));
308 checkGLcall("glBindBufferARB");
309 GL_EXTCALL(glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, start, end - start, data));
310 checkGLcall("glBufferSubDataARB");
314 HeapFree(GetProcessHeap(), 0, data);
315 } else if(This->Flags & VBFLAG_HASDESC) {
316 /* Free the allocated memory, then Lock will directly lock into the
317 * VBO the next time :-)
319 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
320 This->resource.allocatedMemory = NULL;
323 This->Flags &= ~VBFLAG_DIRTY;
326 static WINED3DRESOURCETYPE WINAPI IWineD3DVertexBufferImpl_GetType(IWineD3DVertexBuffer *iface) {
327 return IWineD3DResourceImpl_GetType((IWineD3DResource *)iface);
330 static HRESULT WINAPI IWineD3DVertexBufferImpl_GetParent(IWineD3DVertexBuffer *iface, IUnknown **pParent) {
331 return IWineD3DResourceImpl_GetParent((IWineD3DResource *)iface, pParent);
334 /* ******************************************************
335 IWineD3DVertexBuffer IWineD3DVertexBuffer parts follow
336 ****************************************************** */
337 static HRESULT WINAPI IWineD3DVertexBufferImpl_Lock(IWineD3DVertexBuffer *iface, UINT OffsetToLock, UINT SizeToLock, BYTE** ppbData, DWORD Flags) {
338 IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *)iface;
340 TRACE("(%p)->%d, %d, %p, %08lx\n", This, OffsetToLock, SizeToLock, ppbData, Flags);
342 InterlockedIncrement(&This->lockcount);
344 if(This->Flags & VBFLAG_DIRTY) {
345 if(This->dirtystart > OffsetToLock) This->dirtystart = OffsetToLock;
347 if(This->dirtyend < OffsetToLock + SizeToLock) This->dirtyend = OffsetToLock + SizeToLock;
349 This->dirtyend = This->resource.size;
352 This->dirtystart = OffsetToLock;
354 This->dirtyend = OffsetToLock + SizeToLock;
356 This->dirtyend = OffsetToLock + This->resource.size;
359 if(This->resource.allocatedMemory) {
360 data = This->resource.allocatedMemory;
361 This->Flags |= VBFLAG_DIRTY;
363 GLenum mode = GL_READ_WRITE_ARB;
364 /* Return data to the VBO */
366 TRACE("Locking directly into the buffer\n");
368 if((This->resource.usage & WINED3DUSAGE_WRITEONLY) || ( Flags & D3DLOCK_DISCARD) ) {
369 mode = GL_WRITE_ONLY_ARB;
370 } else if( Flags & (D3DLOCK_READONLY | D3DLOCK_NO_DIRTY_UPDATE) ) {
371 mode = GL_READ_ONLY_ARB;
375 GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB, This->vbo));
376 checkGLcall("glBindBufferARB");
377 data = GL_EXTCALL(glMapBufferARB(GL_ARRAY_BUFFER_ARB, mode));
380 ERR("glMapBuffer failed\n");
381 return WINED3DERR_INVALIDCALL;
384 *ppbData = data + OffsetToLock;
386 TRACE("(%p) : returning memory of %p (base:%p,offset:%u)\n", This, data + OffsetToLock, data, OffsetToLock);
387 /* TODO: check Flags compatibility with This->currentDesc.Usage (see MSDN) */
390 HRESULT WINAPI IWineD3DVertexBufferImpl_Unlock(IWineD3DVertexBuffer *iface) {
391 IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *) iface;
393 TRACE("(%p)\n", This);
395 lockcount = InterlockedDecrement(&This->lockcount);
397 /* Delay loading the buffer until everything is unlocked */
398 TRACE("Ignoring the unlock\n");
402 if(!This->resource.allocatedMemory) {
404 GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB, This->vbo));
405 checkGLcall("glBindBufferARB");
406 GL_EXTCALL(glUnmapBufferARB(GL_ARRAY_BUFFER_ARB));
407 checkGLcall("glUnmapBufferARB");
410 IWineD3DVertexBufferImpl_PreLoad(iface);
414 static HRESULT WINAPI IWineD3DVertexBufferImpl_GetDesc(IWineD3DVertexBuffer *iface, WINED3DVERTEXBUFFER_DESC *pDesc) {
415 IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *)iface;
417 TRACE("(%p)\n", This);
418 pDesc->Format = This->resource.format;
419 pDesc->Type = This->resource.resourceType;
420 pDesc->Usage = This->resource.usage;
421 pDesc->Pool = This->resource.pool;
422 pDesc->Size = This->resource.size;
423 pDesc->FVF = This->fvf;
427 const IWineD3DVertexBufferVtbl IWineD3DVertexBuffer_Vtbl =
430 IWineD3DVertexBufferImpl_QueryInterface,
431 IWineD3DVertexBufferImpl_AddRef,
432 IWineD3DVertexBufferImpl_Release,
433 /* IWineD3DResource */
434 IWineD3DVertexBufferImpl_GetParent,
435 IWineD3DVertexBufferImpl_GetDevice,
436 IWineD3DVertexBufferImpl_SetPrivateData,
437 IWineD3DVertexBufferImpl_GetPrivateData,
438 IWineD3DVertexBufferImpl_FreePrivateData,
439 IWineD3DVertexBufferImpl_SetPriority,
440 IWineD3DVertexBufferImpl_GetPriority,
441 IWineD3DVertexBufferImpl_PreLoad,
442 IWineD3DVertexBufferImpl_GetType,
443 /* IWineD3DVertexBuffer */
444 IWineD3DVertexBufferImpl_Lock,
445 IWineD3DVertexBufferImpl_Unlock,
446 IWineD3DVertexBufferImpl_GetDesc
449 BYTE* WINAPI IWineD3DVertexBufferImpl_GetMemory(IWineD3DVertexBuffer* iface, DWORD iOffset, GLint *vbo) {
450 IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *)iface;
454 return This->resource.allocatedMemory + iOffset;
456 return (BYTE *) iOffset;
460 HRESULT WINAPI IWineD3DVertexBufferImpl_ReleaseMemory(IWineD3DVertexBuffer* iface) {