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.
This commit is contained in:
Stefan Dösinger 2009-04-09 18:40:57 +02:00 committed by Alexandre Julliard
parent 7b2a44f257
commit 014c4bfc70
4 changed files with 93 additions and 19 deletions

View File

@ -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);
}

View File

@ -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;

View File

@ -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 +

View File

@ -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