wsi/wayland: Move presentation tracing code into loader

Push the presentation feedback code into the common code in the loader,
so we're one step closer to using the perfetto instrumentation here in
the EGL code.

Signed-off-by: Derek Foreman <derek.foreman@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/32757>
This commit is contained in:
Derek Foreman 2024-12-12 11:52:28 -06:00
parent 3521abe0d1
commit 336cbc499f
4 changed files with 278 additions and 132 deletions

View file

@ -25,10 +25,28 @@
#include <stdlib.h>
#include <string.h>
#include "presentation-time-client-protocol.h"
#include "util/list.h"
#include <util/os_time.h>
#include "util/perf/cpu_trace.h"
#include "loader_wayland_helper.h"
struct loader_wayland_presentation_feedback_data {
struct loader_wayland_presentation *presentation;
bool tracing;
uint64_t flow_id;
/* We store copies of name and id, since buffers can be
* destroyed before feedback is serviced */
char *buffer_name;
uint32_t buffer_id;
uint64_t buffer_acquisition_time;
void *callback_data;
struct wp_presentation_feedback *feedback;
struct list_head link;
};
#ifndef HAVE_WL_DISPATCH_QUEUE_TIMEOUT
static int
wl_display_poll(struct wl_display *display,
@ -185,6 +203,7 @@ loader_wayland_wrap_buffer(struct loader_wayland_buffer *lwb,
lwb->id = wl_proxy_get_id((struct wl_proxy *)wl_buffer);
lwb->flow_id = 0;
lwb->name = stringify_wayland_id(lwb->id);
lwb->acquisition_time = 0;
}
void
@ -196,12 +215,14 @@ loader_wayland_buffer_destroy(struct loader_wayland_buffer *lwb)
lwb->flow_id = 0;
free(lwb->name);
lwb->name = NULL;
lwb->acquisition_time = 0;
}
void
loader_wayland_buffer_set_flow(struct loader_wayland_buffer *lwb, uint64_t flow_id)
{
lwb->flow_id = flow_id;
lwb->acquisition_time = os_time_get_nano();
}
bool
@ -209,6 +230,8 @@ loader_wayland_wrap_surface(struct loader_wayland_surface *lws,
struct wl_surface *wl_surface,
struct wl_event_queue *queue)
{
char *track_name;
lws->surface = wl_surface;
lws->wrapper = wl_proxy_create_wrapper(wl_surface);
if (!lws->wrapper)
@ -217,6 +240,11 @@ loader_wayland_wrap_surface(struct loader_wayland_surface *lws,
lws->id = wl_proxy_get_id((struct wl_proxy *)wl_surface);
wl_proxy_set_queue((struct wl_proxy *)lws->wrapper, queue);
asprintf(&track_name, "wl%d presentation", lws->id);
lws->analytics.presentation_track_id = util_perfetto_new_track(track_name);
free(track_name);
asprintf(&lws->analytics.latency_str, "wl%d latency", lws->id);
return true;
}
@ -229,4 +257,178 @@ loader_wayland_surface_destroy(struct loader_wayland_surface *lws)
wl_proxy_wrapper_destroy(lws->wrapper);
lws->surface = NULL;
lws->id = 0;
free(lws->analytics.latency_str);
lws->analytics.latency_str = NULL;
}
static void
loader_wayland_trace_present(struct loader_wayland_presentation_feedback_data *fd,
uint64_t presentation_time)
{
struct loader_wayland_surface *lws;
UNUSED clockid_t clock;
if (!fd->tracing || !util_perfetto_is_tracing_enabled())
return;
lws = fd->presentation->wayland_surface;
clock = fd->presentation->clock_id;
MESA_TRACE_SET_COUNTER(lws->analytics.latency_str,
(presentation_time - fd->buffer_acquisition_time) / 1000000.0);
/* Close the previous image display interval first, if there is one. */
if (lws->analytics.presenting) {
MESA_TRACE_TIMESTAMP_END(fd->buffer_name,
lws->analytics.presentation_track_id,
clock, presentation_time);
}
lws->analytics.presenting = fd->buffer_id;
MESA_TRACE_TIMESTAMP_BEGIN(fd->buffer_name,
lws->analytics.presentation_track_id,
fd->flow_id,
clock, presentation_time);
}
static void
presentation_handle_sync_output(void *data,
struct wp_presentation_feedback *feedback,
struct wl_output *output)
{
}
static void
feedback_fini(struct loader_wayland_presentation_feedback_data *fd)
{
if (fd->tracing)
free(fd->buffer_name);
wp_presentation_feedback_destroy(fd->feedback);
list_del(&fd->link);
free(fd);
}
static void
presentation_handle_presented(void *data,
struct wp_presentation_feedback *feedback,
uint32_t tv_sec_hi, uint32_t tv_sec_lo,
uint32_t tv_nsec, uint32_t refresh,
uint32_t seq_hi, uint32_t seq_lo,
uint32_t flags)
{
struct loader_wayland_presentation_feedback_data *fd = data;
struct loader_wayland_presentation *pres = fd->presentation;
struct timespec presentation_ts;
uint64_t presentation_time;
MESA_TRACE_FUNC_FLOW(&fd->flow_id);
presentation_ts.tv_sec = ((uint64_t)tv_sec_hi << 32) + tv_sec_lo;
presentation_ts.tv_nsec = tv_nsec;
presentation_time = timespec_to_nsec(&presentation_ts);
loader_wayland_trace_present(fd, presentation_time);
if (pres->presented_callback) {
pres->presented_callback(fd->callback_data, presentation_time, refresh);
}
feedback_fini(fd);
}
static void
presentation_handle_discarded(void *data,
struct wp_presentation_feedback *feedback)
{
struct loader_wayland_presentation_feedback_data *fd = data;
struct loader_wayland_presentation *pres = fd->presentation;
MESA_TRACE_FUNC_FLOW(&fd->flow_id);
if (pres->discarded_callback)
pres->discarded_callback(fd->callback_data);
feedback_fini(fd);
}
static const struct wp_presentation_feedback_listener
pres_feedback_listener = {
presentation_handle_sync_output,
presentation_handle_presented,
presentation_handle_discarded,
};
void
loader_wayland_wrap_presentation(struct loader_wayland_presentation *lpf,
struct wp_presentation *wp_presentation,
struct wl_event_queue *queue,
clockid_t presentation_clock_id,
struct loader_wayland_surface *lws,
void (*presented_callback)(void *data, uint64_t pres_time, uint32_t refresh),
void (*discarded_callback)(void *data),
void (*teardown_callback)(void *data))
{
lpf->presentation = wl_proxy_create_wrapper(wp_presentation);
wl_proxy_set_queue((void *)lpf->presentation, queue);
lpf->clock_id = presentation_clock_id;
lpf->wayland_surface = lws;
lpf->presented_callback = presented_callback;
lpf->discarded_callback = discarded_callback;
lpf->teardown_callback = teardown_callback;
list_inithead(&lpf->outstanding_list);
}
void
loader_wayland_presentation_destroy(struct loader_wayland_presentation *pres)
{
struct loader_wayland_presentation_feedback_data *fb, *tmp;
if (pres->presentation == NULL)
return;
LIST_FOR_EACH_ENTRY_SAFE(fb, tmp, &pres->outstanding_list, link) {
if (pres->teardown_callback)
pres->teardown_callback(fb->callback_data);
feedback_fini(fb);
}
wl_proxy_wrapper_destroy(pres->presentation);
pres->presentation = NULL;
}
void
loader_wayland_presentation_feedback(struct loader_wayland_presentation *pres,
struct loader_wayland_buffer *lwb,
void *callback_data)
{
struct loader_wayland_presentation_feedback_data *fd;
bool tracing = false;
if (!pres->presentation)
return;
tracing = util_perfetto_is_tracing_enabled();
if (!pres->presented_callback &&
!pres->discarded_callback &&
!tracing)
return;
fd = malloc(sizeof *fd);
fd->presentation = pres;
fd->tracing = tracing;
fd->flow_id = lwb->flow_id;
if (tracing) {
fd->buffer_name = strdup(lwb->name);
fd->buffer_id = lwb->id;
fd->buffer_acquisition_time = lwb->acquisition_time;
}
fd->callback_data = callback_data;
fd->feedback = wp_presentation_feedback(pres->presentation,
pres->wayland_surface->wrapper);
wp_presentation_feedback_add_listener(fd->feedback,
&pres_feedback_listener,
fd);
list_add(&fd->link, &pres->outstanding_list);
}

