Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45723 Signed-off-by: Andrew Wesie awesie@gmail.com --- dlls/wined3d/adapter_gl.c | 6 ++ dlls/wined3d/query.c | 159 ++++++++++++++++++++++++++++++++++++----- dlls/wined3d/wined3d_gl.h | 2 + dlls/wined3d/wined3d_private.h | 3 + 4 files changed, 151 insertions(+), 19 deletions(-)
diff --git a/dlls/wined3d/adapter_gl.c b/dlls/wined3d/adapter_gl.c index af17df8..0ac72ba 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 }, @@ -2063,6 +2065,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) @@ -3304,7 +3308,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..d364216 100644 --- a/dlls/wined3d/query.c +++ b/dlls/wined3d/query.c @@ -25,6 +25,108 @@
WINE_DEFAULT_DEBUG_CHANNEL(d3d);
+#define INVALID_QUERY_RESULT ((UINT64)-1) + +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, 16, NULL, map_flags)); + checkGLcall("glBufferStorage"); + + query->map_ptr = GL_EXTCALL(glMapBufferRange(GL_QUERY_BUFFER, 0, 16, map_flags)); + checkGLcall("glMapBufferRange"); + + GL_EXTCALL(glBindBuffer(GL_QUERY_BUFFER, 0)); + + *query->map_ptr = INVALID_QUERY_RESULT; + 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) + *query->map_ptr = INVALID_QUERY_RESULT; +} + +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"); + + GL_EXTCALL(glGetQueryObjectui64v(id, GL_QUERY_RESULT, NULL)); + 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 != INVALID_QUERY_RESULT) + { + if (available) + *available = GL_TRUE; + if (result) + *result = *query->map_ptr; + } + 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 +441,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 +492,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 +521,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 +541,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 +681,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 +699,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 525c298..0bc9309 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 7084168..db7c772 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -1741,6 +1741,9 @@ struct wined3d_query
LONG counter_main, counter_retrieved; struct list poll_list_entry; + + GLuint buffer_object; + UINT64 *map_ptr; };
struct wined3d_event_query
On 3 September 2018 at 04:40, Andrew Wesie awesie@gmail.com wrote:
+#define INVALID_QUERY_RESULT ((UINT64)-1)
While at least for occlusion queries this would be a very unlikely result, it's not in general an invalid query result. I think ideally we'd write QUERY_RESULT_AVAILABLE to the buffer object as well, but based on the language in the extension spec, I don't see the requirement that that should flush lifted. On the other hand, based on a quick look at the Mesa source, it's not obvious to me that it actually does flush. Perhaps this is ok in practice, and just an oversight in the spec language.
Something else that's not entirely clear from the extension spec is when glGetQueryObjectui64v(..., QUERY_RESULT,...) should return. I think in practice it will return immediately, but based on the spec language, perhaps an argument could be made that it should wait until the data is in the buffer object, especially when that has been mapped with MAP_COHERENT_BIT.
On Thu, Sep 6, 2018 at 9:46 AM, Henri Verbeet hverbeet@gmail.com wrote:
While at least for occlusion queries this would be a very unlikely result, it's not in general an invalid query result. I think ideally we'd write QUERY_RESULT_AVAILABLE to the buffer object as well, but based on the language in the extension spec, I don't see the requirement that that should flush lifted. On the other hand, based on a quick look at the Mesa source, it's not obvious to me that it actually does flush. Perhaps this is ok in practice, and just an oversight in the spec language.
We could do something closer to "Example 2" in ARB_query_buffer_object spec. At a high level: - after glEndQuery, query on both GL_QUERY_RESULT_AVAILABLE and GL_QUERY_RESULT_NO_WAIT - in wined3d_query_buffer_poll, if available flag in the query buffer is set, return the result - else: emit WINED3D_CS_OP_QUERY_RESULT to cs thread, return result not available - in wined3d_cs_exec_query_result, query on both GL_QUERY_RESULT_AVAILABLE and GL_QUERY_RESULT_NO_WAIT
This avoids the default value problem and also avoids the main thread directly blocking on the cs thread and a GPU flush. It may result in a lot more work on the CPU side, though, compared to the current implementation.
I will not be near my testing machine for a while, so I can't easily benchmark and compare the performance.
On 6 September 2018 at 20:37, Andrew Wesie awesie@gmail.com wrote:
On Thu, Sep 6, 2018 at 9:46 AM, Henri Verbeet hverbeet@gmail.com wrote:
While at least for occlusion queries this would be a very unlikely result, it's not in general an invalid query result. I think ideally we'd write QUERY_RESULT_AVAILABLE to the buffer object as well, but based on the language in the extension spec, I don't see the requirement that that should flush lifted. On the other hand, based on a quick look at the Mesa source, it's not obvious to me that it actually does flush. Perhaps this is ok in practice, and just an oversight in the spec language.
We could do something closer to "Example 2" in ARB_query_buffer_object spec. At a high level:
- after glEndQuery, query on both GL_QUERY_RESULT_AVAILABLE and
GL_QUERY_RESULT_NO_WAIT
The issue I see with that example is that I think there's a potential race condition there, although it probably won't race in practice because of driver internals. I.e., consider the result of the query becoming available between retrieving QUERY_RESULT_AVAILABLE and QUERY_RESULT_NO_WAIT. On the other hand, if we were to retrieve QUERY_RESULT_AVAILABLE first, the application thread may read it before we retrieve QUERY_RESULT_NO_WAIT. It may still be possible to make it work somehow though.