wined3d: Track separate dirty ranges in buffers.

This commit is contained in:
Stefan Dösinger 2009-12-28 00:33:05 +01:00 committed by Alexandre Julliard
parent 1bd98719e6
commit 716520b4b8
2 changed files with 156 additions and 88 deletions

View File

@ -36,6 +36,61 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d);
#define VB_MAXFULLCONVERSIONS 5 /* Number of full conversions before we stop converting */ #define VB_MAXFULLCONVERSIONS 5 /* Number of full conversions before we stop converting */
#define VB_RESETFULLCONVS 20 /* Reset full conversion counts after that number of draws */ #define VB_RESETFULLCONVS 20 /* Reset full conversion counts after that number of draws */
static inline BOOL buffer_add_dirty_area(struct wined3d_buffer *This, UINT offset, UINT size)
{
if (!This->buffer_object) return TRUE;
if (This->maps_size <= This->modified_areas)
{
void *new = HeapReAlloc(GetProcessHeap(), 0, This->maps,
This->maps_size * 2 * sizeof(*This->maps));
if (!new)
{
ERR("Out of memory\n");
return FALSE;
}
else
{
This->maps = new;
This->maps_size *= 2;
}
}
if(!offset && !size)
{
size = This->resource.size;
}
This->maps[This->modified_areas].offset = offset;
This->maps[This->modified_areas].size = size;
This->modified_areas++;
return TRUE;
}
static inline void buffer_clear_dirty_areas(struct wined3d_buffer *This)
{
This->modified_areas = 0;
}
static inline BOOL buffer_is_dirty(struct wined3d_buffer *This)
{
return This->modified_areas != 0;
}
static inline BOOL buffer_is_fully_dirty(struct wined3d_buffer *This)
{
unsigned int i;
for(i = 0; i < This->modified_areas; i++)
{
if(This->maps[i].offset == 0 && This->maps[i].size == This->resource.size)
{
return TRUE;
}
}
return FALSE;
}
/* Context activation is done by the caller. */ /* Context activation is done by the caller. */
static void buffer_create_buffer_object(struct wined3d_buffer *This) static void buffer_create_buffer_object(struct wined3d_buffer *This)
{ {
@ -65,6 +120,7 @@ static void buffer_create_buffer_object(struct wined3d_buffer *This)
if (!This->buffer_object || error != GL_NO_ERROR) if (!This->buffer_object || error != GL_NO_ERROR)
{ {
ERR("Failed to create a VBO with error %s (%#x)\n", debug_glerror(error), error); ERR("Failed to create a VBO with error %s (%#x)\n", debug_glerror(error), error);
LEAVE_GL();
goto fail; goto fail;
} }
@ -77,6 +133,7 @@ static void buffer_create_buffer_object(struct wined3d_buffer *This)
if (error != GL_NO_ERROR) if (error != GL_NO_ERROR)
{ {
ERR("Failed to bind the VBO with error %s (%#x)\n", debug_glerror(error), error); ERR("Failed to bind the VBO with error %s (%#x)\n", debug_glerror(error), error);
LEAVE_GL();
goto fail; goto fail;
} }
@ -101,29 +158,29 @@ static void buffer_create_buffer_object(struct wined3d_buffer *This)
*/ */
GL_EXTCALL(glBufferDataARB(This->buffer_type_hint, This->resource.size, This->resource.allocatedMemory, gl_usage)); GL_EXTCALL(glBufferDataARB(This->buffer_type_hint, This->resource.size, This->resource.allocatedMemory, gl_usage));
error = glGetError(); error = glGetError();
LEAVE_GL();
if (error != GL_NO_ERROR) if (error != GL_NO_ERROR)
{ {
ERR("glBufferDataARB failed with error %s (%#x)\n", debug_glerror(error), error); ERR("glBufferDataARB failed with error %s (%#x)\n", debug_glerror(error), error);
goto fail; goto fail;
} }
LEAVE_GL();
This->buffer_object_size = This->resource.size; This->buffer_object_size = This->resource.size;
This->buffer_object_usage = gl_usage; This->buffer_object_usage = gl_usage;
This->dirty_start = 0;
This->dirty_end = This->resource.size;
if(This->flags & WINED3D_BUFFER_DOUBLEBUFFER) if(This->flags & WINED3D_BUFFER_DOUBLEBUFFER)
{ {
This->flags |= WINED3D_BUFFER_DIRTY; if(!buffer_add_dirty_area(This, 0, 0))
{
ERR("buffer_add_dirty_area failed, this is not expected\n");
goto fail;
}
} }
else else
{ {
HeapFree(GetProcessHeap(), 0, This->resource.heapMemory); HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
This->resource.allocatedMemory = NULL; This->resource.allocatedMemory = NULL;
This->resource.heapMemory = NULL; This->resource.heapMemory = NULL;
This->flags &= ~WINED3D_BUFFER_DIRTY;
} }
return; return;
@ -131,9 +188,14 @@ static void buffer_create_buffer_object(struct wined3d_buffer *This)
fail: fail:
/* Clean up all vbo init, but continue because we can work without a vbo :-) */ /* Clean up all vbo init, but continue because we can work without a vbo :-) */
ERR("Failed to create a vertex buffer object. Continuing, but performance issues may occur\n"); ERR("Failed to create a vertex buffer object. Continuing, but performance issues may occur\n");
if (This->buffer_object) GL_EXTCALL(glDeleteBuffersARB(1, &This->buffer_object)); if (This->buffer_object)
{
ENTER_GL();
GL_EXTCALL(glDeleteBuffersARB(1, &This->buffer_object));
LEAVE_GL();
}
This->buffer_object = 0; This->buffer_object = 0;
LEAVE_GL(); buffer_clear_dirty_areas(This);
} }
static BOOL buffer_process_converted_attribute(struct wined3d_buffer *This, static BOOL buffer_process_converted_attribute(struct wined3d_buffer *This,
@ -620,6 +682,7 @@ static void STDMETHODCALLTYPE buffer_UnLoad(IWineD3DBuffer *iface)
LEAVE_GL(); LEAVE_GL();
This->buffer_object = 0; This->buffer_object = 0;
This->flags |= WINED3D_BUFFER_CREATEBO; /* Recreate the buffer object next load */ This->flags |= WINED3D_BUFFER_CREATEBO; /* Recreate the buffer object next load */
buffer_clear_dirty_areas(This);
context_release(context); context_release(context);
@ -645,6 +708,7 @@ static ULONG STDMETHODCALLTYPE buffer_Release(IWineD3DBuffer *iface)
buffer_UnLoad(iface); buffer_UnLoad(iface);
resource_cleanup((IWineD3DResource *)iface); resource_cleanup((IWineD3DResource *)iface);
This->resource.parent_ops->wined3d_object_destroyed(This->resource.parent); This->resource.parent_ops->wined3d_object_destroyed(This->resource.parent);
HeapFree(GetProcessHeap(), 0, This->maps);
HeapFree(GetProcessHeap(), 0, This); HeapFree(GetProcessHeap(), 0, This);
} }
@ -691,7 +755,7 @@ static void STDMETHODCALLTYPE buffer_PreLoad(IWineD3DBuffer *iface)
{ {
struct wined3d_buffer *This = (struct wined3d_buffer *)iface; struct wined3d_buffer *This = (struct wined3d_buffer *)iface;
IWineD3DDeviceImpl *device = This->resource.device; IWineD3DDeviceImpl *device = This->resource.device;
UINT start = 0, end = 0, vertices; UINT start = 0, end = 0, len = 0, vertices;
struct wined3d_context *context; struct wined3d_context *context;
BOOL decl_changed = FALSE; BOOL decl_changed = FALSE;
unsigned int i, j; unsigned int i, j;
@ -723,7 +787,7 @@ static void STDMETHODCALLTYPE buffer_PreLoad(IWineD3DBuffer *iface)
This->flags |= WINED3D_BUFFER_HASDESC; This->flags |= WINED3D_BUFFER_HASDESC;
} }
if (!decl_changed && !(This->flags & WINED3D_BUFFER_HASDESC && This->flags & WINED3D_BUFFER_DIRTY)) if (!decl_changed && !(This->flags & WINED3D_BUFFER_HASDESC && buffer_is_dirty(This)))
{ {
context_release(context); context_release(context);
++This->draw_count; ++This->draw_count;
@ -764,7 +828,7 @@ static void STDMETHODCALLTYPE buffer_PreLoad(IWineD3DBuffer *iface)
* changes it every minute drop the VBO after VB_MAX_DECL_CHANGES minutes. So count draws without * changes it every minute drop the VBO after VB_MAX_DECL_CHANGES minutes. So count draws without
* decl changes and reset the decl change count after a specific number of them * decl changes and reset the decl change count after a specific number of them
*/ */
if(This->dirty_start == 0 && This->dirty_end == This->resource.size) if(buffer_is_fully_dirty(This))
{ {
++This->full_conversion_count; ++This->full_conversion_count;
if(This->full_conversion_count > VB_MAXFULLCONVERSIONS) if(This->full_conversion_count > VB_MAXFULLCONVERSIONS)
@ -788,28 +852,14 @@ static void STDMETHODCALLTYPE buffer_PreLoad(IWineD3DBuffer *iface)
{ {
/* The declaration changed, reload the whole buffer */ /* The declaration changed, reload the whole buffer */
WARN("Reloading buffer because of decl change\n"); WARN("Reloading buffer because of decl change\n");
start = 0; buffer_clear_dirty_areas(This);
end = This->resource.size; if(!buffer_add_dirty_area(This, 0, 0))
}
else
{
/* No decl change, but dirty data, reload the changed stuff */
if (This->conversion_shift)
{ {
if (This->dirty_start != 0 || This->dirty_end != 0) ERR("buffer_add_dirty_area failed, this is not expected\n");
{ return;
FIXME("Implement partial buffer loading with shifted conversion\n");
}
} }
start = This->dirty_start;
end = This->dirty_end;
} }
/* Mark the buffer clean */
This->flags &= ~WINED3D_BUFFER_DIRTY;
This->dirty_start = 0;
This->dirty_end = 0;
if(This->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB) if(This->buffer_type_hint == GL_ELEMENT_ARRAY_BUFFER_ARB)
{ {
IWineD3DDeviceImpl_MarkStateDirty(This->resource.device, STATE_INDEXBUFFER); IWineD3DDeviceImpl_MarkStateDirty(This->resource.device, STATE_INDEXBUFFER);
@ -833,8 +883,14 @@ static void STDMETHODCALLTYPE buffer_PreLoad(IWineD3DBuffer *iface)
ENTER_GL(); ENTER_GL();
GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object)); GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
checkGLcall("glBindBufferARB"); checkGLcall("glBindBufferARB");
GL_EXTCALL(glBufferSubDataARB(This->buffer_type_hint, start, end-start, This->resource.allocatedMemory + start)); while(This->modified_areas)
checkGLcall("glBufferSubDataARB"); {
This->modified_areas--;
start = This->maps[This->modified_areas].offset;
len = This->maps[This->modified_areas].size;
GL_EXTCALL(glBufferSubDataARB(This->buffer_type_hint, start, len, This->resource.allocatedMemory + start));
checkGLcall("glBufferSubDataARB");
}
LEAVE_GL(); LEAVE_GL();
context_release(context); context_release(context);
@ -854,6 +910,15 @@ static void STDMETHODCALLTYPE buffer_PreLoad(IWineD3DBuffer *iface)
TRACE("Shifted conversion\n"); TRACE("Shifted conversion\n");
data = HeapAlloc(GetProcessHeap(), 0, vertices * This->conversion_stride); data = HeapAlloc(GetProcessHeap(), 0, vertices * This->conversion_stride);
start = 0;
len = This->resource.size;
end = start + len;
if (This->maps[0].offset || This->maps[0].size != This->resource.size)
{
FIXME("Implement partial buffer load with shifted conversion\n");
}
for (i = start / This->stride; i < min((end / This->stride) + 1, vertices); ++i) for (i = start / This->stride; i < min((end / This->stride) + 1, vertices); ++i)
{ {
for (j = 0; j < This->stride; ++j) for (j = 0; j < This->stride; ++j)
@ -893,41 +958,50 @@ static void STDMETHODCALLTYPE buffer_PreLoad(IWineD3DBuffer *iface)
else else
{ {
data = HeapAlloc(GetProcessHeap(), 0, This->resource.size); data = HeapAlloc(GetProcessHeap(), 0, This->resource.size);
memcpy(data + start, This->resource.allocatedMemory + start, end - start);
for (i = start / This->stride; i < min((end / This->stride) + 1, vertices); ++i) while(This->modified_areas)
{ {
for (j = 0; j < This->stride; ++j) This->modified_areas--;
start = This->maps[This->modified_areas].offset;
len = This->maps[This->modified_areas].size;
end = start + len;
memcpy(data + start, This->resource.allocatedMemory + start, end - start);
for (i = start / This->stride; i < min((end / This->stride) + 1, vertices); ++i)
{ {
switch(This->conversion_map[j]) for (j = 0; j < This->stride; ++j)
{ {
case CONV_NONE: switch(This->conversion_map[j])
/* Done already */ {
j += 3; case CONV_NONE:
break; /* Done already */
case CONV_D3DCOLOR: j += 3;
fixup_d3dcolor((DWORD *) (data + i * This->stride + j)); break;
j += 3; case CONV_D3DCOLOR:
break; fixup_d3dcolor((DWORD *) (data + i * This->stride + j));
j += 3;
break;
case CONV_POSITIONT: case CONV_POSITIONT:
fixup_transformed_pos((float *) (data + i * This->stride + j)); fixup_transformed_pos((float *) (data + i * This->stride + j));
j += 15; j += 15;
break; break;
case CONV_FLOAT16_2: case CONV_FLOAT16_2:
ERR("Did not expect FLOAT16 conversion in unshifted conversion\n"); ERR("Did not expect FLOAT16 conversion in unshifted conversion\n");
default: default:
FIXME("Unimplemented conversion %d in shifted conversion\n", This->conversion_map[j]); FIXME("Unimplemented conversion %d in shifted conversion\n", This->conversion_map[j]);
}
} }
} }
}
ENTER_GL(); ENTER_GL();
GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object)); GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
checkGLcall("glBindBufferARB"); checkGLcall("glBindBufferARB");
GL_EXTCALL(glBufferSubDataARB(This->buffer_type_hint, start, end - start, data + start)); GL_EXTCALL(glBufferSubDataARB(This->buffer_type_hint, start, len, data + start));
checkGLcall("glBufferSubDataARB"); checkGLcall("glBufferSubDataARB");
LEAVE_GL(); LEAVE_GL();
}
} }
HeapFree(GetProcessHeap(), 0, data); HeapFree(GetProcessHeap(), 0, data);
@ -950,28 +1024,10 @@ static HRESULT STDMETHODCALLTYPE buffer_Map(IWineD3DBuffer *iface, UINT offset,
TRACE("iface %p, offset %u, size %u, data %p, flags %#x\n", iface, offset, size, data, flags); TRACE("iface %p, offset %u, size %u, data %p, flags %#x\n", iface, offset, size, data, flags);
if (!buffer_add_dirty_area(This, offset, size)) return E_OUTOFMEMORY;
count = InterlockedIncrement(&This->lock_count); count = InterlockedIncrement(&This->lock_count);
if (This->flags & WINED3D_BUFFER_DIRTY)
{
if (This->dirty_start > offset) This->dirty_start = offset;
if (size)
{
if (This->dirty_end < offset + size) This->dirty_end = offset + size;
}
else
{
This->dirty_end = This->resource.size;
}
}
else
{
This->dirty_start = offset;
if (size) This->dirty_end = offset + size;
else This->dirty_end = This->resource.size;
}
if(!(This->flags & WINED3D_BUFFER_DOUBLEBUFFER) && This->buffer_object) if(!(This->flags & WINED3D_BUFFER_DOUBLEBUFFER) && This->buffer_object)
{ {
if(count == 1) if(count == 1)
@ -992,10 +1048,6 @@ static HRESULT STDMETHODCALLTYPE buffer_Map(IWineD3DBuffer *iface, UINT offset,
context_release(context); context_release(context);
} }
} }
else
{
This->flags |= WINED3D_BUFFER_DIRTY;
}
*data = This->resource.allocatedMemory + offset; *data = This->resource.allocatedMemory + offset;
@ -1046,6 +1098,7 @@ static HRESULT STDMETHODCALLTYPE buffer_Unmap(IWineD3DBuffer *iface)
context_release(context); context_release(context);
This->resource.allocatedMemory = NULL; This->resource.allocatedMemory = NULL;
buffer_clear_dirty_areas(This);
} }
else if (This->flags & WINED3D_BUFFER_HASDESC) else if (This->flags & WINED3D_BUFFER_HASDESC)
{ {
@ -1169,5 +1222,15 @@ HRESULT buffer_init(struct wined3d_buffer *buffer, IWineD3DDeviceImpl *device,
} }
} }
buffer->maps = HeapAlloc(GetProcessHeap(), 0, sizeof(*buffer->maps));
if (!buffer->maps)
{
ERR("Out of memory\n");
buffer_UnLoad((IWineD3DBuffer *)buffer);
resource_cleanup((IWineD3DResource *)buffer);
return E_OUTOFMEMORY;
}
buffer->maps_size = 1;
return WINED3D_OK; return WINED3D_OK;
} }

