diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c index 18f3e75ccb6..af0e96a2338 100644 --- a/dlls/wined3d/device.c +++ b/dlls/wined3d/device.c @@ -443,21 +443,67 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateVertexBuffer(IWineD3DDevice *ifac return WINED3D_OK; } +static void CreateIndexBufferVBO(IWineD3DDeviceImpl *This, IWineD3DIndexBufferImpl *object) { + GLenum error, glUsage; + TRACE("Creating VBO for Index Buffer %p\n", object); + + ENTER_GL(); + while(glGetError()); + + GL_EXTCALL(glGenBuffersARB(1, &object->vbo)); + error = glGetError(); + if(error != GL_NO_ERROR || object->vbo == 0) { + ERR("Creating a vbo failed, continueing without vbo for this buffer\n"); + object->vbo = 0; + return; + } + + GL_EXTCALL(glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, object->vbo)); + error = glGetError(); + if(error != GL_NO_ERROR) { + ERR("Failed to bind index buffer, continueing without vbo for this buffer\n"); + goto out; + } + + /* Use static write only usage for now. Dynamic index buffers stay in sysmem, and due to the sysmem + * copy no readback will be needed + */ + glUsage = GL_STATIC_DRAW; + GL_EXTCALL(glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, object->resource.size, NULL, glUsage)); + error = glGetError(); + if(error != GL_NO_ERROR) { + ERR("Failed to initialize the index buffer\n"); + goto out; + } + LEAVE_GL(); + TRACE("Successfully created vbo %d for index buffer %p\n", object->vbo, object); + return; + +out: + GL_EXTCALL(glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0)); + GL_EXTCALL(glDeleteBuffersARB(1, &object->vbo)); + LEAVE_GL(); + object->vbo = 0; +} + static HRESULT WINAPI IWineD3DDeviceImpl_CreateIndexBuffer(IWineD3DDevice *iface, UINT Length, DWORD Usage, WINED3DFORMAT Format, WINED3DPOOL Pool, IWineD3DIndexBuffer** ppIndexBuffer, HANDLE *sharedHandle, IUnknown *parent) { IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; IWineD3DIndexBufferImpl *object; TRACE("(%p) Creating index buffer\n", This); - + /* Allocate the storage for the device */ D3DCREATERESOURCEOBJECTINSTANCE(object,IndexBuffer,WINED3DRTYPE_INDEXBUFFER, Length) - - /*TODO: use VBO's */ - if (Pool == WINED3DPOOL_DEFAULT ) { /* Allocate some system memory for now */ + + if (Pool == WINED3DPOOL_DEFAULT ) { /* We need a local copy for drawStridedSlow */ object->resource.allocatedMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,object->resource.size); } + if(Pool != WINED3DPOOL_SYSTEMMEM && !(Usage & WINED3DUSAGE_DYNAMIC) && GL_SUPPORT(ARB_VERTEX_BUFFER_OBJECT)) { + CreateIndexBufferVBO(This, object); + } + TRACE("(%p) : Len=%d, Use=%x, Format=(%u,%s), Pool=%d - Memory@%p, Iface@%p\n", This, Length, Usage, Format, debug_d3dformat(Format), Pool, object, object->resource.allocatedMemory); *ppIndexBuffer = (IWineD3DIndexBuffer *) object; @@ -2557,10 +2603,15 @@ static HRESULT WINAPI IWineD3DDeviceImpl_SetIndices(IWineD3DDevice *iface, IWine return WINED3D_OK; } - /* So far only the base vertex index is tracked */ + /* The base vertex index affects the stream sources, while + * The index buffer is a seperate index buffer state + */ if(BaseVertexIndex != oldBaseIndex) { IWineD3DDeviceImpl_MarkStateDirty(This, STATE_STREAMSRC); } + if(oldIdxs != pIndexData) { + IWineD3DDeviceImpl_MarkStateDirty(This, STATE_INDEXBUFFER); + } return WINED3D_OK; } @@ -4224,9 +4275,11 @@ static HRESULT WINAPI IWineD3DDeviceImpl_DrawIndexedPrimitive(IWineD3DDevice * UINT idxStride = 2; IWineD3DIndexBuffer *pIB; WINED3DINDEXBUFFER_DESC IdxBufDsc; + GLint vbo; pIB = This->stateBlock->pIndexData; This->stateBlock->streamIsUP = FALSE; + vbo = ((IWineD3DIndexBufferImpl *) pIB)->vbo; TRACE("(%p) : Type=(%d,%s), min=%d, CountV=%d, startIdx=%d, countP=%d\n", This, PrimitiveType, debug_d3dprimitivetype(PrimitiveType), @@ -4245,7 +4298,7 @@ static HRESULT WINAPI IWineD3DDeviceImpl_DrawIndexedPrimitive(IWineD3DDevice * } drawPrimitive(iface, PrimitiveType, primCount, 0, NumVertices, startIndex, - idxStride, ((IWineD3DIndexBufferImpl *) pIB)->resource.allocatedMemory, minIndex); + idxStride, vbo ? NULL : ((IWineD3DIndexBufferImpl *) pIB)->resource.allocatedMemory, minIndex); return WINED3D_OK; } @@ -4310,6 +4363,7 @@ static HRESULT WINAPI IWineD3DDeviceImpl_DrawIndexedPrimitiveUP(IWineD3DDevice * This->stateBlock->loadBaseVertexIndex = 0; /* Mark the state dirty until we have nicer tracking of the stream source pointers */ IWineD3DDeviceImpl_MarkStateDirty(This, STATE_VDECL); + IWineD3DDeviceImpl_MarkStateDirty(This, STATE_INDEXBUFFER); drawPrimitive(iface, PrimitiveType, PrimitiveCount, 0 /* vertexStart */, NumVertices, 0 /* indxStart */, idxStride, pIndexData, MinVertexIndex); @@ -4332,8 +4386,10 @@ static HRESULT WINAPI IWineD3DDeviceImpl_DrawPrimitiveStrided (IWineD3DDevice *i * that value. */ IWineD3DDeviceImpl_MarkStateDirty(This, STATE_VDECL); + IWineD3DDeviceImpl_MarkStateDirty(This, STATE_INDEXBUFFER); This->stateBlock->baseVertexIndex = 0; This->up_strided = DrawPrimStrideData; + This->stateBlock->streamIsUP = TRUE; drawPrimitive(iface, PrimitiveType, PrimitiveCount, 0, 0, 0, 0, NULL, 0); This->up_strided = NULL; return WINED3D_OK; diff --git a/dlls/wined3d/drawprim.c b/dlls/wined3d/drawprim.c index 2cc3a15d35a..7cf6877b2bb 100644 --- a/dlls/wined3d/drawprim.c +++ b/dlls/wined3d/drawprim.c @@ -571,7 +571,7 @@ static void drawStridedFast(IWineD3DDevice *iface,UINT numberOfVertices, GLenum const void *idxData, short idxSize, ULONG minIndex, ULONG startIdx, ULONG startVertex) { IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; - if (idxData != NULL /* This crashes sometimes!*/) { + if (idxSize != 0 /* This crashes sometimes!*/) { TRACE("(%p) : glElements(%x, %d, %d, ...)\n", This, glPrimitiveType, numberOfVertices, minIndex); idxData = idxData == (void *)-1 ? NULL : idxData; #if 1 @@ -629,7 +629,15 @@ static void drawStridedSlow(IWineD3DDevice *iface, WineDirect3DVertexStridedData TRACE("Using slow vertex array code\n"); /* Variable Initialization */ - if (idxData != NULL) { + if (idxSize != 0) { + /* Immediate mode drawing can't make use of indices in a vbo - get the data from the index buffer. + * If the index buffer has no vbo(not supported or other reason), or with user pointer drawing + * idxData will be != NULL + */ + if(idxData == NULL) { + idxData = ((IWineD3DIndexBufferImpl *) This->stateBlock->pIndexData)->resource.allocatedMemory; + } + if (idxSize == 2) pIdxBufS = (const short *) idxData; else pIdxBufL = (const long *) idxData; } @@ -1165,7 +1173,7 @@ inline void drawStridedInstanced(IWineD3DDevice *iface, WineDirect3DVertexStride IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *) iface; IWineD3DStateBlockImpl *stateblock = This->stateBlock; - if (idxData == NULL) { + if (idxSize == 0) { /* This is a nasty thing. MSDN says no hardware supports that and apps have to use software vertex processing. * We don't support this for now * diff --git a/dlls/wined3d/indexbuffer.c b/dlls/wined3d/indexbuffer.c index 39a270dd791..b16ddd00ecc 100644 --- a/dlls/wined3d/indexbuffer.c +++ b/dlls/wined3d/indexbuffer.c @@ -58,6 +58,15 @@ static ULONG WINAPI IWineD3DIndexBufferImpl_Release(IWineD3DIndexBuffer *iface) ULONG ref = InterlockedDecrement(&This->resource.ref); TRACE("(%p) : Releasing from %d\n", This, ref + 1); if (ref == 0) { + if(This->vbo) { + ENTER_GL(); + GL_EXTCALL(glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0)); + checkGLcall("glBindBufferARB"); + GL_EXTCALL(glDeleteBuffersARB(1, &This->vbo)); + checkGLcall("glDeleteBuffersARB"); + LEAVE_GL(); + } + IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface); HeapFree(GetProcessHeap(), 0, This); } @@ -108,13 +117,49 @@ static HRESULT WINAPI IWineD3DIndexBufferImpl_GetParent(IWineD3DIndexBuffer *ifa ****************************************************** */ static HRESULT WINAPI IWineD3DIndexBufferImpl_Lock(IWineD3DIndexBuffer *iface, UINT OffsetToLock, UINT SizeToLock, BYTE** ppbData, DWORD Flags) { IWineD3DIndexBufferImpl *This = (IWineD3DIndexBufferImpl *)iface; - TRACE("(%p) : no real locking yet, offset %d, size %d, Flags=%x\n", This, OffsetToLock, SizeToLock, Flags); + TRACE("(%p) : offset %d, size %d, Flags=%x\n", This, OffsetToLock, SizeToLock, Flags); + + InterlockedIncrement(&This->lockcount); *ppbData = (BYTE *)This->resource.allocatedMemory + OffsetToLock; + + if(Flags & (WINED3DLOCK_READONLY | WINED3DLOCK_NO_DIRTY_UPDATE) || This->vbo == 0) { + return WINED3D_OK; + } + + if(This->dirtystart != This->dirtyend) { + if(This->dirtystart > OffsetToLock) This->dirtystart = OffsetToLock; + if(SizeToLock) { + if(This->dirtyend < OffsetToLock + SizeToLock) This->dirtyend = OffsetToLock + SizeToLock; + } else { + This->dirtyend = This->resource.size; + } + } else { + This->dirtystart = OffsetToLock; + if(SizeToLock) + This->dirtyend = OffsetToLock + SizeToLock; + else + This->dirtyend = This->resource.size; + } + return WINED3D_OK; } static HRESULT WINAPI IWineD3DIndexBufferImpl_Unlock(IWineD3DIndexBuffer *iface) { IWineD3DIndexBufferImpl *This = (IWineD3DIndexBufferImpl *)iface; - TRACE("(%p) : stub\n", This); + unsigned long locks = InterlockedDecrement(&This->lockcount); + TRACE("(%p)\n", This); + + /* For now load in unlock */ + if(locks == 0 && This->vbo) { + ENTER_GL(); + GL_EXTCALL(glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, This->vbo)); + checkGLcall("glBindBufferARB"); + GL_EXTCALL(glBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, + This->dirtystart, This->dirtyend - This->dirtystart, This->resource.allocatedMemory + This->dirtystart)); + checkGLcall("glBufferSubDataARB"); + LEAVE_GL(); + This->dirtystart = 0; + This->dirtyend = 0; + } return WINED3D_OK; } static HRESULT WINAPI IWineD3DIndexBufferImpl_GetDesc(IWineD3DIndexBuffer *iface, WINED3DINDEXBUFFER_DESC *pDesc) { diff --git a/dlls/wined3d/state.c b/dlls/wined3d/state.c index 0b96e542e04..f78720a057c 100644 --- a/dlls/wined3d/state.c +++ b/dlls/wined3d/state.c @@ -3062,6 +3062,17 @@ static void scissorrect(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3D checkGLcall("glScissor"); } +static void indexbuffer(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DContext *context) { + if(GL_SUPPORT(ARB_VERTEX_BUFFER_OBJECT)) { + if(stateblock->streamIsUP || stateblock->pIndexData == NULL ) { + GL_EXTCALL(glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0)); + } else { + IWineD3DIndexBufferImpl *ib = (IWineD3DIndexBufferImpl *) stateblock->pIndexData; + GL_EXTCALL(glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ib->vbo)); + } + } +} + const struct StateEntry StateTable[] = { /* State name representative, apply function */ @@ -4077,6 +4088,7 @@ const struct StateEntry StateTable[] = { /*511, WINED3DTS_WORLDMATRIX(255) */ STATE_TRANSFORM(WINED3DTS_WORLDMATRIX(255)), transform_worldex }, /* Various Vertex states follow */ { /* , STATE_STREAMSRC */ STATE_VDECL, vertexdeclaration }, + { /* , STATE_INDEXBUFFER */ STATE_INDEXBUFFER, indexbuffer }, { /* , STATE_VDECL */ STATE_VDECL, vertexdeclaration }, { /* , STATE_VSHADER */ STATE_VDECL, vertexdeclaration }, { /* , STATE_VIEWPORT */ STATE_VIEWPORT, viewport }, diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index 3bf0c8e46de..be8cd6ef75d 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -426,8 +426,10 @@ typedef void (*APPLYSTATEFUNC)(DWORD state, IWineD3DStateBlockImpl *stateblock, #define STATE_STREAMSRC (STATE_TRANSFORM(WINED3DTS_WORLDMATRIX(255)) + 1) #define STATE_IS_STREAMSRC(a) ((a) == STATE_STREAMSRC) +#define STATE_INDEXBUFFER (STATE_STREAMSRC + 1) +#define STATE_IS_INDEXBUFFER(a) ((a) == STATE_INDEXBUFFER) -#define STATE_VDECL (STATE_STREAMSRC + 1) +#define STATE_VDECL (STATE_INDEXBUFFER + 1) #define STATE_IS_VDECL(a) ((a) == STATE_VDECL) #define STATE_VSHADER (STATE_VDECL + 1) @@ -793,6 +795,10 @@ typedef struct IWineD3DIndexBufferImpl const IWineD3DIndexBufferVtbl *lpVtbl; IWineD3DResourceClass resource; + GLuint vbo; + UINT dirtystart, dirtyend; + LONG lockcount; + /* WineD3DVertexBuffer specifics */ } IWineD3DIndexBufferImpl;