[mutter/gbsneto/charts: 49/50] Add frame time chart
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mutter/gbsneto/charts: 49/50] Add frame time chart
- Date: Thu, 18 Apr 2019 01:22:01 +0000 (UTC)
commit a9b642540cd6fc0de938ab388ae0fec0e2001e2e
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Fri Mar 22 01:29:36 2019 +0000
Add frame time chart
Add a chart at the bottom 10% of the screen where each
bar represents the time the frame took to be painted. The
red line represents the maximum time to draw. For example,
if you are on a 60Hz monitor, the red line means 16.6667
miliseconds.
This covers both layout time (green) and paint time (blue).
Enjoy.
https://gitlab.gnome.org/GNOME/mutter/merge_requests/502
clutter/clutter/clutter-main.c | 1 +
clutter/clutter/clutter-main.h | 1 +
clutter/clutter/clutter-stage-private.h | 4 +
clutter/clutter/clutter-stage.c | 9 ++
clutter/clutter/cogl/clutter-stage-cogl.c | 217 ++++++++++++++++++++++++++++++
clutter/clutter/cogl/clutter-stage-cogl.h | 3 +
6 files changed, 235 insertions(+)
---
diff --git a/clutter/clutter/clutter-main.c b/clutter/clutter/clutter-main.c
index 7616eaae0..cff072331 100644
--- a/clutter/clutter/clutter-main.c
+++ b/clutter/clutter/clutter-main.c
@@ -143,6 +143,7 @@ static const GDebugKey clutter_paint_debug_keys[] = {
{ "continuous-redraw", CLUTTER_DEBUG_CONTINUOUS_REDRAW },
{ "paint-deform-tiles", CLUTTER_DEBUG_PAINT_DEFORM_TILES },
{ "damage-region", CLUTTER_DEBUG_PAINT_DAMAGE_REGION },
+ { "frame-time", CLUTTER_DEBUG_PAINT_FRAME_TIME },
};
static void
diff --git a/clutter/clutter/clutter-main.h b/clutter/clutter/clutter-main.h
index e0f10c892..6846cef7a 100644
--- a/clutter/clutter/clutter-main.h
+++ b/clutter/clutter/clutter-main.h
@@ -74,6 +74,7 @@ typedef enum
CLUTTER_DEBUG_CONTINUOUS_REDRAW = 1 << 6,
CLUTTER_DEBUG_PAINT_DEFORM_TILES = 1 << 7,
CLUTTER_DEBUG_PAINT_DAMAGE_REGION = 1 << 8,
+ CLUTTER_DEBUG_PAINT_FRAME_TIME = 1 << 9,
} ClutterDrawDebugFlag;
/**
diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h
index cb887ea32..abe83c321 100644
--- a/clutter/clutter/clutter-stage-private.h
+++ b/clutter/clutter/clutter-stage-private.h
@@ -136,6 +136,10 @@ GList * _clutter_stage_peek_stage_views (ClutterStage *stage);
int clutter_stage_get_sync_delay (ClutterStage *stage);
+void clutter_stage_get_frame_times (ClutterStage *stage,
+ double *paint_time,
+ double *layout_time);
+
G_END_DECLS
#endif /* __CLUTTER_STAGE_PRIVATE_H__ */
diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c
index 1cb5cb9b6..2eda55595 100644
--- a/clutter/clutter/clutter-stage.c
+++ b/clutter/clutter/clutter-stage.c
@@ -5099,3 +5099,12 @@ _clutter_stage_get_max_view_scale_factor_for_rect (ClutterStage *stage,
*view_scale = scale;
return TRUE;
}
+
+void
+clutter_stage_get_frame_times (ClutterStage *stage,
+ double *paint_time,
+ double *layout_time)
+{
+ *paint_time = stage->priv->last_paint_time;
+ *layout_time = stage->priv->last_layout_time;
+}
diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c
index e0c39185b..7eff2ca07 100644
--- a/clutter/clutter/cogl/clutter-stage-cogl.c
+++ b/clutter/clutter/cogl/clutter-stage-cogl.c
@@ -405,6 +405,215 @@ paint_damage_region (ClutterStageWindow *stage_window,
cogl_framebuffer_pop_matrix (framebuffer);
}
+#define CHART_COLUMN_WIDTH 6 //px
+#define THRESHOLD_LINE_HEIGHT 2 //px
+#define MAX_FRAME_TIME_INFO_ENTRIES 1000
+
+typedef struct
+{
+ double layout_time;
+ double paint_time;
+} FrameTimeInfo;
+
+static void
+get_frame_time_chart_region (ClutterStageWindow *stage_window,
+ ClutterStageView *view,
+ cairo_rectangle_int_t *region)
+{
+ ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
+ cairo_rectangle_int_t painted_region;
+ cairo_rectangle_int_t layout;
+ double refresh_rate_bar_height;
+ double ms_per_frame;
+ float green_line_y;
+ int sync_delay;
+ int n_points;
+ int i;
+
+ clutter_stage_view_get_layout (view, &layout);
+
+ painted_region = (cairo_rectangle_int_t) {
+ .x = layout.x,
+ .y = layout.y + layout.height,
+ .width = layout.width,
+ .height = 0,
+ };
+
+ n_points = layout.width / CHART_COLUMN_WIDTH;
+
+ /* Chart */
+ sync_delay = clutter_stage_get_sync_delay (stage_cogl->wrapper);
+ ms_per_frame = 1000.0 / stage_cogl->refresh_rate - sync_delay;
+ refresh_rate_bar_height = layout.height * 0.1;
+
+ for (i = 0; i < MIN (stage_cogl->frame_times->len, n_points); i++)
+ {
+ int element = stage_cogl->frame_times->len - i - 1;
+ FrameTimeInfo *info = &g_array_index (stage_cogl->frame_times, FrameTimeInfo, element);
+ double layout_bar_height;
+ double paint_bar_height;
+
+ /* Layout time section */
+ layout_bar_height =
+ info->layout_time / ms_per_frame * refresh_rate_bar_height;
+
+ /* Paint time section */
+ paint_bar_height =
+ info->paint_time / ms_per_frame * refresh_rate_bar_height;
+
+ painted_region.height = MAX (painted_region.height,
+ paint_bar_height + layout_bar_height);
+ }
+
+ /* Green line (16.667ms) */
+ green_line_y = layout.height * 0.9;
+
+ painted_region.height = MAX (painted_region.height, green_line_y);
+
+ /* Update the swap region rectangle */
+ painted_region.y -= MIN (layout.height, painted_region.height);
+
+ _clutter_util_rectangle_union (region, &painted_region, region);
+}
+
+static void
+paint_frame_time_chart (ClutterStageWindow *stage_window,
+ ClutterStageView *view)
+{
+ CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (view);
+ CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
+ ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
+ ClutterActor *actor = CLUTTER_ACTOR (stage_cogl->wrapper);
+ cairo_rectangle_int_t layout;
+ static CoglPipeline *threshold_pipeline = NULL;
+ static CoglPipeline *paint_time_pipeline = NULL;
+ static CoglPipeline *layout_time_pipeline = NULL;
+ CoglMatrix modelview;
+ double refresh_rate_bar_height;
+ double ms_per_frame;
+ float green_line_y;
+ int sync_delay;
+ int n_points;
+ int i;
+
+ if (G_UNLIKELY (paint_time_pipeline == NULL))
+ {
+ paint_time_pipeline = cogl_pipeline_new (ctx);
+ cogl_pipeline_set_color4ub (paint_time_pipeline, 0x00, 0x00, 0x60, 0xa0);
+ }
+
+ if (G_UNLIKELY (layout_time_pipeline == NULL))
+ {
+ layout_time_pipeline = cogl_pipeline_new (ctx);
+ cogl_pipeline_set_color4ub (layout_time_pipeline, 0x00, 0x60, 0x00, 0xa0);
+ }
+
+ if (G_UNLIKELY (threshold_pipeline == NULL))
+ {
+ threshold_pipeline = cogl_pipeline_new (ctx);
+ cogl_pipeline_set_color4ub (threshold_pipeline, 0x40, 0x00, 0x00, 0x80);
+ }
+
+ cogl_framebuffer_push_matrix (framebuffer);
+ cogl_matrix_init_identity (&modelview);
+ _clutter_actor_apply_modelview_transform (actor, &modelview);
+ cogl_framebuffer_set_modelview_matrix (framebuffer, &modelview);
+
+ clutter_stage_view_get_layout (view, &layout);
+
+ n_points = layout.width / CHART_COLUMN_WIDTH;
+
+ /* Chart */
+ sync_delay = clutter_stage_get_sync_delay (stage_cogl->wrapper);
+ ms_per_frame = 1000.0 / stage_cogl->refresh_rate - sync_delay;
+ refresh_rate_bar_height = layout.height * 0.1;
+
+ for (i = 0; i < MIN (stage_cogl->frame_times->len, n_points); i++)
+ {
+ int element = stage_cogl->frame_times->len - i - 1;
+ FrameTimeInfo *info = &g_array_index (stage_cogl->frame_times, FrameTimeInfo, element);
+ double x_1 = layout.width - (i + 1) * CHART_COLUMN_WIDTH;
+ double x_2 = layout.width - i * CHART_COLUMN_WIDTH;
+ double layout_bar_height;
+ double paint_bar_height;
+
+ /* Layout time section */
+ layout_bar_height =
+ info->layout_time / ms_per_frame * refresh_rate_bar_height;
+ cogl_framebuffer_draw_rectangle (framebuffer,
+ layout_time_pipeline,
+ x_1,
+ layout.height - layout_bar_height,
+ x_2,
+ layout.height);
+
+ /* Paint time section */
+ paint_bar_height =
+ info->paint_time / ms_per_frame * refresh_rate_bar_height;
+ cogl_framebuffer_draw_rectangle (framebuffer,
+ paint_time_pipeline,
+ x_1,
+ layout.height - paint_bar_height - layout_bar_height,
+ x_2,
+ layout.height - layout_bar_height);
+ }
+
+ /* Green line (16.667ms) */
+ green_line_y = layout.height * 0.9;
+ cogl_framebuffer_draw_rectangle (framebuffer,
+ threshold_pipeline,
+ 0.0f,
+ green_line_y,
+ layout.width,
+ green_line_y + THRESHOLD_LINE_HEIGHT);
+
+ cogl_framebuffer_pop_matrix (framebuffer);
+}
+
+static inline void
+append_frame_time_info (ClutterStageCogl *stage_cogl)
+{
+ FrameTimeInfo previous_frame_info;
+
+ if (G_LIKELY (!(clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_FRAME_TIME)))
+ return;
+
+ if (G_UNLIKELY (stage_cogl->frame_times == NULL))
+ stage_cogl->frame_times = g_array_sized_new (FALSE, TRUE,
+ sizeof (FrameTimeInfo),
+ MAX_FRAME_TIME_INFO_ENTRIES);
+
+ clutter_stage_get_frame_times (stage_cogl->wrapper,
+ &previous_frame_info.paint_time,
+ &previous_frame_info.layout_time);
+
+ g_array_append_val (stage_cogl->frame_times, previous_frame_info);
+
+ if (stage_cogl->frame_times->len > MAX_FRAME_TIME_INFO_ENTRIES)
+ g_array_remove_range (stage_cogl->frame_times, 0,
+ stage_cogl->frame_times->len - MAX_FRAME_TIME_INFO_ENTRIES);
+
+}
+
+static inline void
+maybe_paint_frame_times (ClutterStageWindow *stage_window,
+ ClutterStageView *view)
+{
+ ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
+
+ if (G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_FRAME_TIME)))
+ {
+ paint_frame_time_chart (stage_window, view);
+ stage_cogl->painting_frame_chart = TRUE;
+ }
+ else if (stage_cogl->painting_frame_chart)
+ {
+ g_array_free (stage_cogl->frame_times, TRUE);
+ stage_cogl->frame_times = NULL;
+ stage_cogl->painting_frame_chart = FALSE;
+ }
+}
+
static gboolean
swap_framebuffer (ClutterStageWindow *stage_window,
ClutterStageView *view,
@@ -672,6 +881,9 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
fb_clip_region = (cairo_rectangle_int_t) { 0 };
}
+ if (G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_FRAME_TIME)))
+ get_frame_time_chart_region (stage_window, view, &fb_clip_region);
+
if (may_use_clipped_redraw &&
G_LIKELY (!(clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)))
use_clipped_redraw = TRUE;
@@ -827,6 +1039,9 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
else
paint_stage (stage_cogl, view, &view_rect);
}
+
+ maybe_paint_frame_times (stage_window, view);
+
cogl_pop_framebuffer ();
if (may_use_clipped_redraw &&
@@ -931,6 +1146,8 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
gboolean swap_event = FALSE;
GList *l;
+ append_frame_time_info (stage_cogl);
+
for (l = _clutter_stage_window_get_views (stage_window); l; l = l->next)
{
ClutterStageView *view = l->data;
diff --git a/clutter/clutter/cogl/clutter-stage-cogl.h b/clutter/clutter/cogl/clutter-stage-cogl.h
index 17958cd24..6c56abb85 100644
--- a/clutter/clutter/cogl/clutter-stage-cogl.h
+++ b/clutter/clutter/cogl/clutter-stage-cogl.h
@@ -66,6 +66,9 @@ struct _ClutterStageCogl
/* TRUE if the current paint cycle has a clipped redraw. In that
case bounding_redraw_clip specifies the the bounds. */
guint using_clipped_redraw : 1;
+
+ gboolean painting_frame_chart;
+ GArray *frame_times;
};
struct _ClutterStageCoglClass
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]