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 WINAPI IWineD3DVertexBufferImpl_PreLoad(IWineD3DVertexBuffer *iface) {
102 IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *) iface;
103 TRACE("(%p)->()\n", This);
105 if(This->Flags & VBFLAG_LOAD) {
106 return; /* Already doing that stuff */
109 if(!This->resource.allocatedMemory) {
110 TRACE("Locking directly into VBO, nothing to do\n");
111 return; /* Direct lock into the VBO */
115 WineDirect3DVertexStridedData strided;
116 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
117 BOOL useVertexShaderFunction = FALSE, fixup = FALSE;
120 UINT start = 0, end = 0, stride = 0;
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));
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;
132 /* That means that there is nothing to fixup, just override previously modified data */
135 if(stride) fixup = TRUE;
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");
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");
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;
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
162 if(This->Flags & VBFLAG_HASDESC) return;
164 /* Check against updated declarations */
165 memset(&strided, 0, sizeof(strided));
167 if(device->stateBlock->vertexDecl != NULL) {
168 /* Check against the stream offset and make sure it is 0 */
170 This->Flags |= VBFLAG_LOAD;
171 primitiveDeclarationConvertToStridedData((IWineD3DDevice *) device,
172 useVertexShaderFunction,
176 This->Flags &= ~VBFLAG_LOAD;
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));
184 if(!(This->Flags & VBFLAG_STREAM) ) {
185 TRACE("No vertex decl used and buffer is not bound to a stream, nothing to do\n");
189 This->Flags |= VBFLAG_LOAD;
190 primitiveConvertFVFtoOffset(device->stateBlock->fvf,
191 device->stateBlock->streamStride[This->stream],
195 This->Flags &= ~VBFLAG_LOAD;
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) ) {
205 end = This->resource.size;
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;
212 /* That means that there is nothing to fixup, just override previously modified data */
216 memcpy(&This->strided, &strided, sizeof(strided));
218 TRACE("No declaration change\n");
219 /* nothing to do - the old data is correct*/
222 This->Flags |= VBFLAG_HASDESC;
226 TRACE("Buffer not dirty, nothing to do\n");
227 This->Flags &= ~VBFLAG_DIRTY;
231 TRACE("Loading buffer\n");
233 data = HeapAlloc(GetProcessHeap(), 0, end-start);
235 ERR("Out of memory\n");
238 memcpy(data, This->resource.allocatedMemory + start, end - start);
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);
246 /* rhw conversion like in drawStridedSlow */
247 if(p[3] == 1.0 || ((p[3] < eps) && (p[3] > -eps))) {
258 p = (float *) ((int) data + i * stride + (int) strided.u.s.position.lpData);
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);
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
271 * 0xff000000: Alpha mask
272 * 0x00ff0000: Blue mask
273 * 0x0000ff00: Green mask
274 * 0x000000ff: Red mask
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);
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);
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
292 *dstColor |= (srcColor & 0xff00ff00) ; /* Alpha Green */
293 *dstColor |= (srcColor & 0x00ff0000) >> 16; /* Red */
294 *dstColor |= (srcColor & 0x000000ff) << 16; /* Blue */
298 data = This->resource.allocatedMemory + start;
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");
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 :-)
314 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
315 This->resource.allocatedMemory = NULL;
318 This->Flags &= ~VBFLAG_DIRTY;
321 static WINED3DRESOURCETYPE WINAPI IWineD3DVertexBufferImpl_GetType(IWineD3DVertexBuffer *iface) {
322 return IWineD3DResourceImpl_GetType((IWineD3DResource *)iface);
325 static HRESULT WINAPI IWineD3DVertexBufferImpl_GetParent(IWineD3DVertexBuffer *iface, IUnknown **pParent) {
326 return IWineD3DResourceImpl_GetParent((IWineD3DResource *)iface, pParent);
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;
335 TRACE("(%p)->%d, %d, %p, %08lx\n", This, OffsetToLock, SizeToLock, ppbData, Flags);
337 InterlockedIncrement(&This->lockcount);
339 if(This->Flags & VBFLAG_DIRTY) {
340 if(This->dirtystart > OffsetToLock) This->dirtystart = OffsetToLock;
342 if(This->dirtyend < OffsetToLock + SizeToLock) This->dirtyend = OffsetToLock + SizeToLock;
344 This->dirtyend = This->resource.size;
347 This->dirtystart = OffsetToLock;
349 This->dirtyend = OffsetToLock + SizeToLock;
351 This->dirtyend = OffsetToLock + This->resource.size;
354 if(This->resource.allocatedMemory) {
355 data = This->resource.allocatedMemory;
356 This->Flags |= VBFLAG_DIRTY;
358 GLenum mode = GL_READ_WRITE_ARB;
359 /* Return data to the VBO */
361 TRACE("Locking directly into the buffer\n");
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;
370 GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB, This->vbo));
371 checkGLcall("glBindBufferARB");
372 data = GL_EXTCALL(glMapBufferARB(GL_ARRAY_BUFFER_ARB, mode));
375 ERR("glMapBuffer failed\n");
376 return WINED3DERR_INVALIDCALL;
379 *ppbData = data + OffsetToLock;
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) */
385 HRESULT WINAPI IWineD3DVertexBufferImpl_Unlock(IWineD3DVertexBuffer *iface) {
386 IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *) iface;
388 TRACE("(%p)\n", This);
390 lockcount = InterlockedDecrement(&This->lockcount);
392 /* Delay loading the buffer until everything is unlocked */
393 TRACE("Ignoring the unlock\n");
397 if(!This->resource.allocatedMemory) {
399 GL_EXTCALL(glBindBufferARB(GL_ARRAY_BUFFER_ARB, This->vbo));
400 checkGLcall("glBindBufferARB");
401 GL_EXTCALL(glUnmapBufferARB(GL_ARRAY_BUFFER_ARB));
402 checkGLcall("glUnmapBufferARB");
405 IWineD3DVertexBufferImpl_PreLoad(iface);
409 static HRESULT WINAPI IWineD3DVertexBufferImpl_GetDesc(IWineD3DVertexBuffer *iface, WINED3DVERTEXBUFFER_DESC *pDesc) {
410 IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *)iface;
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;
422 const IWineD3DVertexBufferVtbl IWineD3DVertexBuffer_Vtbl =
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
444 BYTE* WINAPI IWineD3DVertexBufferImpl_GetMemory(IWineD3DVertexBuffer* iface, DWORD iOffset, GLint *vbo) {
445 IWineD3DVertexBufferImpl *This = (IWineD3DVertexBufferImpl *)iface;
449 return This->resource.allocatedMemory + iOffset;
451 return (BYTE *) iOffset;
455 HRESULT WINAPI IWineD3DVertexBufferImpl_ReleaseMemory(IWineD3DVertexBuffer* iface) {