From 014c4bfc70a4d4e60f033d579d1be13a46f65170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20D=C3=B6singer?= Date: Thu, 9 Apr 2009 18:40:57 +0200 Subject: [PATCH] wined3d: Save some memory in vertex buffers. In most cases we're fine with the vbo and glMapBuffer and never use the actual heap memory copy. Try to stick to just the vbo copy and avoid allocating the extra heap memory. In case it is needed(emulation or vertex conversion), fall back to the old double buffering mode. --- dlls/wined3d/buffer.c | 96 +++++++++++++++++++++++++++++----- dlls/wined3d/device.c | 4 +- dlls/wined3d/drawprim.c | 10 ++-- dlls/wined3d/wined3d_private.h | 2 + 4 files changed, 93 insertions(+), 19 deletions(-) diff --git a/dlls/wined3d/buffer.c b/dlls/wined3d/buffer.c index 0e29de0d939..b68dcf7c6cf 100644 --- a/dlls/wined3d/buffer.c +++ b/dlls/wined3d/buffer.c @@ -100,10 +100,11 @@ static void buffer_create_buffer_object(struct wined3d_buffer *This) } /* Reserve memory for the buffer. The amount of data won't change - * so we are safe with calling glBufferData once with a NULL ptr and - * calling glBufferSubData on updates - */ - GL_EXTCALL(glBufferDataARB(This->buffer_type_hint, This->resource.size, NULL, gl_usage)); + * so we are safe with calling glBufferData once and + * calling glBufferSubData on updates. Upload the actual data in case + * we're not double buffering, so we can release the heap mem afterwards + */ + GL_EXTCALL(glBufferDataARB(This->buffer_type_hint, This->resource.size, This->resource.allocatedMemory, gl_usage)); error = glGetError(); if (error != GL_NO_ERROR) { @@ -117,7 +118,18 @@ static void buffer_create_buffer_object(struct wined3d_buffer *This) This->buffer_object_usage = gl_usage; This->dirty_start = 0; This->dirty_end = This->resource.size; - This->flags |= WINED3D_BUFFER_DIRTY; + + if(This->flags & WINED3D_BUFFER_DOUBLEBUFFER) + { + This->flags |= WINED3D_BUFFER_DIRTY; + } + else + { + HeapFree(GetProcessHeap(), 0, This->resource.heapMemory); + This->resource.allocatedMemory = NULL; + This->resource.heapMemory = NULL; + This->flags &= ~WINED3D_BUFFER_DIRTY; + } return; @@ -457,6 +469,13 @@ static void buffer_check_buffer_object_size(struct wined3d_buffer *This) { IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_INDEXBUFFER); } + + /* Rescue the data before resizing the buffer object if we do not have our backup copy */ + if(!(This->flags & WINED3D_BUFFER_DOUBLEBUFFER)) + { + buffer_get_sysmem(This); + } + ENTER_GL(); GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object)); checkGLcall("glBindBufferARB"); @@ -569,21 +588,40 @@ static ULONG STDMETHODCALLTYPE buffer_AddRef(IWineD3DBuffer *iface) return refcount; } +const BYTE *buffer_get_sysmem(struct wined3d_buffer *This) +{ + if(This->flags & WINED3D_BUFFER_DOUBLEBUFFER) return This->resource.allocatedMemory; + + This->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + RESOURCE_ALIGNMENT); + This->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1)); + ENTER_GL(); + GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object)); + GL_EXTCALL(glGetBufferSubDataARB(This->buffer_type_hint, 0, This->resource.size, This->resource.allocatedMemory)); + LEAVE_GL(); + This->flags |= WINED3D_BUFFER_DOUBLEBUFFER; + + return This->resource.allocatedMemory; +} + static void STDMETHODCALLTYPE buffer_UnLoad(IWineD3DBuffer *iface) { struct wined3d_buffer *This = (struct wined3d_buffer *)iface; TRACE("iface %p\n", iface); - /* This is easy: The whole content is shadowed in This->resource.allocatedMemory, - * so we only have to destroy the vbo. Only do it if we have a vbo, which implies - * that vbos are supported - */ if (This->buffer_object) { IWineD3DDeviceImpl *device = This->resource.wineD3DDevice; ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD); + + /* Download the buffer, but don't permanently enable double buffering */ + if(!(This->flags & WINED3D_BUFFER_DOUBLEBUFFER)) + { + buffer_get_sysmem(This); + This->flags &= ~WINED3D_BUFFER_DOUBLEBUFFER; + } + ENTER_GL(); GL_EXTCALL(glDeleteBuffersARB(1, &This->buffer_object)); checkGLcall("glDeleteBuffersARB"); @@ -765,6 +803,9 @@ static void STDMETHODCALLTYPE buffer_PreLoad(IWineD3DBuffer *iface) */ TRACE("No conversion needed\n"); + /* Nothing to do because we locked directly into the vbo */ + if(!(This->flags & WINED3D_BUFFER_DOUBLEBUFFER)) return; + if (!device->isInDraw) { ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD); @@ -778,6 +819,11 @@ static void STDMETHODCALLTYPE buffer_PreLoad(IWineD3DBuffer *iface) return; } + if(!(This->flags & WINED3D_BUFFER_DOUBLEBUFFER)) + { + buffer_get_sysmem(This); + } + /* Now for each vertex in the buffer that needs conversion */ vertices = This->resource.size / This->stride; @@ -875,10 +921,11 @@ static WINED3DRESOURCETYPE STDMETHODCALLTYPE buffer_GetType(IWineD3DBuffer *ifac static HRESULT STDMETHODCALLTYPE buffer_Map(IWineD3DBuffer *iface, UINT offset, UINT size, BYTE **data, DWORD flags) { struct wined3d_buffer *This = (struct wined3d_buffer *)iface; + LONG count; TRACE("iface %p, offset %u, size %u, data %p, flags %#x\n", iface, offset, size, data, flags); - InterlockedIncrement(&This->lock_count); + count = InterlockedIncrement(&This->lock_count); if (This->flags & WINED3D_BUFFER_DIRTY) { @@ -900,7 +947,22 @@ static HRESULT STDMETHODCALLTYPE buffer_Map(IWineD3DBuffer *iface, UINT offset, else This->dirty_end = This->resource.size; } - This->flags |= WINED3D_BUFFER_DIRTY; + if(!(This->flags & WINED3D_BUFFER_DOUBLEBUFFER) && This->buffer_object) + { + if(count == 1) + { + if(This->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB) + { + IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_INDEXBUFFER); + } + GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object)); + This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(This->buffer_type_hint, GL_READ_WRITE_ARB)); + } + } + else + { + This->flags |= WINED3D_BUFFER_DIRTY; + } *data = This->resource.allocatedMemory + offset; @@ -923,7 +985,17 @@ static HRESULT STDMETHODCALLTYPE buffer_Unmap(IWineD3DBuffer *iface) return WINED3D_OK; } - if (This->flags & WINED3D_BUFFER_HASDESC) + if(!(This->flags & WINED3D_BUFFER_DOUBLEBUFFER) && This->buffer_object) + { + if(This->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB) + { + IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_INDEXBUFFER); + } + GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object)); + GL_EXTCALL(glUnmapBufferARB(This->buffer_type_hint)); + This->resource.allocatedMemory = NULL; + } + else if (This->flags & WINED3D_BUFFER_HASDESC) { buffer_PreLoad(iface); } diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c index 879008c6a52..48952df9210 100644 --- a/dlls/wined3d/device.c +++ b/dlls/wined3d/device.c @@ -4598,7 +4598,7 @@ static HRESULT process_vertices_strided(IWineD3DDeviceImpl *This, DWORD dwDestIn FIXME("Clipping is broken and disabled for now\n"); } } else doClip = FALSE; - dest_ptr = ((char *) dest->resource.allocatedMemory) + dwDestIndex * get_flexible_vertex_size(DestFVF); + dest_ptr = ((char *) buffer_get_sysmem(dest)) + dwDestIndex * get_flexible_vertex_size(DestFVF); IWineD3DDevice_GetTransform( (IWineD3DDevice *) This, WINED3DTS_VIEW, @@ -4901,7 +4901,7 @@ static HRESULT WINAPI IWineD3DDeviceImpl_ProcessVertices(IWineD3DDevice *iface, { struct wined3d_buffer *vb = (struct wined3d_buffer *)This->stateBlock->streamSource[e->stream_idx]; e->buffer_object = 0; - e->data = (BYTE *)((unsigned long)e->data + (unsigned long)vb->resource.allocatedMemory); + e->data = (BYTE *)((unsigned long)e->data + (unsigned long)buffer_get_sysmem(vb)); ENTER_GL(); GL_EXTCALL(glDeleteBuffersARB(1, &vb->buffer_object)); vb->buffer_object = 0; diff --git a/dlls/wined3d/drawprim.c b/dlls/wined3d/drawprim.c index ee5f6268447..c96b97586a7 100644 --- a/dlls/wined3d/drawprim.c +++ b/dlls/wined3d/drawprim.c @@ -95,7 +95,7 @@ static void drawStridedSlow(IWineD3DDevice *iface, const struct wined3d_stream_i * idxData will be != NULL */ if(idxData == NULL) { - idxData = ((struct wined3d_buffer *) This->stateBlock->pIndexData)->resource.allocatedMemory; + idxData = buffer_get_sysmem((struct wined3d_buffer *) This->stateBlock->pIndexData); } if (idxSize == 2) pIdxBufS = idxData; @@ -413,7 +413,7 @@ static void drawStridedSlowVs(IWineD3DDevice *iface, const struct wined3d_stream * idxData will be != NULL */ if(idxData == NULL) { - idxData = ((struct wined3d_buffer *) stateblock->pIndexData)->resource.allocatedMemory; + idxData = buffer_get_sysmem((struct wined3d_buffer *) This->stateBlock->pIndexData); } if (idxSize == 2) pIdxBufS = idxData; @@ -512,7 +512,7 @@ static inline void drawStridedInstanced(IWineD3DDevice *iface, const struct wine { struct wined3d_buffer *vb = (struct wined3d_buffer *)stateblock->streamSource[si->elements[instancedData[j]].stream_idx]; - ptr += (long) vb->resource.allocatedMemory; + ptr += (long) buffer_get_sysmem(vb); } send_attribute(This, si->elements[instancedData[j]].format_desc->format, instancedData[j], ptr); @@ -535,7 +535,7 @@ static inline void remove_vbos(IWineD3DDeviceImpl *This, struct wined3d_stream_i { struct wined3d_buffer *vb = (struct wined3d_buffer *)This->stateBlock->streamSource[e->stream_idx]; e->buffer_object = 0; - e->data = (BYTE *)((unsigned long)e->data + (unsigned long)vb->resource.allocatedMemory); + e->data = (BYTE *)((unsigned long)e->data + (unsigned long)buffer_get_sysmem(vb)); } } } @@ -744,7 +744,7 @@ HRESULT tesselate_rectpatch(IWineD3DDeviceImpl *This, { struct wined3d_buffer *vb; vb = (struct wined3d_buffer *)This->stateBlock->streamSource[e->stream_idx]; - e->data = (BYTE *)((unsigned long)e->data + (unsigned long)vb->resource.allocatedMemory); + e->data = (BYTE *)((unsigned long)e->data + (unsigned long)buffer_get_sysmem(vb)); } vtxStride = e->stride; data = e->data + diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index 1e4f9ac468e..82afe3b700f 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -2079,6 +2079,7 @@ enum wined3d_buffer_conversion_type #define WINED3D_BUFFER_DIRTY 0x02 /* Buffer data has been modified */ #define WINED3D_BUFFER_HASDESC 0x04 /* A vertex description has been found */ #define WINED3D_BUFFER_CREATEBO 0x08 /* Attempt to create a buffer object next PreLoad */ +#define WINED3D_BUFFER_DOUBLEBUFFER 0x10 /* Use a vbo and local allocated memory */ struct wined3d_buffer { @@ -2110,6 +2111,7 @@ struct wined3d_buffer extern const IWineD3DBufferVtbl wined3d_buffer_vtbl; const BYTE *buffer_get_memory(IWineD3DBuffer *iface, UINT offset, GLuint *buffer_object); +const BYTE *buffer_get_sysmem(struct wined3d_buffer *This); /* IWineD3DRendertargetView */ struct wined3d_rendertarget_view