From 901568b31a229783f5cf220dac49e072b627dfcb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20D=C3=B6singer?= <stefan@codeweavers.com>
Date: Mon, 25 Jan 2010 23:43:37 +0100
Subject: [PATCH 05/18] WineD3D: Implement manual fencing with GL_APPLE_flush_buffer_range

---
 dlls/wined3d/buffer.c          |   59 ++++++++++++++++++++++++++++++++++++++-
 dlls/wined3d/device.c          |   10 ++++++-
 dlls/wined3d/drawprim.c        |   18 ++++++++++++
 dlls/wined3d/wined3d_private.h |    4 +++
 4 files changed, 88 insertions(+), 3 deletions(-)

diff --git a/dlls/wined3d/buffer.c b/dlls/wined3d/buffer.c
index 64d144a..63bc299 100644
--- a/dlls/wined3d/buffer.c
+++ b/dlls/wined3d/buffer.c
@@ -101,6 +101,10 @@ static void delete_gl_buffer(struct wined3d_buffer *This)
     checkGLcall("glDeleteBuffersARB");
     LEAVE_GL();
     This->buffer_object = 0;
+
+    if(This->query) IWineD3DQuery_Release(This->query);
+    This->query = NULL;
+    This->flags &= ~WINED3D_BUFFER_APPLESYNC;
 }
 
 /* Context activation is done by the caller. */
@@ -130,10 +134,10 @@ static void buffer_create_buffer_object(struct wined3d_buffer *This)
 
     GL_EXTCALL(glGenBuffersARB(1, &This->buffer_object));
     error = glGetError();
+    LEAVE_GL();
     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;
     }
 
@@ -141,12 +145,14 @@ static void buffer_create_buffer_object(struct wined3d_buffer *This)
     {
         IWineD3DDeviceImpl_MarkStateDirty(This->resource.device, STATE_INDEXBUFFER);
     }
+
+    ENTER_GL();
     GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
     error = glGetError();
+    LEAVE_GL();
     if (error != GL_NO_ERROR)
     {
         ERR("Failed to bind the VBO with error %s (%#x)\n", debug_glerror(error), error);
-        LEAVE_GL();
         goto fail;
     }
 
@@ -160,9 +166,26 @@ static void buffer_create_buffer_object(struct wined3d_buffer *This)
 
         if(gl_info->supported[APPLE_FLUSH_BUFFER_RANGE])
         {
+            IWineD3DQuery *query;
+            IWineD3DDevice *device = (IWineD3DDevice *) This->resource.device;
+            HRESULT hr;
+
+            ENTER_GL();
             GL_EXTCALL(glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE));
             checkGLcall("glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE)");
+            LEAVE_GL();
             This->flags |= WINED3D_BUFFER_FLUSH;
+
+            hr = IWineD3DDevice_CreateQuery(device, WINED3DQUERYTYPE_EVENT, &query, NULL);
+            if(SUCCEEDED(hr))
+            {
+                IWineD3DQuery_Release(query);
+                This->flags |= WINED3D_BUFFER_APPLESYNC;
+                ENTER_GL();
+                GL_EXTCALL(glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE));
+                checkGLcall("glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_FALSE)");
+                LEAVE_GL();
+            }
         }
     }
     else
@@ -176,6 +199,7 @@ static void buffer_create_buffer_object(struct wined3d_buffer *This)
      * calling glBufferSubData on updates. Upload the actual data in case
      * we're not double buffering, so we can release the heap mem afterwards
      */
+    ENTER_GL();
     GL_EXTCALL(glBufferDataARB(This->buffer_type_hint, This->resource.size, This->resource.allocatedMemory, gl_usage));
     error = glGetError();
     LEAVE_GL();
