timeline: Add the TL_POINTs into perfetto data

Try to roughly achieve feature parity with WESGR by processing the same
timepoint data and feeding it into perfetto, with output and surface
specific tracks.

We can't match the way WESGR draws this information, because perfetto has
a different display paradigm, but we can try to at least make sure all the
same information is visible in an understandable way.

Signed-off-by: Derek Foreman <derek.foreman@collabora.com>
This commit is contained in:
Derek Foreman 2025-01-10 08:35:24 -06:00 committed by Daniel Stone
parent b3635c17f4
commit 485cb4ab12
5 changed files with 216 additions and 0 deletions

View file

@ -567,6 +567,10 @@ struct weston_output {
struct weston_coord_global pos;
int32_t width, height;
uint64_t gpu_track_id;
uint64_t paint_track_id;
uint64_t presentation_track_id;
/** List of paint nodes in z-order, from top to bottom, maybe pruned
*
* struct weston_paint_node::z_order_link
@ -2072,6 +2076,8 @@ struct weston_surface {
* this list. */
struct wl_list cm_feedback_surface_resource_list;
struct wl_resource *cm_surface;
uint64_t damage_track_id;
};
struct weston_subsurface {

View file

@ -8195,6 +8195,9 @@ weston_output_init(struct weston_output *output,
* free to set the color profile to whatever they want later on. */
cm = compositor->color_manager;
output->color_profile = cm->ref_stock_sRGB_color_profile(cm);
output->gpu_track_id = 0;
output->paint_track_id = 0;
output->presentation_track_id = 0;
}
/** Adds weston_output object to pending output list.

View file

@ -108,6 +108,7 @@ if get_option('perfetto')
srcs_libweston += [
'perfetto/u_perfetto.cc',
'perfetto/u_perfetto.h',
'timeline-perfetto.c'
]
dep_perfetto = dependency('perfetto', fallback: ['perfetto', 'dep_perfetto'])
deps_libweston += dep_perfetto

View file

@ -0,0 +1,190 @@
/*
* Copyright © 2025 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <libweston/libweston.h>
#include "shared/timespec-util.h"
#include "timeline.h"
#include "weston-trace.h"
static void
weston_perfetto_ensure_output_ids(struct weston_output *output)
{
char track_name[512];
if (output->gpu_track_id)
return;
snprintf(track_name, sizeof(track_name), "%s GPU activity", output->name);
output->gpu_track_id = util_perfetto_new_track(track_name);
snprintf(track_name, sizeof(track_name), "%s paint", output->name);
output->paint_track_id = util_perfetto_new_track(track_name);
snprintf(track_name, sizeof(track_name), "%s present", output->name);
output->presentation_track_id = util_perfetto_new_track(track_name);
}
static void
build_track_name(struct weston_surface *surface, char *name, int size)
{
static int disambiguator = 0;
char surface_label[512];
/* Make sure we only call this once, so we don't accidentally
* make multiple names for the same surface */
assert(surface->damage_track_id == 0);
if (surface->get_label)
surface->get_label(surface, surface_label, sizeof(surface_label));
else {
uint32_t res_id;
res_id = wl_resource_get_id(surface->resource);
snprintf(surface_label, sizeof(surface_label), "surface %d", res_id);
}
disambiguator++;
snprintf(name, size, "%s #%d", surface_label, disambiguator);
}
static void
weston_perfetto_ensure_surface_id(struct weston_surface *surface)
{
char track_name[600];
if (surface->damage_track_id)
return;
build_track_name(surface, track_name, sizeof(track_name));
surface->damage_track_id = util_perfetto_new_track(track_name);
}
/**
* Translates a timeline point for perfetto.
*
* The TL_POINT() is a wrapper over this function, but it uses the weston_compositor
* instance to pass the timeline scope.
*
* @param timeline_scope the timeline scope
* @param tlp_name the name of the timeline point.
*
* @ingroup log
*/
WL_EXPORT void
weston_timeline_perfetto(struct weston_log_scope *timeline_scope,
enum timeline_point_name tlp_name, ...)
{
struct weston_output *output = NULL;
struct weston_surface *surface = NULL;
struct timespec ts;
uint64_t now_ns;
uint64_t vblank_ns = 0, gpu_ns = 0;
va_list argp;
if (!util_perfetto_is_tracing_enabled())
return;
clock_gettime(CLOCK_MONOTONIC, &ts);
now_ns = timespec_to_nsec(&ts);
va_start(argp, tlp_name);
while (1) {
enum timeline_type otype;
void *obj;
otype = va_arg(argp, enum timeline_type);
if (otype == TLT_END)
break;
obj = va_arg(argp, void *);
switch (otype) {
case TLT_OUTPUT:
output = obj;
weston_perfetto_ensure_output_ids(output);
break;
case TLT_SURFACE:
surface = obj;
weston_perfetto_ensure_surface_id(surface);
break;
case TLT_VBLANK:
vblank_ns = timespec_to_nsec(obj);
break;
case TLT_GPU:
gpu_ns = timespec_to_nsec(obj);
break;
default:
assert(!"not reached");
}
}
va_end(argp);
switch (tlp_name) {
case TLP_CORE_REPAINT_ENTER_LOOP:
case TLP_CORE_REPAINT_RESTART:
case TLP_CORE_REPAINT_EXIT_LOOP:
break;
case TLP_CORE_FLUSH_DAMAGE:
WESTON_TRACE_TIMESTAMP_END("Damaged", surface->damage_track_id, CLOCK_MONOTONIC, now_ns);
WESTON_TRACE_TIMESTAMP_BEGIN("Clean", surface->damage_track_id, 0, CLOCK_MONOTONIC, now_ns);
break;
case TLP_CORE_REPAINT_BEGIN:
WESTON_TRACE_TIMESTAMP_END("Scheduled", output->paint_track_id, CLOCK_MONOTONIC, now_ns);
WESTON_TRACE_TIMESTAMP_BEGIN("Paint", output->paint_track_id, 0, CLOCK_MONOTONIC, now_ns);
break;
case TLP_CORE_REPAINT_POSTED:
WESTON_TRACE_TIMESTAMP_END("Paint", output->paint_track_id, CLOCK_MONOTONIC, now_ns);
WESTON_TRACE_TIMESTAMP_BEGIN("Posted", output->presentation_track_id, 0, CLOCK_MONOTONIC, now_ns);
break;
case TLP_CORE_REPAINT_FINISHED:
WESTON_TRACE_TIMESTAMP_END("Posted", output->presentation_track_id, CLOCK_MONOTONIC, vblank_ns);
break;
case TLP_CORE_REPAINT_REQ:
WESTON_TRACE_TIMESTAMP_BEGIN("Scheduled", output->paint_track_id, 0, CLOCK_MONOTONIC, now_ns);
break;
case TLP_CORE_COMMIT_DAMAGE:
WESTON_TRACE_TIMESTAMP_END("Clean", surface->damage_track_id, CLOCK_MONOTONIC, now_ns);
WESTON_TRACE_TIMESTAMP_END("Damaged", surface->damage_track_id, CLOCK_MONOTONIC, now_ns);
WESTON_TRACE_TIMESTAMP_BEGIN("Damaged", surface->damage_track_id, 0, CLOCK_MONOTONIC, now_ns);
break;
case TLP_RENDERER_GPU_BEGIN:
WESTON_TRACE_TIMESTAMP_BEGIN("Active", output->gpu_track_id, 0, CLOCK_MONOTONIC, gpu_ns);
break;
case TLP_RENDERER_GPU_END:
WESTON_TRACE_TIMESTAMP_END("Active", output->gpu_track_id, CLOCK_MONOTONIC, gpu_ns);
break;
default:
assert(!"not reached");
}
}

View file

@ -27,6 +27,8 @@
#ifndef WESTON_TIMELINE_H
#define WESTON_TIMELINE_H
#include "config.h"
#include <wayland-util.h>
#include <stdbool.h>
@ -99,18 +101,32 @@ struct weston_timeline_subscription_object {
*
* Use TLP_END when done for the vargs.
*
* Timeline points are fed to the timeline log scope, and
* to perfetto if it was enabled at build time.
*
* @param ec weston_compositor instance
*
* @ingroup log
*/
#ifdef HAVE_PERFETTO
#define TL_POINT(ec, ...) do { \
weston_timeline_perfetto(ec->timeline, __VA_ARGS__); \
weston_timeline_point(ec->timeline, __VA_ARGS__); \
} while (0)
#else
#define TL_POINT(ec, ...) do { \
weston_timeline_point(ec->timeline, __VA_ARGS__); \
} while (0)
#endif /* HAVE_PERFETTO */
void
weston_timeline_point(struct weston_log_scope *timeline_scope,
enum timeline_point_name tlp_name, ...);
void
weston_timeline_perfetto(struct weston_log_scope *timeline_scope,
enum timeline_point_name tlp_name, ...);
bool
weston_timeline_profiling(struct weston_log_scope *timeline_scope);