[PATCH weston v3 4/4] gl-renderer: Emit GPU rendering begin and end timeline timepoints

Alexandros Frantzis alexandros.frantzis at collabora.com
Wed Sep 27 12:09:16 UTC 2017


Use EGL fence sync objects to emit timepoints for the beginning and the
end of rendering on the GPU. The timepoints are emitted asynchronously
using the sync file fds associated with the fence sync objects. The sync
file fds are acquired using the facilities provided by the
EGL_ANDROID_native_fence_sync extension.

The asynchronous timepoint submissions are stored in a list in
gl_output_state until they are executed, and any pending submissions
that remain at output destruction time are cleaned up.

If timelining is inactive or the required EGL extensions are not
present, then GPU timepoint processing and emission are skipped.

Note that the GPU timestamps returned by sync files are in the
CLOCK_MONOTONIC clock domain, and are thus compatible with the
timeline timestamps (which also are in the CLOCK_MONOTONIC domain).

Signed-off-by: Alexandros Frantzis <alexandros.frantzis at collabora.com>
Reviewed-by: Daniel Stone <daniels at collabora.com>
---

Changes in v3:
 - Fix missing "_H" in HAVE_LINUX_SYNC_FILE_H check.
 - Add comments to list and list link variables.
 - Fix compiler warning in zero initialization of structs with older
   gcc versions.

Changes in v2:
 - Added Signed-off-by.
 - Used fallback sync_file.h header if required.
 - Improved error checking.
 - Used enum timeline_render_point_type instead of int.
 - Stored pending timeline submissions in a list in gl_output_state.
 - Renamed various struct and function to improve consistency and clarity.
 - Added fallbacks for newly used EGL definitions.
 - Added log messages for render gpu related warnings.

 libweston/gl-renderer.c | 163 ++++++++++++++++++++++++++++++++++++++++++++++++
 shared/weston-egl-ext.h |  12 ++++
 2 files changed, 175 insertions(+)

diff --git a/libweston/gl-renderer.c b/libweston/gl-renderer.c
index c2e88a65..5749aa71 100644
--- a/libweston/gl-renderer.c
+++ b/libweston/gl-renderer.c
@@ -39,6 +39,16 @@
 #include <assert.h>
 #include <linux/input.h>
 #include <drm_fourcc.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_LINUX_SYNC_FILE_H
+#include <linux/sync_file.h>
+#else
+#include "weston-sync-file.h"
+#endif
+
+#include "timeline.h"
 
 #include "gl-renderer.h"
 #include "vertex-clipping.h"
@@ -47,6 +57,7 @@
 
 #include "shared/helpers.h"
 #include "shared/platform.h"
