From 04acb0eed8a9cb69af52484ae8803bbbafdd5bf2 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Thu, 19 Dec 2024 14:24:38 -0600 Subject: [PATCH] egl/dri/wayland: Use presentation feedback to track frame delivery Use the new shared presentation feedback code in the loader to implement perfetto frame delivery tracing similar to the wayland vulkan wsi code. Signed-off-by: Derek Foreman Part-of: --- src/egl/drivers/dri2/egl_dri2.h | 3 + src/egl/drivers/dri2/platform_wayland.c | 87 ++++++++++++++++++++++--- 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h index 20dfb3e1dde..94a5a3a4bd4 100644 --- a/src/egl/drivers/dri2/egl_dri2.h +++ b/src/egl/drivers/dri2/egl_dri2.h @@ -282,6 +282,7 @@ struct dri2_egl_display { struct wl_shm *wl_shm; struct wl_event_queue *wl_queue; struct zwp_linux_dmabuf_v1 *wl_dmabuf; + struct wp_presentation *wp_presentation; struct dri2_wl_formats formats; struct zwp_linux_dmabuf_feedback_v1 *wl_dmabuf_feedback; struct dmabuf_feedback_format_table format_table; @@ -289,6 +290,7 @@ struct dri2_egl_display { uint32_t capabilities; char *device_name; bool is_render_node; + clockid_t presentation_clock_id; #endif #ifdef HAVE_ANDROID_PLATFORM @@ -331,6 +333,7 @@ struct dri2_egl_surface { struct wl_callback *throttle_callback; struct zwp_linux_dmabuf_feedback_v1 *wl_dmabuf_feedback; struct dmabuf_feedback dmabuf_feedback, pending_dmabuf_feedback; + struct loader_wayland_presentation wayland_presentation; bool compositor_using_another_device; int format; bool resized; diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c index aa2f61ab122..47fd8bcd692 100644 --- a/src/egl/drivers/dri2/platform_wayland.c +++ b/src/egl/drivers/dri2/platform_wayland.c @@ -42,6 +42,7 @@ #include #include "util/anon_file.h" +#include "util/perf/cpu_trace.h" #include "util/u_vector.h" #include "util/format/u_formats.h" #include "main/glconfig.h" @@ -762,6 +763,15 @@ dri2_wl_create_window_surface(_EGLDisplay *disp, _EGLConfig *conf, goto cleanup_dpy_wrapper; } + if (dri2_dpy->wp_presentation) { + loader_wayland_wrap_presentation(&dri2_surf->wayland_presentation, + dri2_dpy->wp_presentation, + dri2_surf->wl_queue, + dri2_dpy->presentation_clock_id, + &dri2_surf->wayland_surface, + NULL, NULL, NULL); + } + if (dri2_dpy->wl_dmabuf && zwp_linux_dmabuf_v1_get_version(dri2_dpy->wl_dmabuf) >= ZWP_LINUX_DMABUF_V1_GET_SURFACE_FEEDBACK_SINCE_VERSION) { @@ -874,6 +884,8 @@ dri2_wl_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf) dri2_surf->wl_win->destroy_window_callback = NULL; } + loader_wayland_presentation_destroy(&dri2_surf->wayland_presentation); + loader_wayland_surface_destroy(&dri2_surf->wayland_surface); wl_proxy_wrapper_destroy(dri2_surf->wl_dpy_wrapper); if (dri2_surf->wl_drm_wrapper) @@ -1182,7 +1194,8 @@ wait_for_free_buffer(struct dri2_egl_display *dri2_dpy, } static int -get_back_bo(struct dri2_egl_surface *dri2_surf) +get_back_bo(struct dri2_egl_surface *dri2_surf, + struct mesa_trace_flow *flow) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(dri2_surf->base.Resource.Display); @@ -1191,6 +1204,8 @@ get_back_bo(struct dri2_egl_surface *dri2_surf) unsigned int pipe_format; unsigned int linear_pipe_format; + MESA_TRACE_FUNC_FLOW(flow); + visual_idx = dri2_wl_visual_idx_from_fourcc(dri2_surf->format); assert(visual_idx != -1); pipe_format = dri2_wl_visuals[visual_idx].pipe_format; @@ -1355,6 +1370,7 @@ get_back_bo(struct dri2_egl_surface *dri2_surf) if (dri2_surf->back->dri_image == NULL) return -1; + loader_wayland_buffer_set_flow(&dri2_surf->back->wayland_buffer, flow); dri2_surf->back->locked = true; return 0; @@ -1385,11 +1401,14 @@ back_bo_to_dri_buffer(struct dri2_egl_surface *dri2_surf, __DRIbuffer *buffer) #define BUFFER_TRIM_AGE_HYSTERESIS 20 static int -update_buffers(struct dri2_egl_surface *dri2_surf) +update_buffers(struct dri2_egl_surface *dri2_surf, + struct mesa_trace_flow *flow) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(dri2_surf->base.Resource.Display); + MESA_TRACE_FUNC_FLOW(flow); + if (dri2_surf->wl_win && (dri2_surf->base.Width != dri2_surf->wl_win->width || dri2_surf->base.Height != dri2_surf->wl_win->height)) { @@ -1406,7 +1425,7 @@ update_buffers(struct dri2_egl_surface *dri2_surf) dri2_surf->received_dmabuf_feedback = false; } - if (get_back_bo(dri2_surf) < 0) { + if (get_back_bo(dri2_surf, flow) < 0) { _eglError(EGL_BAD_ALLOC, "failed to allocate color buffer"); return -1; } @@ -1436,12 +1455,15 @@ update_buffers(struct dri2_egl_surface *dri2_surf) } static int -update_buffers_if_needed(struct dri2_egl_surface *dri2_surf) +update_buffers_if_needed(struct dri2_egl_surface *dri2_surf, + struct mesa_trace_flow *flow) { + MESA_TRACE_FUNC_FLOW(flow); + if (dri2_surf->back != NULL) return 0; - return update_buffers(dri2_surf); + return update_buffers(dri2_surf, flow); } static int @@ -1450,8 +1472,11 @@ image_get_buffers(struct dri_drawable *driDrawable, unsigned int format, struct __DRIimageList *buffers) { struct dri2_egl_surface *dri2_surf = loaderPrivate; + struct mesa_trace_flow flow = {0}; - if (update_buffers_if_needed(dri2_surf) < 0) + MESA_TRACE_FUNC_FLOW(&flow); + + if (update_buffers_if_needed(dri2_surf, &flow) < 0) return 0; buffers->image_mask = __DRI_IMAGE_BUFFER_BACK; @@ -1683,10 +1708,16 @@ dri2_wl_swap_buffers_with_damage(_EGLDisplay *disp, _EGLSurface *draw, { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw); + struct mesa_trace_flow flow = { 0 }; if (!dri2_surf->wl_win) return _eglError(EGL_BAD_NATIVE_WINDOW, "dri2_swap_buffers"); + if (dri2_surf->back) + flow = dri2_surf->back->wayland_buffer.flow; + + MESA_TRACE_FUNC_FLOW(&flow); + /* Flush (and finish glthread) before: * - update_buffers_if_needed because the unmarshalling thread * may be running currently, and we would concurrently alloc/free @@ -1709,7 +1740,7 @@ dri2_wl_swap_buffers_with_damage(_EGLDisplay *disp, _EGLSurface *draw, /* Make sure we have a back buffer in case we're swapping without ever * rendering. */ - if (update_buffers_if_needed(dri2_surf) < 0) + if (update_buffers_if_needed(dri2_surf, &flow) < 0) return _eglError(EGL_BAD_ALLOC, "dri2_swap_buffers"); if (draw->SwapInterval > 0) { @@ -1742,6 +1773,8 @@ dri2_wl_swap_buffers_with_damage(_EGLDisplay *disp, _EGLSurface *draw, wl_buffer_add_listener(dri2_surf->current->wayland_buffer.buffer, &wl_buffer_listener, dri2_surf); + + loader_wayland_buffer_set_flow(&dri2_surf->current->wayland_buffer, &flow); } wl_surface_attach(dri2_surf->wayland_surface.wrapper, @@ -1774,6 +1807,10 @@ dri2_wl_swap_buffers_with_damage(_EGLDisplay *disp, _EGLSurface *draw, dri_flush_drawable(dri_drawable); } + loader_wayland_presentation_feedback(&dri2_surf->wayland_presentation, + &dri2_surf->current->wayland_buffer, + NULL); + wl_surface_commit(dri2_surf->wayland_surface.wrapper); /* If we're not waiting for a frame callback then we'll at least throttle @@ -1795,8 +1832,11 @@ static EGLint dri2_wl_query_buffer_age(_EGLDisplay *disp, _EGLSurface *surface) { struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surface); + struct mesa_trace_flow flow = { 0 }; - if (update_buffers_if_needed(dri2_surf) < 0) { + MESA_TRACE_FUNC_FLOW(&flow); + + if (update_buffers_if_needed(dri2_surf, &flow) < 0) { _eglError(EGL_BAD_ALLOC, "dri2_query_buffer_age"); return -1; } @@ -2106,6 +2146,18 @@ static const struct zwp_linux_dmabuf_feedback_v1_listener .done = default_dmabuf_feedback_done, }; +static void +presentation_handle_clock_id(void* data, struct wp_presentation *wp_presentation, uint32_t clk_id) +{ + struct dri2_egl_display *dri2_dpy = data; + + dri2_dpy->presentation_clock_id = clk_id; +} + +static const struct wp_presentation_listener presentation_listener = { + presentation_handle_clock_id, +}; + static void registry_handle_global_drm(void *data, struct wl_registry *registry, uint32_t name, const char *interface, @@ -2123,6 +2175,11 @@ registry_handle_global_drm(void *data, struct wl_registry *registry, MIN2(version, ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION)); zwp_linux_dmabuf_v1_add_listener(dri2_dpy->wl_dmabuf, &dmabuf_listener, dri2_dpy); + } else if (strcmp(interface, wp_presentation_interface.name) == 0) { + dri2_dpy->wp_presentation = + wl_registry_bind(registry, name, &wp_presentation_interface, 1); + wp_presentation_add_listener(dri2_dpy->wp_presentation, + &presentation_listener, dri2_dpy); } } @@ -2867,7 +2924,13 @@ registry_handle_global_kopper(void *data, struct wl_registry *registry, ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION)); zwp_linux_dmabuf_v1_add_listener(dri2_dpy->wl_dmabuf, &dmabuf_listener, dri2_dpy); + } else if (strcmp(interface, wp_presentation_interface.name) == 0) { + dri2_dpy->wp_presentation = + wl_registry_bind(registry, name, &wp_presentation_interface, 1); + wp_presentation_add_listener(dri2_dpy->wp_presentation, + &presentation_listener, dri2_dpy); } + } static const struct wl_registry_listener registry_listener_kopper = { @@ -2885,7 +2948,13 @@ registry_handle_global_swrast(void *data, struct wl_registry *registry, if (strcmp(interface, wl_shm_interface.name) == 0) { dri2_dpy->wl_shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); wl_shm_add_listener(dri2_dpy->wl_shm, &shm_listener, dri2_dpy); + } else if (strcmp(interface, wp_presentation_interface.name) == 0) { + dri2_dpy->wp_presentation = + wl_registry_bind(registry, name, &wp_presentation_interface, 1); + wp_presentation_add_listener(dri2_dpy->wp_presentation, + &presentation_listener, dri2_dpy); } + } static const struct wl_registry_listener registry_listener_swrast = { @@ -3132,6 +3201,8 @@ void dri2_teardown_wayland(struct dri2_egl_display *dri2_dpy) { dri2_wl_formats_fini(&dri2_dpy->formats); + if (dri2_dpy->wp_presentation) + wp_presentation_destroy(dri2_dpy->wp_presentation); if (dri2_dpy->wl_drm) wl_drm_destroy(dri2_dpy->wl_drm); if (dri2_dpy->wl_dmabuf)