[PATCH v2] wined3d: Use query buffer objects for occlusion queries.

Andrew Wesie awesie at gmail.com
Wed Sep 26 14:24:50 CDT 2018


Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45723
Signed-off-by: Andrew Wesie <awesie at gmail.com>
---

v2: Remove QUERY_INVALID_VALUE. Instead, use two elements of results buffer.
    Initialize the elements to different values, then update both of them with
    the query result. Once the elements are equal, we know that we have the
    result.

 dlls/wined3d/adapter_gl.c      |   6 ++
 dlls/wined3d/query.c           | 166 ++++++++++++++++++++++++++++++++++++-----
 dlls/wined3d/wined3d_gl.h      |   2 +
 dlls/wined3d/wined3d_private.h |   3 +
 4 files changed, 158 insertions(+), 19 deletions(-)

diff --git a/dlls/wined3d/adapter_gl.c b/dlls/wined3d/adapter_gl.c
index 1722438..23462cf 100644
--- a/dlls/wined3d/adapter_gl.c
+++ b/dlls/wined3d/adapter_gl.c
@@ -58,6 +58,7 @@ static const struct wined3d_extension_map gl_extension_map[] =
     /* ARB */
     {"GL_ARB_base_instance",                ARB_BASE_INSTANCE             },
     {"GL_ARB_blend_func_extended",          ARB_BLEND_FUNC_EXTENDED       },
+    {"GL_ARB_buffer_storage",               ARB_BUFFER_STORAGE            },
     {"GL_ARB_clear_buffer_object",          ARB_CLEAR_BUFFER_OBJECT       },
     {"GL_ARB_clear_texture",                ARB_CLEAR_TEXTURE             },
     {"GL_ARB_clip_control",                 ARB_CLIP_CONTROL              },
@@ -103,6 +104,7 @@ static const struct wined3d_extension_map gl_extension_map[] =
     {"GL_ARB_point_parameters",             ARB_POINT_PARAMETERS          },
     {"GL_ARB_point_sprite",                 ARB_POINT_SPRITE              },
     {"GL_ARB_provoking_vertex",             ARB_PROVOKING_VERTEX          },
+    {"GL_ARB_query_buffer_object",          ARB_QUERY_BUFFER_OBJECT       },
     {"GL_ARB_sample_shading",               ARB_SAMPLE_SHADING            },
     {"GL_ARB_sampler_objects",              ARB_SAMPLER_OBJECTS           },
     {"GL_ARB_seamless_cube_map",            ARB_SEAMLESS_CUBE_MAP         },
@@ -2061,6 +2063,8 @@ static void load_gl_funcs(struct wined3d_gl_info *gl_info)
     /* GL_ARB_blend_func_extended */
     USE_GL_FUNC(glBindFragDataLocationIndexed)
     USE_GL_FUNC(glGetFragDataIndex)
+    /* GL_ARB_buffer_storage */
+    USE_GL_FUNC(glBufferStorage)
     /* GL_ARB_clear_buffer_object */
     USE_GL_FUNC(glClearBufferData)
     USE_GL_FUNC(glClearBufferSubData)
