[PATCH 1/2 v3] screenshooter: Grab pixel data directly before buffer swap

Scott Moreau oreaus at gmail.com
Fri Apr 20 02:31:12 PDT 2012


---
 clients/screenshot.c       |   17 ++++++++-
 protocol/screenshooter.xml |    2 +
 src/compositor-drm.c       |   18 +--------
 src/compositor-wayland.c   |   18 +--------
 src/compositor-x11.c       |   18 +--------
 src/compositor.c           |   15 ++++++++
 src/compositor.h           |   13 ++++++-
 src/screenshooter.c        |   86 +++++++++++++++++++++++++++++++------------
 8 files changed, 113 insertions(+), 74 deletions(-)

diff --git a/clients/screenshot.c b/clients/screenshot.c
index 086ddd9..da22423 100644
--- a/clients/screenshot.c
+++ b/clients/screenshot.c
@@ -41,6 +41,7 @@
 static struct wl_shm *shm;
 static struct screenshooter *screenshooter;
 static struct wl_list output_list;
+int buffer_copy_done;
 
 struct screenshooter_output {
 	struct wl_output *output;
@@ -94,6 +95,16 @@ static const struct wl_output_listener output_listener = {
 };
 
 static void
+screenshot_done(void *data, struct screenshooter *screenshooter)
+{
+	buffer_copy_done = 1;
+}
+
+static const struct screenshooter_listener screenshooter_listener = {
+	screenshot_done
+};
+
+static void
 handle_global(struct wl_display *display, uint32_t id,
 	      const char *interface, uint32_t version, void *data)
 {
@@ -188,6 +199,8 @@ int main(int argc, char *argv[])
 		return -1;
 	}
 
+	screenshooter_add_listener(screenshooter, &screenshooter_listener, screenshooter);
+
 	wl_list_for_each(output, &output_list, link) {
 		width = MAX(width, output->offset_x + output->width);
 		height = MAX(height, output->offset_y + output->height);
@@ -197,10 +210,12 @@ int main(int argc, char *argv[])
 
 	wl_list_for_each_safe(output, next, &output_list, link) {
 		screenshooter_shoot(screenshooter, output->output, buffer);
+		buffer_copy_done = 0;
+		while (!buffer_copy_done)
+			wl_display_roundtrip(display);
 		free(output);
 	}
 
-	wl_display_roundtrip(display);
 
 	write_png(width, height, data);
 
diff --git a/protocol/screenshooter.xml b/protocol/screenshooter.xml
index 1487c6d..76e3c85 100644
--- a/protocol/screenshooter.xml
+++ b/protocol/screenshooter.xml
@@ -5,6 +5,8 @@
       <arg name="output" type="object" interface="wl_output"/>
       <arg name="buffer" type="object" interface="wl_buffer"/>
     </request>
+    <event name="done">
+    </event>
   </interface>
 
 </protocol>
diff --git a/src/compositor-drm.c b/src/compositor-drm.c
index 837da8c..dcd5e99 100644
--- a/src/compositor-drm.c
+++ b/src/compositor-drm.c
@@ -215,6 +215,8 @@ drm_output_render(struct drm_output *output, pixman_region32_t *damage)
 	wl_list_for_each_reverse(surface, &compositor->base.surface_list, link)
 		weston_surface_draw(surface, &output->base, damage);
 
+	weston_output_do_read_pixels(&output->base);
+
 	eglSwapBuffers(compositor->base.display, output->egl_surface);
 	output->next_bo = gbm_surface_lock_front_buffer(output->surface);
 	if (!output->next_bo) {
@@ -1190,21 +1192,6 @@ drm_set_dpms(struct weston_output *output_base, enum dpms_enum level)
 	drmModeFreeConnector(connector);
 }
 
-static void
-drm_output_read_pixels(struct weston_output *output_base, void *data)
-{
-	struct drm_output *output = (struct drm_output *) output_base;
-	struct drm_compositor *compositor =
-		(struct drm_compositor *) output->base.compositor;
-
-	eglMakeCurrent(compositor->base.display, output->egl_surface,
-			output->egl_surface, compositor->base.context);
-
-	glReadPixels(0, 0, output_base->current->width,
-		     output_base->current->height,
-		     compositor->base.read_format, GL_UNSIGNED_BYTE, data);
-}
-
 static int
 create_output_for_connector(struct drm_compositor *ec,
 			    drmModeRes *resources,
@@ -1313,7 +1300,6 @@ create_output_for_connector(struct drm_compositor *ec,
 	output->base.repaint = drm_output_repaint;
 	output->base.destroy = drm_output_destroy;
 	output->base.assign_planes = drm_assign_planes;
-	output->base.read_pixels = drm_output_read_pixels;
 	output->base.set_dpms = drm_set_dpms;
 	output->base.switch_mode = drm_output_switch_mode;
 
diff --git a/src/compositor-wayland.c b/src/compositor-wayland.c
index afd9121..c01dd17 100644
--- a/src/compositor-wayland.c
+++ b/src/compositor-wayland.c
@@ -355,6 +355,8 @@ wayland_output_repaint(struct weston_output *output_base,
 	wl_list_for_each_reverse(surface, &compositor->base.surface_list, link)
 		weston_surface_draw(surface, &output->base, damage);
 
+	weston_output_do_read_pixels(&output->base);
+
 	draw_border(output);
 
 	eglSwapBuffers(compositor->base.display, output->egl_surface);
@@ -377,21 +379,6 @@ wayland_output_destroy(struct weston_output *output_base)
 	return;
 }
 
-static void
-wayland_output_read_pixels(struct weston_output *output_base, void *data)
-{
-	struct wayland_output *output = (struct wayland_output *) output_base;
-	struct wayland_compositor *compositor =
-		(struct wayland_compositor *) output->base.compositor;
-
-	eglMakeCurrent(compositor->base.display, output->egl_surface,
-			output->egl_surface, compositor->base.context);
-
-	glReadPixels(0, 0, output_base->current->width,
-		     output_base->current->height,
-		     compositor->base.read_format, GL_UNSIGNED_BYTE, data);
-}
-
 static int
 wayland_compositor_create_output(struct wayland_compositor *c,
 				 int width, int height)
@@ -460,7 +447,6 @@ wayland_compositor_create_output(struct wayland_compositor *c,
 	output->base.repaint = wayland_output_repaint;
 	output->base.destroy = wayland_output_destroy;
 	output->base.assign_planes = NULL;
-	output->base.read_pixels = wayland_output_read_pixels;
 	output->base.set_backlight = NULL;
 	output->base.set_dpms = NULL;
 	output->base.switch_mode = NULL;
diff --git a/src/compositor-x11.c b/src/compositor-x11.c
index b910831..6d9bb0e 100644
--- a/src/compositor-x11.c
+++ b/src/compositor-x11.c
@@ -212,6 +212,8 @@ x11_output_repaint(struct weston_output *output_base,
 	wl_list_for_each_reverse(surface, &compositor->base.surface_list, link)
 		weston_surface_draw(surface, &output->base, damage);
 
+	weston_output_do_read_pixels(&output->base);
+
 	eglSwapBuffers(compositor->base.display, output->egl_surface);
 
 	wl_event_source_timer_update(output->finish_frame_timer, 10);
@@ -344,21 +346,6 @@ x11_output_set_icon(struct x11_compositor *c,
 	pixman_image_unref(image);
 }
 
-static void
-x11_output_read_pixels(struct weston_output *output_base, void *data)
-{
-	struct x11_output *output = (struct x11_output *) output_base;
-	struct x11_compositor *compositor =
-		(struct x11_compositor *) output->base.compositor;
-
-	eglMakeCurrent(compositor->base.display, output->egl_surface,
-			output->egl_surface, compositor->base.context);
-
-	glReadPixels(0, 0, output_base->current->width,
-		     output_base->current->height,
-		     compositor->base.read_format, GL_UNSIGNED_BYTE, data);
-}
-
 static int
 x11_compositor_create_output(struct x11_compositor *c, int x, int y,
 			     int width, int height, int fullscreen)
@@ -470,7 +457,6 @@ x11_compositor_create_output(struct x11_compositor *c, int x, int y,
 	output->base.repaint = x11_output_repaint;
 	output->base.destroy = x11_output_destroy;
 	output->base.assign_planes = NULL;
-	output->base.read_pixels = x11_output_read_pixels;
 	output->base.set_backlight = NULL;
 	output->base.set_dpms = NULL;
 	output->base.switch_mode = NULL;
diff --git a/src/compositor.c b/src/compositor.c
index abf5ce9..ca6a803 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -2355,6 +2355,7 @@ weston_output_init(struct weston_output *output, struct weston_compositor *c,
 	output->mm_width = width;
 	output->mm_height = height;
 	output->dirty = 1;
+	wl_list_init(&output->read_pixels_list);
 
 	output->zoom.active = 0;
 	output->zoom.increment = 0.05;
@@ -2374,6 +2375,20 @@ weston_output_init(struct weston_output *output, struct weston_compositor *c,
 				      output, bind_output);
 }
 
+WL_EXPORT void
+weston_output_do_read_pixels(struct weston_output *output)
+{
+	struct weston_read_pixels *r, *next;
+
+	glPixelStorei(GL_PACK_ALIGNMENT, 1);
+	wl_list_for_each_safe(r, next, &output->read_pixels_list, link) {
+		glReadPixels(r->x, r->y, r->width, r->height,
+			     output->compositor->read_format,
+			     GL_UNSIGNED_BYTE, r->data);
+		r->done(r, output);
+	}
+}
+
 static void
 compositor_bind(struct wl_client *client,
 		void *data, uint32_t version, uint32_t id)
diff --git a/src/compositor.h b/src/compositor.h
index b19900b..61cafc1 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -44,6 +44,7 @@ struct weston_transform {
 
 struct weston_surface;
 struct weston_input_device;
+struct weston_output;
 
 struct weston_mode {
 	uint32_t flags;
@@ -72,6 +73,14 @@ enum dpms_enum {
 	WESTON_DPMS_OFF
 };
 
+struct weston_read_pixels {
+	void *data;
+	int x, y, width, height;
+	void (*done)(struct weston_read_pixels *read_pixels,
+		     struct weston_output *output);
+	struct wl_list link;
+};
+
 struct weston_output {
 	struct wl_list link;
 	struct wl_global *global;
@@ -87,6 +96,7 @@ struct weston_output {
 	int repaint_scheduled;
 	struct weston_output_zoom zoom;
 	int dirty;
+	struct wl_list read_pixels_list;
 
 	char *make, *model;
 	uint32_t subpixel;
@@ -99,7 +109,6 @@ struct weston_output {
 			pixman_region32_t *damage);
 	void (*destroy)(struct weston_output *output);
 	void (*assign_planes)(struct weston_output *output);
-	void (*read_pixels)(struct weston_output *output, void *data);
 	int (*switch_mode)(struct weston_output *output, struct weston_mode *mode);
 
 	/* backlight values are on 0-255 range, where higher is brighter */
@@ -413,6 +422,8 @@ weston_output_finish_frame(struct weston_output *output, int msecs);
 void
 weston_output_damage(struct weston_output *output);
 void
+weston_output_do_read_pixels(struct weston_output *output);
+void
 weston_compositor_repick(struct weston_compositor *compositor);
 void
 weston_compositor_schedule_repaint(struct weston_compositor *compositor);
diff --git a/src/screenshooter.c b/src/screenshooter.c
index b4b341b..87f1a9a 100644
--- a/src/screenshooter.c
+++ b/src/screenshooter.c
@@ -36,6 +36,12 @@ struct screenshooter {
 	struct wl_listener destroy_listener;
 };
 
+struct screenshooter_read_pixels {
+	struct weston_read_pixels base;
+	struct wl_buffer *buffer;
+	struct wl_resource *resource;
+};
+
 static void
 copy_bgra_yflip(uint8_t *dst, uint8_t *src, int height,
 		int dst_stride, int src_stride)
@@ -82,15 +88,52 @@ copy_rgba_yflip(uint8_t *dst, uint8_t *src, int height,
 }
 
 static void
+screenshooter_read_pixels_done(struct weston_read_pixels *base,
+			       struct weston_output *output)
+{
+	struct screenshooter_read_pixels *r =
+		(struct screenshooter_read_pixels *) base;
+	int32_t buffer_stride, output_stride;
+	uint8_t *d, *s;
+
+	buffer_stride = wl_shm_buffer_get_stride(r->buffer);
+	output_stride = output->current->width * 4;
+
+	d = wl_shm_buffer_get_data(r->buffer) +
+		output->y * buffer_stride + output->x * 4;
+	s = r->base.data + output_stride * (output->current->height - 1);
+
+	switch (output->compositor->read_format) {
+	case GL_BGRA_EXT:
+		copy_bgra_yflip(d, s, output->current->height,
+				buffer_stride, output_stride);
+		break;
+	case GL_RGBA:
+		copy_rgba_yflip(d, s, output->current->height,
+				buffer_stride, output_stride);
+		break;
+	default:
+		break;
+	}
+
+	wl_list_remove(&r->base.link);
+
+	screenshooter_send_done(r->resource);
+	free(r->base.data);
+	free(r);
+
+}
+
+static void
 screenshooter_shoot(struct wl_client *client,
 		    struct wl_resource *resource,
 		    struct wl_resource *output_resource,
 		    struct wl_resource *buffer_resource)
 {
 	struct weston_output *output = output_resource->data;
+	struct screenshooter_read_pixels *r;
 	struct wl_buffer *buffer = buffer_resource->data;
-	uint8_t *tmp, *d, *s;
-	int32_t buffer_stride, output_stride;
+	int32_t output_stride;
 
 	if (!wl_buffer_is_shm(buffer))
 		return;
@@ -99,35 +142,30 @@ screenshooter_shoot(struct wl_client *client,
 	    buffer->height < output->current->height)
 		return;
 
-	buffer_stride = wl_shm_buffer_get_stride(buffer);
-	output_stride = output->current->width * 4;
-	tmp = malloc(output_stride * output->current->height);
-	if (tmp == NULL) {
+	r = malloc(sizeof *r);
+	if (r == NULL) {
 		wl_resource_post_no_memory(resource);
 		return;
 	}
 
-	glPixelStorei(GL_PACK_ALIGNMENT, 1);
-	output->read_pixels(output, tmp);
-
-	d = wl_shm_buffer_get_data(buffer) + output->y * buffer_stride +
-							output->x * 4;
-	s = tmp + output_stride * (output->current->height - 1);
+	r->base.x = 0;
+	r->base.y = 0;
+	r->base.width = output->current->width;
+	r->base.height = output->current->height;
+	r->base.done = screenshooter_read_pixels_done;
+	r->buffer = buffer;
+	r->resource = resource;
+	output_stride = output->current->width * 4;
+	r->base.data = malloc(output_stride * output->current->height);
 
-	switch (output->compositor->read_format) {
-	case GL_BGRA_EXT:
-		copy_bgra_yflip(d, s, output->current->height,
-				buffer_stride, output_stride);
-		break;
-	case GL_RGBA:
-		copy_rgba_yflip(d, s, output->current->height,
-				buffer_stride, output_stride);
-		break;
-	default:
-		break;
+	if (r->base.data == NULL) {
+		free(r);
+		wl_resource_post_no_memory(resource);
+		return;
 	}
 
-	free(tmp);
+	wl_list_insert(output->read_pixels_list.prev, &r->base.link);
+	weston_compositor_schedule_repaint(output->compositor);
 }
 
 struct screenshooter_interface screenshooter_implementation = {
-- 
1.7.4.1



More information about the wayland-devel mailing list