+#include "shared/timespec-util.h"
 #include "weston-egl-ext.h"
 
 struct gl_shader {
@@ -87,6 +98,9 @@ struct gl_output_state {
 	enum gl_border_status border_status;
 
 	struct weston_matrix output_matrix;
+
+	/* struct timeline_render_point::link */
+	struct wl_list timeline_render_point_list;
 };
 
 enum buffer_type {
@@ -238,6 +252,20 @@ struct gl_renderer {
 	PFNEGLDUPNATIVEFENCEFDANDROIDPROC dup_native_fence_fd;
 };
 
+enum timeline_render_point_type {
+	TIMELINE_RENDER_POINT_TYPE_BEGIN,
+	TIMELINE_RENDER_POINT_TYPE_END
+};
+
+struct timeline_render_point {
+	struct wl_list link; /* gl_output_state::timeline_render_point_list */
+
+	enum timeline_render_point_type type;
+	int fd;
+	struct weston_output *output;
+	struct wl_event_source *event_source;
+};
+
 static PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = NULL;
 
 static inline const char *
@@ -274,6 +302,115 @@ get_renderer(struct weston_compositor *ec)
 	return (struct gl_renderer *)ec->renderer;
 }
 
+static int
+linux_sync_file_read_timestamp(int fd, uint64_t *ts)
+{
+	struct sync_file_info file_info = { { 0 } };
+	struct sync_fence_info fence_info = { { 0 } };
+
+	assert(ts != NULL);
+
+	file_info.sync_fence_info = (uint64_t)(uintptr_t)&fence_info;
+	file_info.num_fences = 1;
+
+	if (ioctl(fd, SYNC_IOC_FILE_INFO, &file_info) < 0)
+		return -1;
+
+	*ts = fence_info.timestamp_ns;
+
+	return 0;
+}
+
+static void
+timeline_render_point_destroy(struct timeline_render_point *trp)
+{
+	wl_list_remove(&trp->link);
+	wl_event_source_remove(trp->event_source);
+	close(trp->fd);
+	free(trp);
+}
+
+static int
+timeline_render_point_handler(int fd, uint32_t mask, void *data)
+{
+	struct timeline_render_point *trp = data;
+	const char *tp_name = trp->type == TIMELINE_RENDER_POINT_TYPE_BEGIN ?
+			      "renderer_gpu_begin" : "renderer_gpu_end";
+
+	if (mask & WL_EVENT_READABLE) {
+		uint64_t ts;
+
+		if (linux_sync_file_read_timestamp(trp->fd, &ts) == 0) {
+			struct timespec tspec = { 0 };
+
+			timespec_add_nsec(&tspec, &tspec, ts);
+
+			TL_POINT(tp_name, TLP_GPU(&tspec),
+				 TLP_OUTPUT(trp->output), TLP_END);
+		}
+	}
+
+	timeline_render_point_destroy(trp);
+
+	return 0;
+}
+
+static EGLSyncKHR
+timeline_create_render_sync(struct gl_renderer *gr)
+{
+	static const EGLint attribs[] = { EGL_NONE };
+
+	if (!weston_timeline_enabled_ || !gr->has_native_fence_sync)
+		return EGL_NO_SYNC_KHR;
+
+	return gr->create_sync(gr->egl_display, EGL_SYNC_NATIVE_FENCE_ANDROID,
+			       attribs);
+}
+
+static void
+timeline_submit_render_sync(struct gl_renderer *gr,
+			    struct weston_compositor *ec,
+			    struct weston_output *output,
+			    EGLSyncKHR sync,
+			    enum timeline_render_point_type type)
+{
+	struct gl_output_state *go;
+	struct wl_event_loop *loop;
+	int fd;
+	struct timeline_render_point *trp;
+
+	if (!weston_timeline_enabled_ ||
+	    !gr->has_native_fence_sync ||
+	    sync == EGL_NO_SYNC_KHR)
+		return;
+
+	go = get_output_state(output);
+	loop = wl_display_get_event_loop(ec->wl_display);
+
+	fd = gr->dup_native_fence_fd(gr->egl_display, sync);
+	if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID)
+		goto out;
+
+	trp = zalloc(sizeof *trp);
+	if (trp == NULL) {
+		close(fd);
+		goto out;
+	}
+
+	trp->type = type;
+	trp->fd = fd;
+	trp->output = output;
+	trp->event_source = wl_event_loop_add_fd(loop, fd,
+						 WL_EVENT_READABLE,
+						 timeline_render_point_handler,
+						 trp);
+
+	wl_list_insert(&go->timeline_render_point_list, &trp->link);
+
+out:
+	gr->destroy_sync(gr->egl_display, sync);
+}
+
 static struct egl_image*
 egl_image_create(struct gl_renderer *gr, EGLenum target,
 		 EGLClientBuffer buffer, const EGLint *attribs)