@@ -3302,7 +3306,9 @@ static BOOL wined3d_adapter_init_gl_caps(struct wined3d_adapter *adapter,
         {ARB_TEXTURE_STORAGE_MULTISAMPLE,  MAKEDWORD_VERSION(4, 2)},
         {ARB_TEXTURE_VIEW,                 MAKEDWORD_VERSION(4, 3)},
 
+        {ARB_BUFFER_STORAGE,               MAKEDWORD_VERSION(4, 4)},
         {ARB_CLEAR_TEXTURE,                MAKEDWORD_VERSION(4, 4)},
+        {ARB_QUERY_BUFFER_OBJECT,          MAKEDWORD_VERSION(4, 4)},
 
         {ARB_CLIP_CONTROL,                 MAKEDWORD_VERSION(4, 5)},
         {ARB_CULL_DISTANCE,                MAKEDWORD_VERSION(4, 5)},
diff --git a/dlls/wined3d/query.c b/dlls/wined3d/query.c
index 01e6bcb..607709e 100644
--- a/dlls/wined3d/query.c
+++ b/dlls/wined3d/query.c
@@ -25,6 +25,115 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
 
+static void wined3d_query_buffer_invalidate(struct wined3d_query *query)
+{
+    /* map[0] != map[1]: exact values do not have any significance. */
+    query->map_ptr[0] = 0;
+    query->map_ptr[1] = ~0;
+}
+
+static void wined3d_query_create_buffer_object(struct wined3d_context *context, struct wined3d_query *query)
+{
+    const struct wined3d_gl_info *gl_info = context->gl_info;
+    const GLuint map_flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
+    GLuint buffer_object;
+
+    if (!gl_info->supported[ARB_BUFFER_STORAGE])
+        return;
+
+    GL_EXTCALL(glGenBuffers(1, &buffer_object));
+    checkGLcall("glGenBuffers");
+
+    GL_EXTCALL(glBindBuffer(GL_QUERY_BUFFER, buffer_object));
+    checkGLcall("glBindBuffer");
+
+    GL_EXTCALL(glBufferStorage(GL_QUERY_BUFFER, sizeof(query->map_ptr[0]) * 2, NULL, map_flags));
+    checkGLcall("glBufferStorage");
+
+    query->map_ptr = GL_EXTCALL(glMapBufferRange(GL_QUERY_BUFFER, 0, sizeof(query->map_ptr[0]) * 2, map_flags));
+    checkGLcall("glMapBufferRange");
+
+    GL_EXTCALL(glBindBuffer(GL_QUERY_BUFFER, 0));
+
+    wined3d_query_buffer_invalidate(query);
+    query->buffer_object = buffer_object;
+}
+
+static void wined3d_query_destroy_buffer_object(struct wined3d_context *context, struct wined3d_query *query)
+{
+    const struct wined3d_gl_info *gl_info = context->gl_info;
+
+    GL_EXTCALL(glDeleteBuffers(1, &query->buffer_object));
+    checkGLcall("glDeleteBuffers");
+
+    query->buffer_object = 0;
+    query->map_ptr = NULL;
+}
+
+static void wined3d_query_buffer_begin(struct wined3d_query *query)
+{
+    if (query->buffer_object)
+        wined3d_query_buffer_invalidate(query);
+}
+
+static void wined3d_query_buffer_begin_cs(struct wined3d_context *context, struct wined3d_query *query)
+{
+    if (!context->gl_info->supported[ARB_QUERY_BUFFER_OBJECT])
+        return;
+
+    if (!query->buffer_object)
+        wined3d_query_create_buffer_object(context, query);
+}
+
+static BOOL wined3d_query_buffer_end_cs(struct wined3d_context *context, struct wined3d_query *query, GLuint id)
+{
+    const struct wined3d_gl_info *gl_info = context->gl_info;
+
+    if (!query->buffer_object)
+        return FALSE;
+
+    GL_EXTCALL(glBindBuffer(GL_QUERY_BUFFER, query->buffer_object));
+    checkGLcall("glBindBuffer");
+
+    /* Read the same value twice. We know we have the result if map[0] == map[1]. */
+    GL_EXTCALL(glGetQueryObjectui64v(id, GL_QUERY_RESULT, (void *)0));
+    GL_EXTCALL(glGetQueryObjectui64v(id, GL_QUERY_RESULT, (void *)sizeof(query->map_ptr[0])));
+    checkGLcall("glGetQueryObjectui64v");
+
+    GL_EXTCALL(glBindBuffer(GL_QUERY_BUFFER, 0));
+    return TRUE;
+}
+
+static BOOL wined3d_query_buffer_poll(struct wined3d_query *query, GLuint *available, UINT64 *result)
+{
+    if (!query->buffer_object)
+        return FALSE;
+
+    if (query->map_ptr && query->map_ptr[0] == query->map_ptr[1])
+    {
+        if (available)
+            *available = GL_TRUE;
+        if (result)
+            *result = query->map_ptr[0];
+    }
+    else
+    {
+        if (available)
+            *available = GL_FALSE;
+    }
+    return TRUE;
+}
+
+static BOOL wined3d_query_buffer_get_data(struct wined3d_query *query, DWORD flags)
+{
+    if (!query->buffer_object)
+        return FALSE;
+
+    if (flags & WINED3DGETDATA_FLUSH && query->device->cs->thread && !query->device->cs->queries_flushed)
+        wined3d_cs_emit_flush(query->device->cs);
+    return TRUE;
+}
+
 static UINT64 get_query_result64(GLuint id, const struct wined3d_gl_info *gl_info)
 {
     if (gl_info->supported[ARB_TIMER_QUERY])
@@ -339,6 +448,14 @@ static void wined3d_query_destroy_object(void *object)
     if (!list_empty(&query->poll_list_entry))
         list_remove(&query->poll_list_entry);
 
+    if (query->buffer_object)
+    {
+        struct wined3d_context *context;
+        context = context_acquire(query->device, NULL, 0);
+        wined3d_query_destroy_buffer_object(context, query);
+        context_release(context);
+    }
+
     /* Queries are specific to the GL context that created them. Not
      * deleting the query will obviously leak it, but that's still better
      * than potentially deleting a different query with the same id in this
@@ -382,7 +499,7 @@ HRESULT CDECL wined3d_query_get_data(struct wined3d_query *query,
         return WINED3DERR_INVALIDCALL;
     }
 
-    if (!query->device->cs->thread)
+    if (!query->device->cs->thread || wined3d_query_buffer_get_data(query, flags))
     {
         if (!query->query_ops->query_poll(query, flags))
             return S_FALSE;
@@ -411,6 +528,9 @@ HRESULT CDECL wined3d_query_issue(struct wined3d_query *query, DWORD flags)
 {
     TRACE("query %p, flags %#x.\n", query, flags);
 
+    if (flags & WINED3DISSUE_BEGIN)
+        wined3d_query_buffer_begin(query);
+
     if (flags & WINED3DISSUE_END)
         ++query->counter_main;
 
@@ -428,31 +548,35 @@ static BOOL wined3d_occlusion_query_ops_poll(struct wined3d_query *query, DWORD
 {
     struct wined3d_occlusion_query *oq = wined3d_occlusion_query_from_query(query);
     struct wined3d_device *device = query->device;
-    const struct wined3d_gl_info *gl_info;
-    struct wined3d_context *context;
-    GLuint available;
+    GLuint available = FALSE;
 
     TRACE("query %p, flags %#x.\n", query, flags);
 
-    if (!(context = context_reacquire(device, oq->context)))
+    if (!wined3d_query_buffer_poll(query, &available, &oq->samples))
     {
-        FIXME("%p Wrong thread, returning 1.\n", query);
-        oq->samples = 1;
-        return TRUE;
-    }
-    gl_info = context->gl_info;
+        const struct wined3d_gl_info *gl_info;
+        struct wined3d_context *context;
 
-    GL_EXTCALL(glGetQueryObjectuiv(oq->id, GL_QUERY_RESULT_AVAILABLE, &available));
-    TRACE("Available %#x.\n", available);
+        if (!(context = context_reacquire(device, oq->context)))
+        {
+            FIXME("%p Wrong thread, returning 1.\n", query);
+            oq->samples = 1;
+            return TRUE;
+        }
+        gl_info = context->gl_info;
 
-    if (available)
-    {
-        oq->samples = get_query_result64(oq->id, gl_info);
-        TRACE("Returning 0x%s samples.\n", wine_dbgstr_longlong(oq->samples));
+        GL_EXTCALL(glGetQueryObjectuiv(oq->id, GL_QUERY_RESULT_AVAILABLE, &available));
+        TRACE("Available %#x.\n", available);
+
+        if (available)
+            oq->samples = get_query_result64(oq->id, gl_info);
+
+        checkGLcall("poll occlusion query");
+        context_release(context);
     }
 
-    checkGLcall("poll occlusion query");
-    context_release(context);
+    if (available)
+        TRACE("Returning 0x%s samples.\n", wine_dbgstr_longlong(oq->samples));
 
     return available;
 }
@@ -564,6 +688,8 @@ static BOOL wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD
         GL_EXTCALL(glBeginQuery(GL_SAMPLES_PASSED, oq->id));
         checkGLcall("glBeginQuery()");
 
+        wined3d_query_buffer_begin_cs(context, query);
+
         context_release(context);
         oq->started = TRUE;
     }
@@ -580,8 +706,10 @@ static BOOL wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD
                 GL_EXTCALL(glEndQuery(GL_SAMPLES_PASSED));
                 checkGLcall("glEndQuery()");
 
+                /* If we cannot use query buffers, poll instead. */
+                poll = !wined3d_query_buffer_end_cs(context, query, oq->id);
+
                 context_release(context);
-                poll = TRUE;
             }
             else
             {
diff --git a/dlls/wined3d/wined3d_gl.h b/dlls/wined3d/wined3d_gl.h
index 06aa21c..2a985da 100644
--- a/dlls/wined3d/wined3d_gl.h
+++ b/dlls/wined3d/wined3d_gl.h
@@ -44,6 +44,7 @@ enum wined3d_gl_extension
     /* ARB */
     ARB_BASE_INSTANCE,
     ARB_BLEND_FUNC_EXTENDED,
+    ARB_BUFFER_STORAGE,
     ARB_CLEAR_BUFFER_OBJECT,
     ARB_CLEAR_TEXTURE,
     ARB_CLIP_CONTROL,
@@ -89,6 +90,7 @@ enum wined3d_gl_extension
     ARB_POINT_PARAMETERS,
     ARB_POINT_SPRITE,
     ARB_PROVOKING_VERTEX,
+    ARB_QUERY_BUFFER_OBJECT,
     ARB_SAMPLE_SHADING,
     ARB_SAMPLER_OBJECTS,
     ARB_SEAMLESS_CUBE_MAP,
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 40a27ff..4508764 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -1729,6 +1729,9 @@ struct wined3d_query
 
     LONG counter_main, counter_retrieved;
     struct list poll_list_entry;
+
+    GLuint buffer_object;
+    UINT64 *map_ptr;
 };
 
 struct wined3d_event_query
-- 
2.7.4




More information about the wine-devel mailing list