[PATCH 1/4] compositor: add an plane assignment hook

Jesse Barnes jbarnes at virtuousgeek.org
Mon Jan 16 14:40:46 PST 2012


Each output may have ways of optimizing surface drawing (e.g. by using
sprites), so push the handling of surface assignment to display planes
into the output structure, providing the existing surface setup function
as a helper.
---
 clients/window.h         |    3 -
 configure.ac             |    2 +-
 src/compositor-drm.c     |  202 ++++++++++++++++++++++++++++++++++++++++++++--
 src/compositor-openwfd.c |    1 +
 src/compositor-wayland.c |   10 +--
 src/compositor-x11.c     |   10 +--
 src/compositor.c         |   54 ++++++-------
 src/compositor.h         |    9 ++-
 8 files changed, 230 insertions(+), 61 deletions(-)

diff --git a/clients/window.h b/clients/window.h
index e9d717b..da286e3 100644
--- a/clients/window.h
+++ b/clients/window.h
@@ -175,9 +175,6 @@ typedef void (*window_drop_handler_t)(struct window *window,
 
 typedef void (*window_close_handler_t)(struct window *window, void *data);
 
-typedef void (*widget_resize_handler_t)(struct widget *widget,
-					int32_t width, int32_t height,
-					void *data);
 typedef void (*widget_redraw_handler_t)(struct widget *widget, void *data);
 
 typedef int (*widget_enter_handler_t)(struct widget *widget,
diff --git a/configure.ac b/configure.ac
index 2f2cee4..9aa95b3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -68,7 +68,7 @@ AC_ARG_ENABLE(drm-compositor, [  --enable-drm-compositor],,
 AM_CONDITIONAL(ENABLE_DRM_COMPOSITOR, test x$enable_drm_compositor == xyes)
 if test x$enable_drm_compositor == xyes; then
   AC_DEFINE([BUILD_DRM_COMPOSITOR], [1], [Build the DRM compositor])
-  PKG_CHECK_MODULES(DRM_COMPOSITOR, [libudev >= 136 libdrm >= 2.4.23 gbm])
+  PKG_CHECK_MODULES(DRM_COMPOSITOR, [libudev >= 136 libdrm >= 2.4.30 gbm])
 fi
 
 
diff --git a/src/compositor-drm.c b/src/compositor-drm.c
index c07eb2d..35a734e 100644
--- a/src/compositor-drm.c
+++ b/src/compositor-drm.c
@@ -23,6 +23,7 @@
 
 #define _GNU_SOURCE
 
+#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -31,6 +32,7 @@
 
 #include <xf86drm.h>
 #include <xf86drmMode.h>
+#include <drm_fourcc.h>
 
 #include <gbm.h>
 
@@ -51,9 +53,12 @@ struct drm_compositor {
 	} drm;
 	struct gbm_device *gbm;
 	uint32_t crtc_allocator;
+	uint32_t sprite_allocator;
 	uint32_t connector_allocator;
 	struct tty *tty;
 
+	struct wl_list sprite_list;
+
 	uint32_t prev_state;
 };
 
@@ -74,10 +79,33 @@ struct drm_output {
 	struct gbm_bo *bo[2];
 	uint32_t current;	
 
+	struct wl_list sprite_list;
+
 	uint32_t fs_surf_fb_id;
 	uint32_t pending_fs_surf_fb_id;
 };
 
+/*
+ * An output has a primary display plane plus zero or more sprites for
+ * blending display contents.
+ */
+struct drm_sprite {
+	struct wl_list link;
+	struct wl_list output_link;
+
+	uint32_t possible_crtcs;
+	uint32_t plane_id;
+	uint32_t fb_id;
+	uint32_t count_formats;
+
+	int32_t src_x, src_y;
+	uint32_t src_w, src_h;
+	uint32_t dest_x, dest_y;
+	uint32_t dest_w, dest_h;
+
+	uint32_t formats[];
+};
+
 static int
 drm_output_prepare_render(struct weston_output *output_base)
 {
@@ -188,6 +216,110 @@ drm_output_prepare_scanout_surface(struct weston_output *output_base,
 }
 
 static int
+drm_assign_overlay_surface(struct weston_output *output_base,
+			   struct weston_surface *es)
+{
+	struct weston_compositor *ec = output_base->compositor;
+	struct drm_compositor *c =(struct drm_compositor *) ec;
+	struct drm_output *output = (struct drm_output *) output_base;
+	struct drm_sprite *s;
+	int found = 0;
+	EGLint handle, stride;
+	struct gbm_bo *bo;
+	uint32_t fb_id = 0;
+	uint32_t handles[4], pitches[4], offsets[4];
+	int ret;
+
+	wl_list_for_each(s, &output->sprite_list, output_link) {
+		if (!s->fb_id) {
+			found = 1;
+			break;
+		}
+	}
+
+	/* No sprites available */
+	if (!found)
+		return -1;
+
+	bo = gbm_bo_create_from_egl_image(c->gbm, c->base.display, es->image,
+					  es->width, es->height,
+					  GBM_BO_USE_SCANOUT);
+	handle = gbm_bo_get_handle(bo).s32;
+	stride = gbm_bo_get_pitch(bo);
+
+	gbm_bo_destroy(bo);
+
+	if (!handle)
+		return -1;
+
+	handles[0] = handle;
+	pitches[0] = stride;
+	offsets[0] = 0;
+
+	ret = drmModeAddFB2(c->drm.fd, es->width, es->height,
+			    DRM_FORMAT_XRGB8888, handles, pitches, offsets,
+			    &fb_id, 0);
+	if (ret) {
+		fprintf(stderr, "addfb2 failed: %d\n", ret);
+		return -1;
+	}
+
+	fprintf(stderr, "displaying surface on sprite %d\n", s->plane_id);
+
+	fprintf(stderr, "%d, %d, %d, %d, (%d,%d), %dx%d\n", c->drm.fd,
+		s->plane_id, output->crtc_id, fb_id, es->x, es->y, es->width,
+		es->height);
+
+	ret = drmModeSetPlane(c->drm.fd, s->plane_id, output->crtc_id, fb_id, 0,
+			      es->x, es->y, es->width, es->height, 0, 0,
+			      es->width, es->height);
+	if (ret) {
+		fprintf(stderr, "setplane failed %d: %s\n", ret,
+			strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+drm_assign_planes(struct weston_output *output)
+{
+	struct weston_compositor *ec = output->compositor;
+	struct weston_surface *es;
+
+	es = container_of(ec->surface_list.next, struct weston_surface, link);
+
+	if (es->visual == WESTON_RGB_VISUAL) {
+		if (!drm_output_prepare_scanout_surface(output, es))
+			weston_queue_scanout_surface(output, es);
+	}
+
+	/*
+	 * Find a surface for each sprite in the output using some heuristics:
+	 * 1) size
+	 * 2) frequency of update
+	 * 3) opacity (though some hw might support alpha blending)
+	 * 4) clipping (this can be fixed with color keys)
+	 *
+	 * The idea is to save on blitting since this should save power.
+	 * If we can get a large video surface on the sprite for example,
+	 * the main display surface may not need to update at all, and
+	 * the client buffer can be used directly for the sprite surface
+	 * as we do for flipping full screen surfaces.
+	 */
+	wl_list_for_each_reverse(es, &ec->surface_list, link) {
+		if (es == scanout)
+			continue;
+
+		if (!drm_assign_overlay_surface(output, es))
+			pixman_region32_init(&es->damage);
+	}
+
+	return 0;
+}
+
+static int
 drm_output_set_cursor(struct weston_output *output_base,
 		      struct weston_input_device *eid)
 {
@@ -233,7 +365,7 @@ drm_output_set_cursor(struct weston_output *output_base,
 	gbm_bo_destroy(bo);
 
 	if (stride != 64 * 4) {
-		fprintf(stderr, "info: cursor stride is != 64\n");
+//		fprintf(stderr, "info: cursor stride is != 64\n");
 		goto out;
 	}
 
@@ -423,6 +555,52 @@ drm_subpixel_to_wayland(int drm_value)
 	}
 }
 
+static void
+create_sprites(struct drm_compositor *ec)
+{
+	struct drm_sprite *sprite;
+	drmModePlaneRes *plane_res;
+	drmModePlane *plane;
+	int i;
+
+	plane_res = drmModeGetPlaneResources(ec->drm.fd);
+	if (!plane_res) {
+		fprintf(stderr, "failed to get plane resources: %s\n",
+			strerror(errno));
+		return;
+	}
+
+	for (i = 0; i < plane_res->count_planes; i++) {
+		plane = drmModeGetPlane(ec->drm.fd, plane_res->planes[i]);
+		if (!plane)
+			continue;
+		sprite = malloc(sizeof(*sprite) + ((sizeof(uint32_t)) *
+						   plane->count_formats));
+		if (!sprite) {
+			fprintf(stderr, "%s: out of memory\n",
+				__func__);
+			free(plane);
+			continue;
+		}
+
+		memset(sprite, 0, sizeof *sprite);
+
+		sprite->possible_crtcs = plane->possible_crtcs;
+		sprite->plane_id = plane->plane_id;
+		memcpy(sprite->formats, plane->formats,
+		       plane->count_formats);
+		drmModeFreePlane(plane);
+
+		fprintf(stderr, "created sprite %d\n", plane->plane_id);
+
+		ec->sprite_allocator = (1 << sprite->plane_id);
+		wl_list_insert(&ec->sprite_list, &sprite->link);
+	}
+
+	free(plane_res->planes);
+	free(plane_res);
+}
+
 static int
 create_output_for_connector(struct drm_compositor *ec,
 			    drmModeRes *resources,
@@ -432,6 +610,7 @@ create_output_for_connector(struct drm_compositor *ec,
 	struct drm_output *output;
 	struct drm_mode *drm_mode, *next;
 	drmModeEncoder *encoder;
+	struct drm_sprite *sprite;
 	int i, ret;
 	unsigned handle, stride;
 
@@ -465,6 +644,7 @@ create_output_for_connector(struct drm_compositor *ec,
 	output->base.make = "unknown";
 	output->base.model = "unknown";
 	wl_list_init(&output->base.mode_list);
+	wl_list_init(&output->sprite_list);
 
 	output->crtc_id = resources->crtcs[i];
 	ec->crtc_allocator |= (1 << output->crtc_id);
@@ -474,6 +654,16 @@ create_output_for_connector(struct drm_compositor *ec,
 	output->original_crtc = drmModeGetCrtc(ec->drm.fd, output->crtc_id);
 	drmModeFreeEncoder(encoder);
 
+	wl_list_for_each(sprite, &ec->sprite_list, link) {
+		for (i = 0; i < resources->count_crtcs; i++)
+			if (sprite->possible_crtcs & (1 << i)) {
+				wl_list_insert(&output->sprite_list,
+					       &sprite->output_link);
+				fprintf(stderr, "found sprite %d\n",
+					sprite->plane_id);
+			}
+	}
+
 	for (i = 0; i < connector->count_modes; i++) {
 		ret = drm_output_add_mode(output, &connector->modes[i]);
 		if (ret)
@@ -548,10 +738,9 @@ create_output_for_connector(struct drm_compositor *ec,
 	wl_list_insert(ec->base.output_list.prev, &output->base.link);
 
 	output->pending_fs_surf_fb_id = 0;
+	output->base.assign_planes = drm_assign_planes;
 	output->base.prepare_render = drm_output_prepare_render;
 	output->base.present = drm_output_present;
-	output->base.prepare_scanout_surface =
-		drm_output_prepare_scanout_surface;
 	output->base.set_hardware_cursor = drm_output_set_cursor;
 	output->base.destroy = drm_output_destroy;
 
@@ -598,10 +787,8 @@ create_outputs(struct drm_compositor *ec, int option_connector)
 	int x = 0, y = 0;
 
 	resources = drmModeGetResources(ec->drm.fd);
-	if (!resources) {
-		fprintf(stderr, "drmModeGetResources failed\n");
+	if (!resources)
 		return -1;
-	}
 
 	for (i = 0; i < resources->count_connectors; i++) {
 		connector = drmModeGetConnector(ec->drm.fd,
@@ -899,6 +1086,9 @@ drm_compositor_create(struct wl_display *display,
 	if (weston_compositor_init(&ec->base, display) < 0)
 		return NULL;
 
+	wl_list_init(&ec->sprite_list);
+	create_sprites(ec);
+
 	if (create_outputs(ec, connector) < 0) {
 		fprintf(stderr, "failed to create output for %s\n", path);
 		return NULL;
diff --git a/src/compositor-openwfd.c b/src/compositor-openwfd.c
index 2299d99..2e5fe8f 100644
--- a/src/compositor-openwfd.c
+++ b/src/compositor-openwfd.c
@@ -406,6 +406,7 @@ create_output_for_port(struct wfd_compositor *ec,
 
 	wfdDeviceCommit(ec->dev, WFD_COMMIT_ENTIRE_DEVICE, WFD_INVALID_HANDLE);
 
+	output->base.repaint = weston_output_repaint;
 	output->base.prepare_render = wfd_output_prepare_render;
 	output->base.present = wfd_output_present;
 	output->base.prepare_scanout_surface =
diff --git a/src/compositor-wayland.c b/src/compositor-wayland.c
index 4fa9df1..a80670d 100644
--- a/src/compositor-wayland.c
+++ b/src/compositor-wayland.c
@@ -207,13 +207,6 @@ wayland_output_present(struct weston_output *output_base)
 }
 
 static int
-wayland_output_prepare_scanout_surface(struct weston_output *output_base,
-				       struct weston_surface *es)
-{
-	return -1;
-}
-
-static int
 wayland_output_set_cursor(struct weston_output *output_base,
 			  struct weston_input_device *input)
 {
@@ -290,10 +283,9 @@ wayland_compositor_create_output(struct wayland_compositor *c,
 
 	glClearColor(0, 0, 0, 0.5);
 
+	output->base.repaint = weston_output_repaint;
 	output->base.prepare_render = wayland_output_prepare_render;
 	output->base.present = wayland_output_present;
-	output->base.prepare_scanout_surface =
-		wayland_output_prepare_scanout_surface;
 	output->base.set_hardware_cursor = wayland_output_set_cursor;
 	output->base.destroy = wayland_output_destroy;
 
diff --git a/src/compositor-x11.c b/src/compositor-x11.c
index 09213f7..d4850c8 100644
--- a/src/compositor-x11.c
+++ b/src/compositor-x11.c
@@ -231,13 +231,6 @@ x11_output_present(struct weston_output *output_base)
 }
 
 static int
-x11_output_prepare_scanout_surface(struct weston_output *output_base,
-				   struct weston_surface *es)
-{
-	return -1;
-}
-
-static int
 x11_output_set_cursor(struct weston_output *output_base,
 		      struct weston_input_device *input)
 {
@@ -461,10 +454,9 @@ x11_compositor_create_output(struct x11_compositor *c, int x, int y,
 	output->finish_frame_timer =
 		wl_event_loop_add_timer(loop, finish_frame_handler, output);
 
+	output->base.repaint = weston_output_repaint;
 	output->base.prepare_render = x11_output_prepare_render;
 	output->base.present = x11_output_present;
-	output->base.prepare_scanout_surface =
-		x11_output_prepare_scanout_surface;
 	output->base.set_hardware_cursor = x11_output_set_cursor;
 	output->base.destroy = x11_output_destroy;
 
diff --git a/src/compositor.c b/src/compositor.c
index b6facd2..b95bb5c 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -746,13 +746,10 @@ out:
 	pixman_region32_fini(&cursor_region);
 }
 
-static int
-setup_scanout_surface(struct weston_output *output, struct weston_surface *es)
+WL_EXPORT int
+weston_queue_scanout_surface(struct weston_output *output,
+			     struct weston_surface *es)
 {
-	if (es->visual != WESTON_RGB_VISUAL ||
-	    output->prepare_scanout_surface(output, es) != 0)
-		return -1;
-
 	/* assert output->pending_scanout_buffer == NULL */
 	output->pending_scanout_buffer = es->buffer;
 	output->pending_scanout_buffer->busy_count++;
@@ -763,7 +760,7 @@ setup_scanout_surface(struct weston_output *output, struct weston_surface *es)
 	return 0;
 }
 
-static void
+WL_EXPORT void
 weston_output_repaint(struct weston_output *output)
 {
 	struct weston_compositor *ec = output->compositor;
@@ -802,30 +799,27 @@ weston_output_repaint(struct weston_output *output)
 
 	es = container_of(ec->surface_list.next, struct weston_surface, link);
 
-	if (setup_scanout_surface(output, es) == 0)
-		/* We're drawing nothing, just let the damage accumulate */
-		return;
-
-	if (es->fullscreen_output == output) {
-		if (es->width < output->current->width ||
-		    es->height < output->current->height)
-			glClear(GL_COLOR_BUFFER_BIT);
-		weston_surface_draw(es, output, &total_damage);
-	} else {
-		wl_list_for_each(es, &ec->surface_list, link) {
-			pixman_region32_copy(&es->damage, &total_damage);
-			pixman_region32_subtract(&total_damage, &total_damage, &es->opaque);
-		}
+	wl_list_for_each(es, &ec->surface_list, link) {
+		pixman_region32_copy(&es->damage, &total_damage);
+		pixman_region32_subtract(&total_damage, &total_damage,
+					 &es->opaque);
+	}
 
-		wl_list_for_each_reverse(es, &ec->surface_list, link) {
-			pixman_region32_init(&repaint);
-			pixman_region32_intersect(&repaint, &output->region,
-						  &es->damage);
-			weston_surface_draw(es, output, &repaint);
-			pixman_region32_subtract(&es->damage,
-						 &es->damage, &output->region);
-			pixman_region32_fini(&repaint);
-		}
+	if (output->assign_planes)
+		/* This will queue flips for the fbs and sprites where
+		 * applicable and clear the damage for those surfaces.
+		 * The repaint loop below will repaint everything
+		 * else. */
+		output->assign_planes(output);
+
+	wl_list_for_each_reverse(es, &ec->surface_list, link) {
+		pixman_region32_init(&repaint);
+		pixman_region32_intersect(&repaint, &output->region,
+					  &es->damage);
+		weston_surface_draw(es, output, &repaint);
+		pixman_region32_subtract(&es->damage,
+					 &es->damage, &output->region);
+		pixman_region32_fini(&repaint);
 	}
 
 	if (ec->fade.spring.current > 0.001)
diff --git a/src/compositor.h b/src/compositor.h
index d21e285..82fd1c1 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -87,10 +87,10 @@ struct weston_output {
 	struct wl_buffer *pending_scanout_buffer;
 	struct wl_listener pending_scanout_buffer_destroy_listener;
 
+	void (*repaint)(struct weston_output *output);
 	int (*prepare_render)(struct weston_output *output);
 	int (*present)(struct weston_output *output);
-	int (*prepare_scanout_surface)(struct weston_output *output,
-				       struct weston_surface *es);
+	int (*assign_planes)(struct weston_output *output);
 	int (*set_hardware_cursor)(struct weston_output *output,
 				   struct weston_input_device *input);
 	void (*destroy)(struct weston_output *output);
@@ -299,6 +299,8 @@ notify_touch(struct wl_input_device *device, uint32_t time, int touch_id,
 	     int x, int y, int touch_type);
 
 void
+weston_output_repaint(struct weston_output *output);
+void
 weston_output_finish_frame(struct weston_output *output, int msecs);
 void
 weston_output_damage(struct weston_output *output);
@@ -447,5 +449,6 @@ typedef	void (*weston_zoom_done_func_t)(struct weston_zoom *zoom, void *data);
 struct weston_zoom *
 weston_zoom_run(struct weston_surface *surface, GLfloat start, GLfloat stop,
 		weston_zoom_done_func_t done, void *data);
-
+int weston_queue_scanout_surface(struct weston_output *output,
+				 struct weston_surface *es);
 #endif
-- 
1.7.4.1



More information about the wayland-devel mailing list