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
This commit is contained in:
Georges Basile Stavracas Neto 2019-03-22 01:29:36 +00:00
parent 6350efc28e
commit a9b642540c
No known key found for this signature in database
GPG Key ID: 886C17EE170D1385
6 changed files with 235 additions and 0 deletions

View File

@ -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

View File

@ -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;
/**

View File

@ -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__ */

View File

@ -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;
}

View File

@ -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;

View File

@ -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