gsk: Add GL profiler

We can use the GL_ARB_timer_query extension (available since OpenGL
3.2, and part of the OpenGL specification since version 3.3) to query
the time elapsed when drawing each frame. This allows us to gather
timing information on our use of the GPU.
This commit is contained in:
Emmanuele Bassi 2016-07-12 14:50:48 +01:00
parent 8807f23f76
commit 30be7bd543
4 changed files with 139 additions and 2 deletions

View File

@ -38,6 +38,7 @@ gsk_private_source_h = \
gskcairorendererprivate.h \
gskdebugprivate.h \
gskgldriverprivate.h \
gskglprofilerprivate.h \
gskglrendererprivate.h \
gskprivate.h \
gskrendererprivate.h \
@ -55,6 +56,7 @@ gsk_source_c = \
gskcairorenderer.c \
gskdebug.c \
gskgldriver.c \
gskglprofiler.c \
gskglrenderer.c \
gskrenderer.c \
gskrendernode.c \

108
gsk/gskglprofiler.c Normal file
View File

@ -0,0 +1,108 @@
#include "config.h"
#include "gskglprofilerprivate.h"
#include <epoxy/gl.h>
#define N_QUERIES 4
struct _GskGLProfiler
{
GObject parent_instance;
/* Creating GL queries is kind of expensive, so we pay the
* price upfront and create a circular buffer of queries
*/
GLuint gl_queries[N_QUERIES];
GLuint active_query;
gboolean has_timer : 1;
gboolean first_frame : 1;
};
G_DEFINE_TYPE (GskGLProfiler, gsk_gl_profiler, G_TYPE_OBJECT)
static void
gsk_gl_profiler_finalize (GObject *gobject)
{
GskGLProfiler *self = GSK_GL_PROFILER (gobject);
glDeleteQueries (N_QUERIES, self->gl_queries);
G_OBJECT_CLASS (gsk_gl_profiler_parent_class)->finalize (gobject);
}
static void
gsk_gl_profiler_class_init (GskGLProfilerClass *klass)
{
G_OBJECT_CLASS (klass)->finalize = gsk_gl_profiler_finalize;
}
static void
gsk_gl_profiler_init (GskGLProfiler *self)
{
glGenQueries (N_QUERIES, self->gl_queries);
self->first_frame = TRUE;
self->has_timer = epoxy_has_gl_extension ("GL_ARB_timer_query");
}
GskGLProfiler *
gsk_gl_profiler_new (void)
{
return g_object_new (GSK_TYPE_GL_PROFILER, NULL);
}
void
gsk_gl_profiler_begin_gpu_region (GskGLProfiler *profiler)
{
GLuint query_id;
g_return_if_fail (GSK_IS_GL_PROFILER (profiler));
if (!profiler->has_timer)
return;
query_id = profiler->gl_queries[profiler->active_query];
glBeginQuery (GL_TIME_ELAPSED, query_id);
}
guint64
gsk_gl_profiler_end_gpu_region (GskGLProfiler *profiler)
{
GLuint last_query_id;
GLint res;
GLuint64 elapsed;
g_return_val_if_fail (GSK_IS_GL_PROFILER (profiler), 0);
if (!profiler->has_timer)
return 0;
glEndQuery (GL_TIME_ELAPSED);
if (profiler->active_query == 0)
last_query_id = N_QUERIES - 1;
else
last_query_id = profiler->active_query - 1;
/* Advance iterator */
profiler->active_query += 1;
if (profiler->active_query == N_QUERIES)
profiler->active_query = 0;
/* If this is the first frame we already have a result */
if (profiler->first_frame)
{
profiler->first_frame = FALSE;
return 0;
}
glGetQueryObjectiv (profiler->gl_queries[last_query_id], GL_QUERY_RESULT_AVAILABLE, &res);
if (res == 1)
glGetQueryObjectui64v (profiler->gl_queries[last_query_id], GL_QUERY_RESULT, &elapsed);
else
elapsed = 0;
return elapsed;
}

View File

@ -0,0 +1,18 @@
#ifndef __GSK_GL_PROFILER_PRIVATE_H__
#define __GSK_GL_PROFILER_PRIVATE_H__
#include <gsk/gsktypes.h>
G_BEGIN_DECLS
#define GSK_TYPE_GL_PROFILER (gsk_gl_profiler_get_type ())
G_DECLARE_FINAL_TYPE (GskGLProfiler, gsk_gl_profiler, GSK, GL_PROFILER, GObject)
GskGLProfiler * gsk_gl_profiler_new (void);
void gsk_gl_profiler_begin_gpu_region (GskGLProfiler *profiler);
guint64 gsk_gl_profiler_end_gpu_region (GskGLProfiler *profiler);
G_END_DECLS
#endif /* __GSK_GL_PROFILER_PRIVATE_H__ */

View File

@ -5,6 +5,7 @@
#include "gskdebugprivate.h"
#include "gskenums.h"
#include "gskgldriverprivate.h"
#include "gskglprofilerprivate.h"
#include "gskrendererprivate.h"
#include "gskrendernodeprivate.h"
#include "gskrendernodeiter.h"
@ -89,7 +90,7 @@ struct _GskGLRenderer
GQuark attributes[N_ATTRIBUTES];
GskGLDriver *gl_driver;
GskGLProfiler *gl_profiler;
GskShaderBuilder *shader_builder;
int gl_min_filter;
@ -421,6 +422,7 @@ gsk_gl_renderer_realize (GskRenderer *renderer)
g_assert (self->gl_driver == NULL);
self->gl_driver = gsk_gl_driver_new (self->context);
self->gl_profiler = gsk_gl_profiler_new ();
GSK_NOTE (OPENGL, g_print ("Creating buffers and programs\n"));
if (!gsk_gl_renderer_create_programs (self))
@ -447,6 +449,7 @@ gsk_gl_renderer_unrealize (GskRenderer *renderer)
gsk_gl_renderer_destroy_buffers (self);
gsk_gl_renderer_destroy_programs (self);
g_clear_object (&self->gl_profiler);
g_clear_object (&self->gl_driver);
if (self->context == gdk_gl_context_get_current ())
@ -815,6 +818,7 @@ gsk_gl_renderer_render (GskRenderer *renderer,
gboolean use_alpha;
int status;
guint i;
guint64 gpu_time;
if (self->context == NULL)
return;
@ -837,6 +841,8 @@ gsk_gl_renderer_render (GskRenderer *renderer,
if (!gsk_gl_renderer_validate_tree (self, root))
goto out;
gsk_gl_profiler_begin_gpu_region (self->gl_profiler);
gsk_gl_driver_begin_frame (self->gl_driver);
/* Ensure that the viewport is up to date */
@ -887,6 +893,10 @@ gsk_gl_renderer_render (GskRenderer *renderer,
GSK_NOTE (OPENGL, g_print ("Drawing GL content on Cairo surface using a %s\n",
self->texture_id != 0 ? "texture" : "renderbuffer"));
gsk_gl_driver_end_frame (self->gl_driver);
gpu_time = gsk_gl_profiler_end_gpu_region (self->gl_profiler);
GSK_NOTE (OPENGL, g_print ("GPU time: %" G_GUINT64_FORMAT " nsec\n", gpu_time));
out:
use_alpha = gsk_renderer_get_use_alpha (renderer);
@ -898,7 +908,6 @@ out:
0, 0, viewport.size.width, viewport.size.height);
gdk_gl_context_make_current (self->context);
gsk_gl_driver_end_frame (self->gl_driver);
gsk_gl_renderer_clear_tree (self);
}