View file

@ -25,18 +25,40 @@
#include <util/timespec.h>
#include <wayland-client.h>
#include "presentation-time-client-protocol.h"
#include "util/list.h"
struct loader_wayland_presentation_feedback_data;
struct loader_wayland_buffer {
struct wl_buffer *buffer;
uint32_t id;
uint64_t flow_id;
uint64_t acquisition_time;
char *name;
};
struct loader_wayland_surface_analytics {
char *latency_str;
uint64_t presenting;
uint64_t presentation_track_id;
};
struct loader_wayland_surface {
struct wl_surface *surface;
struct wl_surface *wrapper;
uint32_t id;
struct loader_wayland_surface_analytics analytics;
};
struct loader_wayland_presentation {
struct wp_presentation *presentation;
clockid_t clock_id;
struct loader_wayland_surface *wayland_surface;
void (*presented_callback)(void *data, uint64_t pres_time, uint32_t refresh);
void (*discarded_callback)(void *data);
void (*teardown_callback)(void *data);
struct list_head outstanding_list;
};
#ifndef HAVE_WL_DISPATCH_QUEUE_TIMEOUT
@ -75,4 +97,22 @@ loader_wayland_wrap_surface(struct loader_wayland_surface *lws,
void
loader_wayland_surface_destroy(struct loader_wayland_surface *lws);
void
loader_wayland_wrap_presentation(struct loader_wayland_presentation *lwp,
struct wp_presentation *wp_presentation,
struct wl_event_queue *queue,
clockid_t presentation_clock_id,
struct loader_wayland_surface *lws,
void (*presented_callback)(void *data, uint64_t pres_time, uint32_t refresh),
void (*discarded_callback)(void *data),
void (*teardown_callback)(void *data));
void
loader_wayland_presentation_destroy(struct loader_wayland_presentation *lwp);
void
loader_wayland_presentation_feedback(struct loader_wayland_presentation *pres,
struct loader_wayland_buffer *lwb,
void *callback_data);
#endif

View file

@ -34,6 +34,7 @@ if with_platform_wayland
libloader_wayland_helper = static_library(
'loader_wayland_helper',
'loader_wayland_helper.c',
wp_files['presentation-time'],
gnu_symbol_visibility : 'hidden',
include_directories : [inc_include, inc_src, inc_gallium],
dependencies : [

View file

@ -182,13 +182,6 @@ struct wsi_wl_surface {
struct loader_wayland_surface wayland_surface;
struct wsi_wl_display *display;
/* This has no functional use, and is here only for perfetto */
struct {
char *latency_str;
uint64_t presenting;
uint64_t presentation_track_id;
} analytics;
struct zwp_linux_dmabuf_feedback_v1 *wl_dmabuf_feedback;
struct dmabuf_feedback dmabuf_feedback, pending_dmabuf_feedback;
@ -238,10 +231,10 @@ struct wsi_wl_swapchain {
uint64_t prev_max_present_id;
uint64_t outstanding_count;
struct wl_list outstanding_list;
struct wl_list fallback_frame_list;
struct u_cnd_monotonic list_advanced;
struct wl_event_queue *queue;
struct wp_presentation *wp_presentation;
struct loader_wayland_presentation wayland_presentation;
/* Fallback when wp_presentation is not supported */
struct wl_surface *surface;
bool dispatch_in_progress;
@ -1996,15 +1989,6 @@ wsi_wl_surface_get_present_rectangles(VkIcdSurfaceBase *surface,
return vk_outarray_status(&out);
}
static void
wsi_wl_surface_analytics_fini(struct wsi_wl_surface *wsi_wl_surface,
const VkAllocationCallbacks *parent_pAllocator,
const VkAllocationCallbacks *pAllocator)
{
vk_free2(parent_pAllocator, pAllocator,
wsi_wl_surface->analytics.latency_str);
}
void
wsi_wl_surface_destroy(VkIcdSurfaceBase *icd_surface, VkInstance _instance,
const VkAllocationCallbacks *pAllocator)
@ -2030,8 +2014,6 @@ wsi_wl_surface_destroy(VkIcdSurfaceBase *icd_surface, VkInstance _instance,
if (wsi_wl_surface->display)
wsi_wl_display_destroy(wsi_wl_surface->display);
wsi_wl_surface_analytics_fini(wsi_wl_surface, &instance->alloc, pAllocator);
vk_free2(&instance->alloc, pAllocator, wsi_wl_surface);
}
@ -2245,25 +2227,6 @@ fail:
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
static void
wsi_wl_surface_analytics_init(struct wsi_wl_surface *wsi_wl_surface,
const VkAllocationCallbacks *pAllocator)
{
uint64_t wl_id;
char *track_name;
wl_id = wsi_wl_surface->wayland_surface.id;
track_name = vk_asprintf(pAllocator, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT,
"wl%" PRIu64 " presentation", wl_id);
wsi_wl_surface->analytics.presentation_track_id = util_perfetto_new_track(track_name);
vk_free(pAllocator, track_name);
wsi_wl_surface->analytics.latency_str =
vk_asprintf(pAllocator,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT,
"wl%" PRIu64 " latency", wl_id);
}
static VkResult wsi_wl_surface_init(struct wsi_wl_surface *wsi_wl_surface,
struct wsi_device *wsi_device,
const VkAllocationCallbacks *pAllocator)
@ -2309,8 +2272,6 @@ static VkResult wsi_wl_surface_init(struct wsi_wl_surface *wsi_wl_surface,
goto fail;
}
wsi_wl_surface_analytics_init(wsi_wl_surface, pAllocator);
return VK_SUCCESS;
fail:
@ -2353,7 +2314,6 @@ wsi_CreateWaylandSurfaceKHR(VkInstance _instance,
}
struct wsi_wl_present_id {
struct wp_presentation_feedback *feedback;
/* Fallback when wp_presentation is not supported.
* Using frame callback is not the intended way to achieve
* this, but it is the best effort alternative when the proper interface is
@ -2362,7 +2322,6 @@ struct wsi_wl_present_id {
struct wl_callback *frame;
uint64_t present_id;
uint64_t flow_id;
struct loader_wayland_buffer *buffer;
uint64_t submission_time;
const VkAllocationCallbacks *alloc;
struct wsi_wl_swapchain *chain;
@ -2708,13 +2667,6 @@ wsi_wl_swapchain_acquire_next_image_implicit(struct wsi_swapchain *wsi_chain,
}
}
static void
presentation_handle_sync_output(void *data,
struct wp_presentation_feedback *feedback,
struct wl_output *output)
{
}
static void
wsi_wl_presentation_update_present_id(struct wsi_wl_present_id *id)
{
@ -2724,62 +2676,19 @@ wsi_wl_presentation_update_present_id(struct wsi_wl_present_id *id)
id->chain->present_ids.max_completed = id->present_id;
id->chain->present_ids.display_time_correction -= id->correction;
wl_list_remove(&id->link);
mtx_unlock(&id->chain->present_ids.lock);
vk_free(id->alloc, id);
}
static void
trace_present(const struct wsi_wl_present_id *id,
uint64_t presentation_time)
{
struct wsi_wl_swapchain *chain = id->chain;
struct wsi_wl_surface *surface = chain->wsi_wl_surface;
if (!util_perfetto_is_tracing_enabled())
return;
MESA_TRACE_SET_COUNTER(surface->analytics.latency_str,
(presentation_time - id->submission_time) / 1000000.0);
/* Close the previous image display interval first, if there is one. */
if (surface->analytics.presenting) {
MESA_TRACE_TIMESTAMP_END(id->buffer->name,
surface->analytics.presentation_track_id,
chain->wsi_wl_surface->display->presentation_clock_id, presentation_time);
}
surface->analytics.presenting = id->buffer->id;
MESA_TRACE_TIMESTAMP_BEGIN(id->buffer->name,
surface->analytics.presentation_track_id,
id->flow_id,
chain->wsi_wl_surface->display->presentation_clock_id, presentation_time);
}
static void
presentation_handle_presented(void *data,
struct wp_presentation_feedback *feedback,
uint32_t tv_sec_hi, uint32_t tv_sec_lo,
uint32_t tv_nsec, uint32_t refresh,
uint32_t seq_hi, uint32_t seq_lo,
uint32_t flags)
uint64_t presentation_time,
uint32_t refresh)
{
struct wsi_wl_present_id *id = data;
struct timespec presentation_ts;
uint64_t presentation_time;
MESA_TRACE_FUNC_FLOW(&id->flow_id);
struct wsi_wl_swapchain *chain = id->chain;
uint64_t target_time = id->target_time;
presentation_ts.tv_sec = ((uint64_t)tv_sec_hi << 32) + tv_sec_lo;
presentation_ts.tv_nsec = tv_nsec;
presentation_time = timespec_to_nsec(&presentation_ts);
trace_present(id, presentation_time);
mtx_lock(&chain->present_ids.lock);
chain->present_ids.refresh_nsec = refresh;
if (!chain->present_ids.valid_refresh_nsec) {
@ -2798,16 +2707,12 @@ presentation_handle_presented(void *data,
mtx_unlock(&chain->present_ids.lock);
wsi_wl_presentation_update_present_id(id);
wp_presentation_feedback_destroy(feedback);
}
static void
presentation_handle_discarded(void *data,
struct wp_presentation_feedback *feedback)
presentation_handle_discarded(void *data)
{
struct wsi_wl_present_id *id = data;
MESA_TRACE_FUNC_FLOW(&id->flow_id);
struct wsi_wl_swapchain *chain = id->chain;
mtx_lock(&chain->present_ids.lock);
@ -2821,20 +2726,26 @@ presentation_handle_discarded(void *data,
mtx_unlock(&chain->present_ids.lock);
wsi_wl_presentation_update_present_id(id);
wp_presentation_feedback_destroy(feedback);
}
static const struct wp_presentation_feedback_listener
pres_feedback_listener = {
presentation_handle_sync_output,
presentation_handle_presented,
presentation_handle_discarded,
};
static void
presentation_handle_teardown(void *data)
{
struct wsi_wl_present_id *id = data;
vk_free(id->alloc, id);
}
static void
presentation_frame_handle_done(void *data, struct wl_callback *callback, uint32_t serial)
{
struct wsi_wl_present_id *id = data;
struct wsi_wl_swapchain *chain = id->chain;
mtx_lock(&chain->present_ids.lock);
wl_list_remove(&id->link);
mtx_unlock(&chain->present_ids.lock);
wsi_wl_presentation_update_present_id(id);
wl_callback_destroy(callback);
}
@ -3030,13 +2941,6 @@ wsi_wl_swapchain_queue_present(struct wsi_swapchain *wsi_chain,
id->chain = chain;
id->present_id = present_id;
id->alloc = chain->wsi_wl_surface->display->wsi_wl->alloc;
id->buffer = &chain->images[image_index].wayland_buffer;
/* The buffer can be reused for another flow before the feedback event
* arrives, so we need a copy.
*/
id->flow_id = id->buffer->flow_id;
id->submission_time = os_time_get_nano();
mtx_lock(&chain->present_ids.lock);
@ -3044,14 +2948,13 @@ wsi_wl_swapchain_queue_present(struct wsi_swapchain *wsi_chain,
timestamped = set_timestamp(chain, &id->target_time, &id->correction);
if (!chain->present_ids.frame_fallback) {
id->feedback = wp_presentation_feedback(chain->present_ids.wp_presentation,
chain->wsi_wl_surface->wayland_surface.wrapper);
wp_presentation_feedback_add_listener(id->feedback,
&pres_feedback_listener,
id);
loader_wayland_presentation_feedback(&chain->present_ids.wayland_presentation,
&chain->images[image_index].wayland_buffer,
id);
} else {
id->frame = wl_surface_frame(chain->present_ids.surface);
wl_callback_add_listener(id->frame, &pres_frame_listener, id);
wl_list_insert(&chain->present_ids.fallback_frame_list, &id->link);
}
chain->present_ids.prev_max_present_id = chain->present_ids.max_present_id;
@ -3069,7 +2972,6 @@ wsi_wl_swapchain_queue_present(struct wsi_swapchain *wsi_chain,
}
chain->present_ids.outstanding_count++;
wl_list_insert(&chain->present_ids.outstanding_list, &id->link);
mtx_unlock(&chain->present_ids.lock);
}
@ -3359,17 +3261,14 @@ wsi_wl_swapchain_chain_free(struct wsi_wl_swapchain *chain,
* Waiting for the swapchain fence is enough.
* Just clean up anything user did not wait for. */
struct wsi_wl_present_id *id, *tmp;
wl_list_for_each_safe(id, tmp, &chain->present_ids.outstanding_list, link) {
if (id->feedback)
wp_presentation_feedback_destroy(id->feedback);
if (id->frame)
wl_callback_destroy(id->frame);
wl_list_for_each_safe(id, tmp, &chain->present_ids.fallback_frame_list, link) {
wl_callback_destroy(id->frame);
wl_list_remove(&id->link);
vk_free(id->alloc, id);
}
if (chain->present_ids.wp_presentation)
wl_proxy_wrapper_destroy(chain->present_ids.wp_presentation);
loader_wayland_presentation_destroy(&chain->present_ids.wayland_presentation);
if (chain->present_ids.surface)
wl_proxy_wrapper_destroy(chain->present_ids.surface);
u_cnd_monotonic_destroy(&chain->present_ids.list_advanced);
@ -3437,7 +3336,7 @@ wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
if (chain == NULL)
return VK_ERROR_OUT_OF_HOST_MEMORY;
wl_list_init(&chain->present_ids.outstanding_list);
wl_list_init(&chain->present_ids.fallback_frame_list);
/* We are taking ownership of the wsi_wl_surface, so remove ownership from
* oldSwapchain. If the surface is currently owned by a swapchain that is
@ -3625,10 +3524,14 @@ wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
if (chain->wsi_wl_surface->display->wp_presentation_notwrapped) {
chain->present_ids.frame_fallback = false;
chain->present_ids.wp_presentation =
wl_proxy_create_wrapper(chain->wsi_wl_surface->display->wp_presentation_notwrapped);
wl_proxy_set_queue((struct wl_proxy *) chain->present_ids.wp_presentation,
chain->present_ids.queue);
loader_wayland_wrap_presentation(&chain->present_ids.wayland_presentation,
chain->wsi_wl_surface->display->wp_presentation_notwrapped,
chain->present_ids.queue,
chain->wsi_wl_surface->display->presentation_clock_id,
&chain->wsi_wl_surface->wayland_surface,
presentation_handle_presented,
presentation_handle_discarded,
presentation_handle_teardown);
} else {
/* Fallback to frame callbacks when presentation protocol is not available.
* We already have a proxy for the surface, but need another since