View File

@ -2358,11 +2358,16 @@ enum wined3d_buffer_conversion_type
CONV_FLOAT16_2, /* Also handles FLOAT16_4 */ CONV_FLOAT16_2, /* Also handles FLOAT16_4 */
}; };
struct wined3d_map_range
{
UINT offset;
UINT size;
};
#define WINED3D_BUFFER_OPTIMIZED 0x01 /* Optimize has been called for the buffer */ #define WINED3D_BUFFER_OPTIMIZED 0x01 /* Optimize has been called for the buffer */
#define WINED3D_BUFFER_DIRTY 0x02 /* Buffer data has been modified */ #define WINED3D_BUFFER_HASDESC 0x02 /* A vertex description has been found */
#define WINED3D_BUFFER_HASDESC 0x04 /* A vertex description has been found */ #define WINED3D_BUFFER_CREATEBO 0x04 /* Attempt to create a buffer object next PreLoad */
#define WINED3D_BUFFER_CREATEBO 0x08 /* Attempt to create a buffer object next PreLoad */ #define WINED3D_BUFFER_DOUBLEBUFFER 0x08 /* Use a vbo and local allocated memory */
#define WINED3D_BUFFER_DOUBLEBUFFER 0x10 /* Use a vbo and local allocated memory */
struct wined3d_buffer struct wined3d_buffer
{ {
@ -2378,9 +2383,9 @@ struct wined3d_buffer
LONG bind_count; LONG bind_count;
DWORD flags; DWORD flags;
UINT dirty_start;
UINT dirty_end;
LONG lock_count; LONG lock_count;
struct wined3d_map_range *maps;
ULONG maps_size, modified_areas;
/* conversion stuff */ /* conversion stuff */
UINT decl_change_count, full_conversion_count; UINT decl_change_count, full_conversion_count;