diff --git a/dlls/wined3d/context.c b/dlls/wined3d/context.c index cfeae83b715..e0c51f92878 100644 --- a/dlls/wined3d/context.c +++ b/dlls/wined3d/context.c @@ -892,6 +892,47 @@ void context_free_timestamp_query(struct wined3d_timestamp_query *query) context->free_timestamp_queries[context->free_timestamp_query_count++] = query->id; } +void context_alloc_so_statistics_query(struct wined3d_context *context, + struct wined3d_so_statistics_query *query) +{ + const struct wined3d_gl_info *gl_info = context->gl_info; + + if (context->free_so_statistics_query_count) + { + query->u = context->free_so_statistics_queries[--context->free_so_statistics_query_count]; + } + else + { + GL_EXTCALL(glGenQueries(ARRAY_SIZE(query->u.id), query->u.id)); + checkGLcall("glGenQueries"); + + TRACE("Allocated SO statistics queries %u, %u in context %p.\n", + query->u.id[0], query->u.id[1], context); + } + + query->context = context; + list_add_head(&context->so_statistics_queries, &query->entry); +} + +void context_free_so_statistics_query(struct wined3d_so_statistics_query *query) +{ + struct wined3d_context *context = query->context; + + list_remove(&query->entry); + query->context = NULL; + + if (!wined3d_array_reserve((void **)&context->free_so_statistics_queries, + &context->free_so_statistics_query_size, context->free_so_statistics_query_count + 1, + sizeof(*context->free_so_statistics_queries))) + { + ERR("Failed to grow free list, leaking GL queries %u, %u in context %p.\n", + query->u.id[0], query->u.id[1], context); + return; + } + + context->free_so_statistics_queries[context->free_so_statistics_query_count++] = query->u; +} + typedef void (context_fbo_entry_func_t)(struct wined3d_context *context, struct fbo_entry *entry); static void context_enum_fbo_entries(const struct wined3d_device *device, @@ -1183,6 +1224,7 @@ static void context_update_window(struct wined3d_context *context) static void context_destroy_gl_resources(struct wined3d_context *context) { const struct wined3d_gl_info *gl_info = context->gl_info; + struct wined3d_so_statistics_query *so_statistics_query; struct wined3d_timestamp_query *timestamp_query; struct wined3d_occlusion_query *occlusion_query; struct wined3d_event_query *event_query; @@ -1199,6 +1241,14 @@ static void context_destroy_gl_resources(struct wined3d_context *context) else if (context->valid) context_set_gl_context(context); + LIST_FOR_EACH_ENTRY(so_statistics_query, &context->so_statistics_queries, + struct wined3d_so_statistics_query, entry) + { + if (context->valid) + GL_EXTCALL(glDeleteQueries(ARRAY_SIZE(so_statistics_query->u.id), so_statistics_query->u.id)); + so_statistics_query->context = NULL; + } + LIST_FOR_EACH_ENTRY(timestamp_query, &context->timestamp_queries, struct wined3d_timestamp_query, entry) { if (context->valid) @@ -1246,6 +1296,15 @@ static void context_destroy_gl_resources(struct wined3d_context *context) GL_EXTCALL(glDeleteProgramsARB(1, &context->dummy_arbfp_prog)); } + if (gl_info->supported[WINED3D_GL_PRIMITIVE_QUERY]) + { + for (i = 0; i < context->free_so_statistics_query_count; ++i) + { + union wined3d_gl_so_statistics_query *q = &context->free_so_statistics_queries[i]; + GL_EXTCALL(glDeleteQueries(ARRAY_SIZE(q->id), q->id)); + } + } + if (gl_info->supported[ARB_TIMER_QUERY]) GL_EXTCALL(glDeleteQueries(context->free_timestamp_query_count, context->free_timestamp_queries)); @@ -1731,15 +1790,16 @@ struct wined3d_context *context_create(struct wined3d_swapchain *swapchain, if (!(ret->free_occlusion_queries = wined3d_calloc(ret->free_occlusion_query_size, sizeof(*ret->free_occlusion_queries)))) goto out; - list_init(&ret->occlusion_queries); ret->free_event_query_size = 4; if (!(ret->free_event_queries = wined3d_calloc(ret->free_event_query_size, sizeof(*ret->free_event_queries)))) goto out; - list_init(&ret->event_queries); + + list_init(&ret->so_statistics_queries); + list_init(&ret->fbo_list); list_init(&ret->fbo_destroy_list); diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c index 7c6a8c045e0..7273d6cec10 100644 --- a/dlls/wined3d/directx.c +++ b/dlls/wined3d/directx.c @@ -4141,6 +4141,22 @@ static BOOL wined3d_adapter_init_gl_caps(struct wined3d_adapter *adapter, if (!counter_bits) gl_info->supported[ARB_TIMER_QUERY] = FALSE; } + if (gl_version >= MAKEDWORD_VERSION(3, 0)) + { + GLint counter_bits; + + gl_info->supported[WINED3D_GL_PRIMITIVE_QUERY] = TRUE; + + GL_EXTCALL(glGetQueryiv(GL_PRIMITIVES_GENERATED, GL_QUERY_COUNTER_BITS, &counter_bits)); + TRACE("Primitives query counter has %d bits.\n", counter_bits); + if (!counter_bits) + gl_info->supported[WINED3D_GL_PRIMITIVE_QUERY] = FALSE; + + GL_EXTCALL(glGetQueryiv(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, GL_QUERY_COUNTER_BITS, &counter_bits)); + TRACE("Transform feedback primitives query counter has %d bits.\n", counter_bits); + if (!counter_bits) + gl_info->supported[WINED3D_GL_PRIMITIVE_QUERY] = FALSE; + } if (gl_info->supported[ARB_VIEWPORT_ARRAY]) { GLint subpixel_bits; diff --git a/dlls/wined3d/query.c b/dlls/wined3d/query.c index 3cfbd8431ac..28db6be239b 100644 --- a/dlls/wined3d/query.c +++ b/dlls/wined3d/query.c @@ -56,6 +56,11 @@ static struct wined3d_timestamp_query *wined3d_timestamp_query_from_query(struct return CONTAINING_RECORD(query, struct wined3d_timestamp_query, query); } +static struct wined3d_so_statistics_query *wined3d_so_statistics_query_from_query(struct wined3d_query *query) +{ + return CONTAINING_RECORD(query, struct wined3d_so_statistics_query, query); +} + BOOL wined3d_event_query_supported(const struct wined3d_gl_info *gl_info) { return gl_info->supported[ARB_SYNC] || gl_info->supported[NV_FENCE] || gl_info->supported[APPLE_FENCE]; @@ -294,6 +299,16 @@ static void wined3d_query_destroy_object(void *object) { HeapFree(GetProcessHeap(), 0, query); } + else if (query->type == WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0 + || query->type == WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM1 + || query->type == WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM2 + || query->type == WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM3) + { + struct wined3d_so_statistics_query *pq = wined3d_so_statistics_query_from_query(query); + if (pq->context) + context_free_so_statistics_query(pq); + HeapFree(GetProcessHeap(), 0, pq); + } else { ERR("Query %p has invalid type %#x.\n", query, query->type); @@ -637,6 +652,127 @@ static BOOL wined3d_timestamp_disjoint_query_ops_issue(struct wined3d_query *que return FALSE; } +static BOOL wined3d_so_statistics_query_ops_poll(struct wined3d_query *query, DWORD flags) +{ + struct wined3d_so_statistics_query *pq = wined3d_so_statistics_query_from_query(query); + struct wined3d_device *device = query->device; + GLuint written_available, generated_available; + const struct wined3d_gl_info *gl_info; + struct wined3d_context *context; + + TRACE("query %p, flags %#x.\n", query, flags); + + if (!(context = context_reacquire(device, pq->context))) + { + FIXME("%p Wrong thread, returning 0 primitives.\n", query); + memset(&pq->statistics, 0, sizeof(pq->statistics)); + return TRUE; + } + gl_info = context->gl_info; + + GL_EXTCALL(glGetQueryObjectuiv(pq->u.query.written, + GL_QUERY_RESULT_AVAILABLE, &written_available)); + GL_EXTCALL(glGetQueryObjectuiv(pq->u.query.generated, + GL_QUERY_RESULT_AVAILABLE, &generated_available)); + TRACE("Available %#x, %#x.\n", written_available, generated_available); + + if (written_available && generated_available) + { + if (gl_info->supported[ARB_TIMER_QUERY]) + { + GLuint64 result; + GL_EXTCALL(glGetQueryObjectui64v(pq->u.query.written, GL_QUERY_RESULT, &result)); + pq->statistics.primitives_written = result; + GL_EXTCALL(glGetQueryObjectui64v(pq->u.query.generated, GL_QUERY_RESULT, &result)); + pq->statistics.primitives_generated = result; + } + else + { + GLuint result; + GL_EXTCALL(glGetQueryObjectuiv(pq->u.query.written, GL_QUERY_RESULT, &result)); + pq->statistics.primitives_written = result; + GL_EXTCALL(glGetQueryObjectuiv(pq->u.query.generated, GL_QUERY_RESULT, &result)); + pq->statistics.primitives_generated = result; + } + TRACE("Returning %s, %s primitives.\n", + wine_dbgstr_longlong(pq->statistics.primitives_written), + wine_dbgstr_longlong(pq->statistics.primitives_generated)); + } + + checkGLcall("poll SO statistics query"); + context_release(context); + + return written_available && generated_available; +} + +static BOOL wined3d_so_statistics_query_ops_issue(struct wined3d_query *query, DWORD flags) +{ + struct wined3d_so_statistics_query *pq = wined3d_so_statistics_query_from_query(query); + struct wined3d_device *device = query->device; + const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; + struct wined3d_context *context; + BOOL poll = FALSE; + + TRACE("query %p, flags %#x.\n", query, flags); + + if (flags & WINED3DISSUE_BEGIN) + { + if (pq->started) + { + if ((context = context_reacquire(device, pq->context))) + { + GL_EXTCALL(glEndQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, pq->stream_idx)); + GL_EXTCALL(glEndQueryIndexed(GL_PRIMITIVES_GENERATED, pq->stream_idx)); + } + else + { + FIXME("Wrong thread, can't restart query.\n"); + context_free_so_statistics_query(pq); + context = context_acquire(device, NULL, 0); + context_alloc_so_statistics_query(context, pq); + } + } + else + { + if (pq->context) + context_free_so_statistics_query(pq); + context = context_acquire(device, NULL, 0); + context_alloc_so_statistics_query(context, pq); + } + + GL_EXTCALL(glBeginQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, + pq->stream_idx, pq->u.query.written)); + GL_EXTCALL(glBeginQueryIndexed(GL_PRIMITIVES_GENERATED, + pq->stream_idx, pq->u.query.generated)); + checkGLcall("begin query"); + + context_release(context); + pq->started = TRUE; + } + if (flags & WINED3DISSUE_END) + { + if (pq->started) + { + if ((context = context_reacquire(device, pq->context))) + { + GL_EXTCALL(glEndQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, pq->stream_idx)); + GL_EXTCALL(glEndQueryIndexed(GL_PRIMITIVES_GENERATED, pq->stream_idx)); + checkGLcall("end query"); + + context_release(context); + poll = TRUE; + } + else + { + FIXME("Wrong thread, can't end query.\n"); + } + } + pq->started = FALSE; + } + + return poll; +} + static const struct wined3d_query_ops event_query_ops = { wined3d_event_query_ops_poll, @@ -785,6 +921,64 @@ static HRESULT wined3d_timestamp_disjoint_query_create(struct wined3d_device *de return WINED3D_OK; } +static const struct wined3d_query_ops so_statistics_query_ops = +{ + wined3d_so_statistics_query_ops_poll, + wined3d_so_statistics_query_ops_issue, +}; + +static HRESULT wined3d_so_statistics_query_create(struct wined3d_device *device, + enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops, + struct wined3d_query **query) +{ + const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; + struct wined3d_so_statistics_query *object; + + TRACE("device %p, type %#x, parent %p, parent_ops %p, query %p.\n", + device, type, parent, parent_ops, query); + + if (!gl_info->supported[WINED3D_GL_PRIMITIVE_QUERY]) + { + WARN("OpenGL implementation does not support primitive queries.\n"); + return WINED3DERR_NOTAVAILABLE; + } + if (!gl_info->supported[ARB_TRANSFORM_FEEDBACK3]) + { + WARN("OpenGL implementation does not support indexed queries.\n"); + return WINED3DERR_NOTAVAILABLE; + } + + if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)))) + return E_OUTOFMEMORY; + + switch (type) + { + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0: + object->stream_idx = 0; + break; + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM1: + object->stream_idx = 1; + break; + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM2: + object->stream_idx = 2; + break; + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM3: + object->stream_idx = 3; + break; + default: + HeapFree(GetProcessHeap(), 0, object); + return WINED3DERR_NOTAVAILABLE; + } + + wined3d_query_init(&object->query, device, type, &object->statistics, + sizeof(object->statistics), &so_statistics_query_ops, parent, parent_ops); + + TRACE("Created query %p.\n", object); + *query = &object->query; + + return WINED3D_OK; +} + HRESULT CDECL wined3d_query_create(struct wined3d_device *device, enum wined3d_query_type type, void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_query **query) { @@ -806,6 +1000,12 @@ HRESULT CDECL wined3d_query_create(struct wined3d_device *device, enum wined3d_q case WINED3D_QUERY_TYPE_TIMESTAMP_FREQ: return wined3d_timestamp_disjoint_query_create(device, type, parent, parent_ops, query); + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM1: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM2: + case WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM3: + return wined3d_so_statistics_query_create(device, type, parent, parent_ops, query); + default: FIXME("Unhandled query type %#x.\n", type); return WINED3DERR_NOTAVAILABLE; diff --git a/dlls/wined3d/wined3d_gl.h b/dlls/wined3d/wined3d_gl.h index b8718129ed1..34599fd20bd 100644 --- a/dlls/wined3d/wined3d_gl.h +++ b/dlls/wined3d/wined3d_gl.h @@ -201,8 +201,9 @@ enum wined3d_gl_extension WGL_WINE_QUERY_RENDERER, /* Internally used */ WINED3D_GL_BLEND_EQUATION, - WINED3D_GL_NORMALIZED_TEXRECT, WINED3D_GL_LEGACY_CONTEXT, + WINED3D_GL_NORMALIZED_TEXRECT, + WINED3D_GL_PRIMITIVE_QUERY, WINED3D_GL_VERSION_2_0, WINED3D_GL_VERSION_3_2, WINED3D_GL_VERSION_4_3, diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index eb5d3160d19..6139c677c76 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -1695,6 +1695,32 @@ struct wined3d_timestamp_query void context_alloc_timestamp_query(struct wined3d_context *context, struct wined3d_timestamp_query *query) DECLSPEC_HIDDEN; void context_free_timestamp_query(struct wined3d_timestamp_query *query) DECLSPEC_HIDDEN; +union wined3d_gl_so_statistics_query +{ + GLuint id[2]; + struct + { + GLuint written; + GLuint generated; + } query; +}; + +struct wined3d_so_statistics_query +{ + struct wined3d_query query; + + struct list entry; + union wined3d_gl_so_statistics_query u; + struct wined3d_context *context; + unsigned int stream_idx; + struct wined3d_query_data_so_statistics statistics; + BOOL started; +}; + +void context_alloc_so_statistics_query(struct wined3d_context *context, + struct wined3d_so_statistics_query *query) DECLSPEC_HIDDEN; +void context_free_so_statistics_query(struct wined3d_so_statistics_query *query) DECLSPEC_HIDDEN; + struct wined3d_gl_view { GLenum target; @@ -1826,6 +1852,11 @@ struct wined3d_context unsigned int free_timestamp_query_count; struct list timestamp_queries; + union wined3d_gl_so_statistics_query *free_so_statistics_queries; + SIZE_T free_so_statistics_query_size; + unsigned int free_so_statistics_query_count; + struct list so_statistics_queries; + struct wined3d_stream_info stream_info; /* Fences for GL_APPLE_flush_buffer_range */ diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h index 97947e44d9c..8697fbfa935 100644 --- a/include/wine/wined3d.h +++ b/include/wine/wined3d.h @@ -685,23 +685,32 @@ enum wined3d_pool enum wined3d_query_type { - WINED3D_QUERY_TYPE_PIPELINE_STATISTICS = 1, - WINED3D_QUERY_TYPE_SO_STATISTICS = 2, - WINED3D_QUERY_TYPE_SO_OVERFLOW = 3, - WINED3D_QUERY_TYPE_VCACHE = 4, - WINED3D_QUERY_TYPE_RESOURCE_MANAGER = 5, - WINED3D_QUERY_TYPE_VERTEX_STATS = 6, - WINED3D_QUERY_TYPE_EVENT = 8, - WINED3D_QUERY_TYPE_OCCLUSION = 9, - WINED3D_QUERY_TYPE_TIMESTAMP = 10, - WINED3D_QUERY_TYPE_TIMESTAMP_DISJOINT = 11, - WINED3D_QUERY_TYPE_TIMESTAMP_FREQ = 12, - WINED3D_QUERY_TYPE_PIPELINE_TIMINGS = 13, - WINED3D_QUERY_TYPE_INTERFACE_TIMINGS = 14, - WINED3D_QUERY_TYPE_VERTEX_TIMINGS = 15, - WINED3D_QUERY_TYPE_PIXEL_TIMINGS = 16, - WINED3D_QUERY_TYPE_BANDWIDTH_TIMINGS = 17, - WINED3D_QUERY_TYPE_CACHE_UTILIZATION = 18 + WINED3D_QUERY_TYPE_VCACHE = 4, + WINED3D_QUERY_TYPE_RESOURCE_MANAGER = 5, + WINED3D_QUERY_TYPE_VERTEX_STATS = 6, + WINED3D_QUERY_TYPE_EVENT = 8, + WINED3D_QUERY_TYPE_OCCLUSION = 9, + WINED3D_QUERY_TYPE_TIMESTAMP = 10, + WINED3D_QUERY_TYPE_TIMESTAMP_DISJOINT = 11, + WINED3D_QUERY_TYPE_TIMESTAMP_FREQ = 12, + WINED3D_QUERY_TYPE_PIPELINE_TIMINGS = 13, + WINED3D_QUERY_TYPE_INTERFACE_TIMINGS = 14, + WINED3D_QUERY_TYPE_VERTEX_TIMINGS = 15, + WINED3D_QUERY_TYPE_PIXEL_TIMINGS = 16, + WINED3D_QUERY_TYPE_BANDWIDTH_TIMINGS = 17, + WINED3D_QUERY_TYPE_CACHE_UTILIZATION = 18, + WINED3D_QUERY_TYPE_MEMORY_PRESSURE = 19, + WINED3D_QUERY_TYPE_PIPELINE_STATISTICS = 20, + WINED3D_QUERY_TYPE_SO_STATISTICS = 21, + WINED3D_QUERY_TYPE_SO_OVERFLOW = 22, + WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM0 = 23, + WINED3D_QUERY_TYPE_SO_OVERFLOW_STREAM0 = 24, + WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM1 = 25, + WINED3D_QUERY_TYPE_SO_OVERFLOW_STREAM1 = 26, + WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM2 = 27, + WINED3D_QUERY_TYPE_SO_OVERFLOW_STREAM2 = 28, + WINED3D_QUERY_TYPE_SO_STATISTICS_STREAM3 = 29, + WINED3D_QUERY_TYPE_SO_OVERFLOW_STREAM3 = 30, }; struct wined3d_query_data_timestamp_disjoint @@ -710,6 +719,12 @@ struct wined3d_query_data_timestamp_disjoint BOOL disjoint; }; +struct wined3d_query_data_so_statistics +{ + UINT64 primitives_written; + UINT64 primitives_generated; +}; + #define WINED3DISSUE_BEGIN (1u << 1) #define WINED3DISSUE_END (1u << 0) #define WINED3DGETDATA_FLUSH (1u << 0)