[9/10] WineD3D: Use VBOs for index buffers

Mirek thunder.m at czela.net
Tue Feb 20 08:03:32 CST 2007


Hi, this patch caused regression in many Direct3D games. TES IV: 
Oblivion can't start game (menu works), Flatout II is completly broken, 
Alpine Sky Racing 2007 can't run, HalfLife 2 EO can't run. I tried 
rewrite GLint vbo; to GLuint vbo; but it doesn't help.

Mirek

Stefan Dösinger napsal(a):
> 
> ------------------------------------------------------------------------
> 
> From 4d52be700666b6b1e853bad296c502b5b0d76189 Mon Sep 17 00:00:00 2001
> From: Stefan Doesinger <stefan at codeweavers.com>
> Date: Mon, 19 Feb 2007 15:12:39 +0100
> Subject: [PATCH] WineD3D: Use VBOs for index buffers
> 
> This patch uses (if available) gl vertex buffer objects for static d3d index buffers
> to store indices in video memory. Therefore a new index buffer state is added which
> binds the vbo to GL_ELEMENT_ARRAY_BUFFER. DrawStrided* use the index buffer stride to
> tell if indices are used for drawing because a NULL index data pointer is valid now.
> DrawStridedSlow has to read the indices from the index buffer if it is used, because
> it can't use the data from the vbo(or only very inefficiently).
> ---
>  dlls/wined3d/device.c          |   68 ++++++++++++++++++++++++++++++++++++---
>  dlls/wined3d/drawprim.c        |   14 ++++++--
>  dlls/wined3d/indexbuffer.c     |   49 +++++++++++++++++++++++++++-
>  dlls/wined3d/state.c           |   12 +++++++
>  dlls/wined3d/wined3d_private.h |    8 ++++-
>  5 files changed, 139 insertions(+), 12 deletions(-)
> 
> diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
> index 18f3e75..af0e96a 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 2cc3a15..7cf6877 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 39a270d..0f768ac 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 0b96e54..f78720a 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 3bf0c8e..be8cd6e 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;
>  
> 
> 
> ------------------------------------------------------------------------
> 
> 



More information about the wine-devel mailing list