@@ -1106,10 +1243,13 @@ gl_renderer_repaint_output(struct weston_output *output,
 	pixman_box32_t *rects;
 	pixman_region32_t buffer_damage, total_damage;
 	enum gl_border_status border_damage = BORDER_STATUS_CLEAN;
+	EGLSyncKHR begin_render_sync, end_render_sync;
 
 	if (use_output(output) < 0)
 		return;
 
+	begin_render_sync = timeline_create_render_sync(gr);
+
 	/* Calculate the viewport */
 	glViewport(go->borders[GL_RENDERER_BORDER_LEFT].width,
 		   go->borders[GL_RENDERER_BORDER_BOTTOM].height,
@@ -1158,6 +1298,8 @@ gl_renderer_repaint_output(struct weston_output *output,
 	pixman_region32_copy(&output->previous_damage, output_damage);
 	wl_signal_emit(&output->frame_signal, output);
 
+	end_render_sync = timeline_create_render_sync(gr);
+
 	if (gr->swap_buffers_with_damage) {
 		pixman_region32_init(&buffer_damage);
 		weston_transformed_region(output->width, output->height,
@@ -1203,6 +1345,14 @@ gl_renderer_repaint_output(struct weston_output *output,
 	}
 
 	go->border_status = BORDER_STATUS_CLEAN;
+
+	/* We have to submit the render sync objects after swap buffers, since
+	 * the objects get assigned a valid sync file fd only after a gl flush.
+	 */
+	timeline_submit_render_sync(gr, compositor, output, begin_render_sync,
+				    TIMELINE_RENDER_POINT_TYPE_BEGIN);
+	timeline_submit_render_sync(gr, compositor, output, end_render_sync,
+				    TIMELINE_RENDER_POINT_TYPE_END);
 }
 
 static int
@@ -2827,6 +2977,8 @@ gl_renderer_output_create(struct weston_output *output,
 	for (i = 0; i < BUFFER_DAMAGE_COUNT; i++)
 		pixman_region32_init(&go->buffer_damage[i]);
 
+	wl_list_init(&go->timeline_render_point_list);
+
 	output->renderer_state = go;
 
 	return 0;
@@ -2867,6 +3019,7 @@ gl_renderer_output_destroy(struct weston_output *output)
 {
 	struct gl_renderer *gr = get_renderer(output->compositor);
 	struct gl_output_state *go = get_output_state(output);
+	struct timeline_render_point *trp, *tmp;
 	int i;
 
 	for (i = 0; i < 2; i++)
@@ -2878,6 +3031,13 @@ gl_renderer_output_destroy(struct weston_output *output)
 
 	weston_platform_destroy_egl_surface(gr->egl_display, go->egl_surface);
 
+	if (!wl_list_empty(&go->timeline_render_point_list))
+		weston_log("warning: discarding pending timeline render"
+			   "objects at output destruction");
+
+	wl_list_for_each_safe(trp, tmp, &go->timeline_render_point_list, link)
+		timeline_render_point_destroy(trp);
+
 	free(go);
 }
 
@@ -3042,6 +3202,9 @@ gl_renderer_setup_egl_extensions(struct weston_compositor *ec)
 		gr->dup_native_fence_fd =
 			(void *) eglGetProcAddress("eglDupNativeFenceFDANDROID");
 		gr->has_native_fence_sync = 1;
+	} else {
+		weston_log("warning: Disabling render GPU timeline due to "
+			   "missing EGL_ANDROID_native_fence_sync extension\n");
 	}
 
 	renderer_setup_egl_client_extensions(gr);
diff --git a/shared/weston-egl-ext.h b/shared/weston-egl-ext.h
index 8aacbd01..0784ea2d 100644
--- a/shared/weston-egl-ext.h
+++ b/shared/weston-egl-ext.h
@@ -181,6 +181,10 @@ typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPLATFORMPIXMAPSURFACEEXTPROC) (EGLD
 typedef void *EGLSyncKHR;
 #endif /* EGL_KHR_cl_event2 */
 
+#ifndef EGL_NO_SYNC_KHR
+#define EGL_NO_SYNC_KHR ((EGLSyncKHR)0)
+#endif
+
 #ifndef EGL_KHR_fence_sync
 #define EGL_KHR_fence_sync 1
 typedef EGLSyncKHR (EGLAPIENTRYP PFNEGLCREATESYNCKHRPROC) (EGLDisplay dpy, EGLenum type, const EGLint *attrib_list);
@@ -192,6 +196,14 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSYNCKHRPROC) (EGLDisplay dpy, EGLS
 typedef EGLint (EGLAPIENTRYP PFNEGLDUPNATIVEFENCEFDANDROIDPROC) (EGLDisplay dpy, EGLSyncKHR sync);
 #endif /* EGL_ANDROID_native_fence_sync */
 
+#ifndef EGL_SYNC_NATIVE_FENCE_ANDROID
+#define EGL_SYNC_NATIVE_FENCE_ANDROID 0x3144
+#endif
+
+#ifndef EGL_NO_NATIVE_FENCE_FD_ANDROID
+#define EGL_NO_NATIVE_FENCE_FD_ANDROID -1
+#endif
+
 #else /* ENABLE_EGL */
 
 /* EGL platform definition are keept to allow compositor-xx.c to build */
-- 
2.14.1



More information about the wayland-devel mailing list