From fbe6740df121db60e1dbe0fbc6b01a72a6401461 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Sat, 15 May 2021 13:56:49 +0300 Subject: [PATCH] cogl: Add GPU timestamp querying utilities Add utilities that allow getting the current GPU timestamp and creating a query which completes upon completion of all operations currently submitted on a framebuffer. Combined, these two allow measuring how long it took the GPU to finish rendering something to a framebuffer. Part-of: --- cogl/cogl/cogl-context-private.h | 5 ++ cogl/cogl/cogl-context.c | 24 +++++++++ cogl/cogl/cogl-context.h | 23 ++++++++ cogl/cogl/cogl-driver.h | 14 +++++ cogl/cogl/cogl-framebuffer.c | 25 +++++++++ cogl/cogl/cogl-framebuffer.h | 13 +++++ cogl/cogl/driver/gl/cogl-util-gl-private.h | 21 ++++++++ cogl/cogl/driver/gl/cogl-util-gl.c | 60 +++++++++++++++++++++ cogl/cogl/driver/gl/gl/cogl-driver-gl.c | 10 ++++ cogl/cogl/driver/gl/gles/cogl-driver-gles.c | 10 ++++ 10 files changed, 205 insertions(+) diff --git a/cogl/cogl/cogl-context-private.h b/cogl/cogl/cogl-context-private.h index 25004c393..5856f76e9 100644 --- a/cogl/cogl/cogl-context-private.h +++ b/cogl/cogl/cogl-context-private.h @@ -62,6 +62,11 @@ typedef struct GLubyte c[4]; } CoglTextureGLVertex; +struct _CoglTimestampQuery +{ + unsigned int id; +}; + struct _CoglContext { CoglObject _parent; diff --git a/cogl/cogl/cogl-context.c b/cogl/cogl/cogl-context.c index b7bf14f87..89019e908 100644 --- a/cogl/cogl/cogl-context.c +++ b/cogl/cogl/cogl-context.c @@ -491,3 +491,27 @@ cogl_context_get_named_pipeline (CoglContext *context, { return g_hash_table_lookup (context->named_pipelines, key); } + +void +cogl_context_free_timestamp_query (CoglContext *context, + CoglTimestampQuery *query) +{ + context->driver_vtable->free_timestamp_query (context, query); +} + +int64_t +cogl_context_timestamp_query_get_time_ns (CoglContext *context, + CoglTimestampQuery *query) +{ + return context->driver_vtable->timestamp_query_get_time_ns (context, query); +} + +int64_t +cogl_context_get_gpu_time_ns (CoglContext *context) +{ + g_return_val_if_fail (cogl_has_feature (context, + COGL_FEATURE_ID_GET_GPU_TIME), + 0); + + return context->driver_vtable->get_gpu_time_ns (context); +} diff --git a/cogl/cogl/cogl-context.h b/cogl/cogl/cogl-context.h index 20b9561b9..38ac11be8 100644 --- a/cogl/cogl/cogl-context.h +++ b/cogl/cogl/cogl-context.h @@ -41,6 +41,7 @@ * dependency issues with the following headers. */ typedef struct _CoglContext CoglContext; +typedef struct _CoglTimestampQuery CoglTimestampQuery; #include #include @@ -208,6 +209,8 @@ typedef enum _CoglFeatureID COGL_FEATURE_ID_BUFFER_AGE, COGL_FEATURE_ID_TEXTURE_EGL_IMAGE_EXTERNAL, COGL_FEATURE_ID_BLIT_FRAMEBUFFER, + COGL_FEATURE_ID_TIMESTAMP_QUERY, + COGL_FEATURE_ID_GET_GPU_TIME, /*< private >*/ _COGL_N_FEATURE_IDS /*< skip >*/ @@ -370,6 +373,26 @@ COGL_EXPORT CoglPipeline * cogl_context_get_named_pipeline (CoglContext *context, CoglPipelineKey *key); +COGL_EXPORT void +cogl_context_free_timestamp_query (CoglContext *context, + CoglTimestampQuery *query); + +COGL_EXPORT int64_t +cogl_context_timestamp_query_get_time_ns (CoglContext *context, + CoglTimestampQuery *query); + +/** + * cogl_context_get_gpu_time_ns: + * @context: a #CoglContext pointer + * + * This function should only be called if the COGL_FEATURE_ID_GET_GPU_TIME + * feature is advertised. + * + * Return value: Current GPU time in nanoseconds + */ +COGL_EXPORT int64_t +cogl_context_get_gpu_time_ns (CoglContext *context); + G_END_DECLS #endif /* __COGL_CONTEXT_H__ */ diff --git a/cogl/cogl/cogl-driver.h b/cogl/cogl/cogl-driver.h index 21519085b..aeabfe0e7 100644 --- a/cogl/cogl/cogl-driver.h +++ b/cogl/cogl/cogl-driver.h @@ -231,6 +231,20 @@ struct _CoglDriverVtable (* set_uniform) (CoglContext *ctx, GLint location, const CoglBoxedValue *value); + + CoglTimestampQuery * + (* create_timestamp_query) (CoglContext *context); + + void + (* free_timestamp_query) (CoglContext *context, + CoglTimestampQuery *query); + + int64_t + (* timestamp_query_get_time_ns) (CoglContext *context, + CoglTimestampQuery *query); + + int64_t + (* get_gpu_time_ns) (CoglContext *context); }; #define COGL_DRIVER_ERROR (_cogl_driver_error_quark ()) diff --git a/cogl/cogl/cogl-framebuffer.c b/cogl/cogl/cogl-framebuffer.c index 7a8acecb9..525b24293 100644 --- a/cogl/cogl/cogl-framebuffer.c +++ b/cogl/cogl/cogl-framebuffer.c @@ -2656,3 +2656,28 @@ cogl_framebuffer_get_driver (CoglFramebuffer *framebuffer) return priv->driver; } + +CoglTimestampQuery * +cogl_framebuffer_create_timestamp_query (CoglFramebuffer *framebuffer) +{ + CoglFramebufferPrivate *priv = + cogl_framebuffer_get_instance_private (framebuffer); + const CoglDriverVtable *driver_vtable = priv->context->driver_vtable; + + g_return_val_if_fail (cogl_has_feature (priv->context, + COGL_FEATURE_ID_TIMESTAMP_QUERY), + NULL); + + /* The timestamp query completes upon completion of all previously submitted + * GL commands. So make sure those commands are indeed submitted by flushing + * the journal. + */ + _cogl_framebuffer_flush_journal (framebuffer); + + cogl_context_flush_framebuffer_state (priv->context, + framebuffer, + framebuffer, + COGL_FRAMEBUFFER_STATE_BIND); + + return driver_vtable->create_timestamp_query (priv->context); +} diff --git a/cogl/cogl/cogl-framebuffer.h b/cogl/cogl/cogl-framebuffer.h index 993de7a25..571b48998 100644 --- a/cogl/cogl/cogl-framebuffer.h +++ b/cogl/cogl/cogl-framebuffer.h @@ -1564,6 +1564,19 @@ cogl_blit_framebuffer (CoglFramebuffer *framebuffer, COGL_EXPORT void cogl_framebuffer_flush (CoglFramebuffer *framebuffer); +/** + * cogl_framebuffer_create_timestamp_query: (skip) + * + * Creates a query for the GPU timestamp that will complete upon completion of + * all previously submitted GL commands related to this framebuffer. E.g. when + * the rendering is finished on this framebuffer. + * + * This function should only be called if the COGL_FEATURE_ID_TIMESTAMP_QUERY + * feature is advertised. + */ +COGL_EXPORT CoglTimestampQuery * +cogl_framebuffer_create_timestamp_query (CoglFramebuffer *framebuffer); + G_END_DECLS #endif /* __COGL_FRAMEBUFFER_H */ diff --git a/cogl/cogl/driver/gl/cogl-util-gl-private.h b/cogl/cogl/driver/gl/cogl-util-gl-private.h index 0b7eaa772..be8fa1eeb 100644 --- a/cogl/cogl/driver/gl/cogl-util-gl-private.h +++ b/cogl/cogl/driver/gl/cogl-util-gl-private.h @@ -146,6 +146,20 @@ _cogl_gl_util_parse_gl_version (const char *version_string, CoglGraphicsResetStatus _cogl_gl_get_graphics_reset_status (CoglContext *context); +CoglTimestampQuery * +cogl_gl_create_timestamp_query (CoglContext *context); + +void +cogl_gl_free_timestamp_query (CoglContext *context, + CoglTimestampQuery *query); + +int64_t +cogl_gl_timestamp_query_get_time_ns (CoglContext *context, + CoglTimestampQuery *query); + +int64_t +cogl_gl_get_gpu_time_ns (CoglContext *context); + #ifndef GL_FRAMEBUFFER #define GL_FRAMEBUFFER 0x8D40 #endif @@ -229,4 +243,11 @@ _cogl_gl_get_graphics_reset_status (CoglContext *context); #define GL_STENCIL 0x1802 #endif +#ifndef GL_TIMESTAMP +#define GL_TIMESTAMP 0x8E28 +#endif +#ifndef GL_QUERY_RESULT +#define GL_QUERY_RESULT 0x8866 +#endif + #endif /* _COGL_UTIL_GL_PRIVATE_H_ */ diff --git a/cogl/cogl/driver/gl/cogl-util-gl.c b/cogl/cogl/driver/gl/cogl-util-gl.c index 9b59bef1d..096fcb788 100644 --- a/cogl/cogl/driver/gl/cogl-util-gl.c +++ b/cogl/cogl/driver/gl/cogl-util-gl.c @@ -493,3 +493,63 @@ _cogl_gl_get_graphics_reset_status (CoglContext *context) return COGL_GRAPHICS_RESET_STATUS_NO_ERROR; } } + +CoglTimestampQuery * +cogl_gl_create_timestamp_query (CoglContext *context) +{ + CoglTimestampQuery *query; + + g_return_val_if_fail (cogl_has_feature (context, + COGL_FEATURE_ID_TIMESTAMP_QUERY), + NULL); + + query = g_new0 (CoglTimestampQuery, 1); + + GE (context, glGenQueries (1, &query->id)); + GE (context, glQueryCounter (query->id, GL_TIMESTAMP)); + + /* Flush right away so GL knows about our timestamp query. + * + * E.g. the direct scanout path doesn't call SwapBuffers or any other + * glFlush-inducing operation, and skipping explicit glFlush here results in + * the timestamp query being placed at the point of glGetQueryObject much + * later, resulting in a GPU timestamp much later on in time. + */ + GE (context, glFlush ()); + + return query; +} + +void +cogl_gl_free_timestamp_query (CoglContext *context, + CoglTimestampQuery *query) +{ + GE (context, glDeleteQueries (1, &query->id)); + g_free (query); +} + +int64_t +cogl_gl_timestamp_query_get_time_ns (CoglContext *context, + CoglTimestampQuery *query) +{ + int64_t query_time_ns; + + GE (context, glGetQueryObjecti64v (query->id, + GL_QUERY_RESULT, + &query_time_ns)); + + return query_time_ns; +} + +int64_t +cogl_gl_get_gpu_time_ns (CoglContext *context) +{ + int64_t gpu_time_ns; + + g_return_val_if_fail (cogl_has_feature (context, + COGL_FEATURE_ID_GET_GPU_TIME), + 0); + + GE (context, glGetInteger64v (GL_TIMESTAMP, &gpu_time_ns)); + return gpu_time_ns; +} diff --git a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c index 35577518d..ef4a61a09 100644 --- a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c +++ b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c @@ -539,6 +539,12 @@ _cogl_driver_update_features (CoglContext *ctx, COGL_PRIVATE_FEATURE_TEXTURE_FORMAT_HALF_FLOAT, TRUE); + if (ctx->glGenQueries && ctx->glQueryCounter) + COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_TIMESTAMP_QUERY, TRUE); + + if (ctx->glGetInteger64v) + COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_GET_GPU_TIME, TRUE); + /* Cache features */ for (i = 0; i < G_N_ELEMENTS (private_features); i++) ctx->private_features[i] |= private_features[i]; @@ -591,4 +597,8 @@ _cogl_driver_gl = _cogl_sampler_gl_init, _cogl_sampler_gl_free, _cogl_gl_set_uniform, /* XXX name is weird... */ + cogl_gl_create_timestamp_query, + cogl_gl_free_timestamp_query, + cogl_gl_timestamp_query_get_time_ns, + cogl_gl_get_gpu_time_ns, }; diff --git a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c index 3e8ea8a0a..633e2c415 100644 --- a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c +++ b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c @@ -432,6 +432,12 @@ _cogl_driver_update_features (CoglContext *context, COGL_FEATURE_ID_TEXTURE_RG, TRUE); + if (context->glGenQueries && context->glQueryCounter) + COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_TIMESTAMP_QUERY, TRUE); + + if (context->glGetInteger64v) + COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_GET_GPU_TIME, TRUE); + /* Cache features */ for (i = 0; i < G_N_ELEMENTS (private_features); i++) context->private_features[i] |= private_features[i]; @@ -479,4 +485,8 @@ _cogl_driver_gles = _cogl_sampler_gl_init, _cogl_sampler_gl_free, _cogl_gl_set_uniform, + cogl_gl_create_timestamp_query, + cogl_gl_free_timestamp_query, + cogl_gl_timestamp_query_get_time_ns, + cogl_gl_get_gpu_time_ns, };