@@ -1040,6 +1064,35 @@ static WINED3DRESOURCETYPE STDMETHODCALLTYPE buffer_GetType(IWineD3DBuffer *ifac
 
 /* IWineD3DBuffer methods */
 
+/* The caller provides a context and GL locking and binds the buffer */
+static void buffer_sync_apple(struct wined3d_buffer *This, DWORD flags)
+{
+    HRESULT hr;
+
+    /* No fencing needs to be done if the app promises not to overwrite
+     * existing data */
+    if(flags & WINED3DLOCK_NOOVERWRITE) return;
+
+    if(!This->query) return; /* No draw yet */
+    LEAVE_GL();
+    hr = wined3d_event_query_finish(This->query);
+    ENTER_GL();
+    if(FAILED(hr))
+    {
+        /* This happens when draws and maps happen in different threads and the query code cannot handle
+         * this. glFinish() does not help here because it doesn't wait for draws in different contexts. */
+        ERR("Waiting for the buffer sync query failed. Dropping async buffer updates\n");
+        LEAVE_GL();
+        IWineD3DQuery_Release(This->query);
+        ENTER_GL();
+        This->query = NULL;
+        This->flags &= ~WINED3D_BUFFER_APPLESYNC;
+
+        GL_EXTCALL(glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_TRUE));
+        checkGLcall("glBufferParameteriAPPLE(This->buffer_type_hint, GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_TRUE)");
+    }
+}
+
 static HRESULT STDMETHODCALLTYPE buffer_Map(IWineD3DBuffer *iface, UINT offset, UINT size, BYTE **data, DWORD flags)
 {
     struct wined3d_buffer *This = (struct wined3d_buffer *)iface;
@@ -1066,6 +1119,8 @@ static HRESULT STDMETHODCALLTYPE buffer_Map(IWineD3DBuffer *iface, UINT offset,
             context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
             ENTER_GL();
             GL_EXTCALL(glBindBufferARB(This->buffer_type_hint, This->buffer_object));
+            if(This->flags & WINED3D_BUFFER_APPLESYNC) buffer_sync_apple(This, flags);
+
             This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(This->buffer_type_hint, GL_READ_WRITE_ARB));
             LEAVE_GL();
             context_release(context);
diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
index d66b46e..739de5c 100644
--- a/dlls/wined3d/device.c
+++ b/dlls/wined3d/device.c
@@ -314,10 +314,18 @@ void device_stream_info_from_declaration(IWineD3DDeviceImpl *This,
      *
      * NULL streams won't be recorded in the array, UP streams won't be either. A stream is only
      * once in there. */
+    This->num_buffer_fences = 0;
     for (i = 0; i < stream_count; ++i)
     {
         IWineD3DBuffer *vb = This->stateBlock->streamSource[streams[i]];
-        if (vb) IWineD3DBuffer_PreLoad(vb);
+        if (vb)
+        {
+            IWineD3DBuffer_PreLoad(vb);
+            if(((struct wined3d_buffer *) vb)->flags & WINED3D_BUFFER_APPLESYNC)
+            {
+                This->buffer_fences[This->num_buffer_fences++] = vb;
+            }
+        }
     }
 }
 
diff --git a/dlls/wined3d/drawprim.c b/dlls/wined3d/drawprim.c
index 8250e79..1bdae6d 100644
--- a/dlls/wined3d/drawprim.c
+++ b/dlls/wined3d/drawprim.c
@@ -692,6 +692,24 @@ void drawPrimitive(IWineD3DDevice *iface, UINT index_count, UINT StartIdx, UINT
     LEAVE_GL();
     context_release(context);
 
+    for(i = 0; i < This->num_buffer_fences; i++)
+    {
+        struct wined3d_buffer *cur = (struct wined3d_buffer *) This->buffer_fences[i];
+
+        if(!cur->query)
+        {
+            HRESULT hr;
+            hr = IWineD3DDevice_CreateQuery(iface, WINED3DQUERYTYPE_EVENT, &cur->query, NULL);
+            if(FAILED(hr))
+            {
+                ERR("Creating an event query for the buffer failed\n");
+                continue;
+            }
+        }
+
+        IWineD3DQuery_Issue(cur->query, WINED3DISSUE_END);
+    }
+
     TRACE("Done all gl drawing\n");
 
     /* Diagnostics */
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 0ea4bb9..26e3da4 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -1597,6 +1597,8 @@ struct IWineD3DDeviceImpl
     /* Stream source management */
     struct wined3d_stream_info strided_streams;
     const WineDirect3DVertexStridedData *up_strided;
+    IWineD3DBuffer *buffer_fences[MAX_ATTRIBS];
+    unsigned int num_buffer_fences;
 
     /* Context management */
     struct wined3d_context **contexts;
@@ -2382,6 +2384,7 @@ struct wined3d_map_range
 #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 */
 #define WINED3D_BUFFER_FLUSH        0x10    /* Manual unmap flushing */
+#define WINED3D_BUFFER_APPLESYNC    0x20    /* Using sync as in GL_APPLE_flush_buffer_range */
 
 struct wined3d_buffer
 {
@@ -2400,6 +2403,7 @@ struct wined3d_buffer
     LONG lock_count;
     struct wined3d_map_range *maps;
     ULONG maps_size, modified_areas;
+    IWineD3DQuery *query;
 
     /* conversion stuff */
     UINT decl_change_count, full_conversion_count;
-- 
1.6.4.4

