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_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. */
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)
{
ERR("Failed to create a VBO with error %s (%#x)\n", debug_glerror(error), error);
LEAVE_GL();
goto fail;
}
@ -77,6 +133,7 @@ static void buffer_create_buffer_object(struct wined3d_buffer *This)
if (error != GL_NO_ERROR)
{
ERR("Failed to bind the VBO with error %s (%#x)\n", debug_glerror(error), error);
LEAVE_GL();
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));
error = glGetError();
LEAVE_GL();
if (error != GL_NO_ERROR)
{
ERR("glBufferDataARB failed with error %s (%#x)\n", debug_glerror(error), error);
goto fail;
}
LEAVE_GL();
This->buffer_object_size = This->resource.size;
This->buffer_object_usage = gl_usage;
This->dirty_start = 0;
This->dirty_end = This->resource.size;
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
{
HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
This->resource.allocatedMemory = NULL;
This->resource.heapMemory = NULL;
This->flags &= ~WINED3D_BUFFER_DIRTY;
}
return;
@ -131,9 +188,14 @@ static void buffer_create_buffer_object(struct wined3d_buffer *This)
fail:
/* 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");
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;
LEAVE_GL();
buffer_clear_dirty_areas(This);
}
static BOOL buffer_process_converted_attribute(struct wined3d_buffer *This,
@ -620,6 +682,7 @@ static void STDMETHODCALLTYPE buffer_UnLoad(IWineD3DBuffer *iface)
LEAVE_GL();
This->buffer_object = 0;
This->flags |= WINED3D_BUFFER_CREATEBO; /* Recreate the buffer object next load */
buffer_clear_dirty_areas(This);
context_release(context);
@ -645,6 +708,7 @@ static ULONG STDMETHODCALLTYPE buffer_Release(IWineD3DBuffer *iface)
buffer_UnLoad(iface);
resource_cleanup((IWineD3DResource *)iface);
This->resource.parent_ops->wined3d_object_destroyed(This->resource.parent);
HeapFree(GetProcessHeap(), 0, This->maps);
HeapFree(GetProcessHeap(), 0, This);
}
@ -691,7 +755,7 @@ static void STDMETHODCALLTYPE buffer_PreLoad(IWineD3DBuffer *iface)
{
struct wined3d_buffer *This = (struct wined3d_buffer *)iface;
IWineD3DDeviceImpl *device = This->resource.device;
UINT start = 0, end = 0, vertices;
UINT start = 0, end = 0, len = 0, vertices;
struct wined3d_context *context;
BOOL decl_changed = FALSE;
unsigned int i, j;
@ -723,7 +787,7 @@ static void STDMETHODCALLTYPE buffer_PreLoad(IWineD3DBuffer *iface)
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);
++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
* 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;
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 */
WARN("Reloading buffer because of decl change\n");
start = 0;
end = This->resource.size;
}
else
{
/* No decl change, but dirty data, reload the changed stuff */
if (This->conversion_shift)
buffer_clear_dirty_areas(This);
if(!buffer_add_dirty_area(This, 0, 0))
{
if (This->dirty_start != 0 || This->dirty_end != 0)
{
FIXME("Implement partial buffer loading with shifted conversion\n");
}
ERR("buffer_add_dirty_area failed, this is not expected\n");
return;
}
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)
{
IWineD3DDeviceImpl_MarkStateDirty(This->resource.device, STATE_INDEXBUFFER);
@ -833,8 +883,14 @@ static void STDMETHODCALLTYPE buffer_PreLoad(IWineD3DBuffer *iface)
ENTER_GL();
GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
checkGLcall("glBindBufferARB");
GL_EXTCALL(glBufferSubDataARB(This->buffer_type_hint, start, end-start, This->resource.allocatedMemory + start));
checkGLcall("glBufferSubDataARB");
while(This->modified_areas)
{
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();
context_release(context);
@ -854,6 +910,15 @@ static void STDMETHODCALLTYPE buffer_PreLoad(IWineD3DBuffer *iface)
TRACE("Shifted conversion\n");
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 (j = 0; j < This->stride; ++j)
@ -893,41 +958,50 @@ static void STDMETHODCALLTYPE buffer_PreLoad(IWineD3DBuffer *iface)
else
{
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:
/* Done already */
j += 3;
break;
case CONV_D3DCOLOR:
fixup_d3dcolor((DWORD *) (data + i * This->stride + j));
j += 3;
break;
switch(This->conversion_map[j])
{
case CONV_NONE:
/* Done already */
j += 3;
break;
case CONV_D3DCOLOR:
fixup_d3dcolor((DWORD *) (data + i * This->stride + j));
j += 3;
break;
case CONV_POSITIONT:
fixup_transformed_pos((float *) (data + i * This->stride + j));
j += 15;
break;
case CONV_POSITIONT:
fixup_transformed_pos((float *) (data + i * This->stride + j));
j += 15;
break;
case CONV_FLOAT16_2:
ERR("Did not expect FLOAT16 conversion in unshifted conversion\n");
default:
FIXME("Unimplemented conversion %d in shifted conversion\n", This->conversion_map[j]);
case CONV_FLOAT16_2:
ERR("Did not expect FLOAT16 conversion in unshifted conversion\n");
default:
FIXME("Unimplemented conversion %d in shifted conversion\n", This->conversion_map[j]);
}
}
}
}
ENTER_GL();
GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
checkGLcall("glBindBufferARB");
GL_EXTCALL(glBufferSubDataARB(This->buffer_type_hint, start, end - start, data + start));
checkGLcall("glBufferSubDataARB");
LEAVE_GL();
ENTER_GL();
GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
checkGLcall("glBindBufferARB");
GL_EXTCALL(glBufferSubDataARB(This->buffer_type_hint, start, len, data + start));
checkGLcall("glBufferSubDataARB");
LEAVE_GL();
}
}
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);
if (!buffer_add_dirty_area(This, offset, size)) return E_OUTOFMEMORY;
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(count == 1)
@ -992,10 +1048,6 @@ static HRESULT STDMETHODCALLTYPE buffer_Map(IWineD3DBuffer *iface, UINT offset,
context_release(context);
}
}
else
{
This->flags |= WINED3D_BUFFER_DIRTY;
}
*data = This->resource.allocatedMemory + offset;
@ -1046,6 +1098,7 @@ static HRESULT STDMETHODCALLTYPE buffer_Unmap(IWineD3DBuffer *iface)
context_release(context);
This->resource.allocatedMemory = NULL;
buffer_clear_dirty_areas(This);
}
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;
}

View File

@ -2358,11 +2358,16 @@ enum wined3d_buffer_conversion_type
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_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 */
#define WINED3D_BUFFER_HASDESC 0x02 /* A vertex description has been found */
#define WINED3D_BUFFER_CREATEBO 0x04 /* Attempt to create a buffer object next PreLoad */
#define WINED3D_BUFFER_DOUBLEBUFFER 0x08 /* Use a vbo and local allocated memory */
struct wined3d_buffer
{
@ -2378,9 +2383,9 @@ struct wined3d_buffer
LONG bind_count;
DWORD flags;
UINT dirty_start;
UINT dirty_end;
LONG lock_count;
struct wined3d_map_range *maps;
ULONG maps_size, modified_areas;
/* conversion stuff */
UINT decl_change_count, full_conversion_count;