[RFC Weston 08/10] window: implement per-surface redraws

Pekka Paalanen ppaalanen at gmail.com
Fri Feb 22 07:07:52 PST 2013


Add redraw_needed flag to all surfaces, in addition to having one in
window.

widget_schedule_redraw() now schedules the redraw only for the surface,
where the widget is on. window_schedule_redraw() is equivalent to
scheduling a redraw for all (sub-)surfaces of the window.

We still use only one deferred task for all redraws.

surface_redraw() will skip the redraw, if the window does not force a
redraw and the surface does not need a redraw. It will also skip the
redraw, if the frame callback from the previous redraw has not triggered
yet. When the frame callback later arrives, the redraw task will be
scheduled, if the surface still needs a redraw.

If the window forces a redraw, the redraw is executed even if there is a
pending frame callback. This is for resizing: resizing should trigger a
window repaint, as it really wants to update all surfaces in one go, to
apply possible sub-surface size and position changes. Resizing is the
only thing that makes a window force a redraw.

With this change, subsurfaces demo can avoid repainting the cairo
sub-surface while still animating the GL sub-surface.
---
 clients/window.c | 80 +++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 59 insertions(+), 21 deletions(-)

diff --git a/clients/window.c b/clients/window.c
index e538da9..4730c89 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -195,6 +195,8 @@ struct surface {
 	enum wl_subsurface_commit_mode default_mode;
 	struct toysurface *toysurface;
 	struct widget *widget;
+	int redraw_needed;
+	struct wl_callback *frame_cb;
 
 	struct rectangle allocation;
 	struct rectangle server_allocation;
@@ -220,8 +222,8 @@ struct window {
 	struct rectangle pending_allocation;
 	int x, y;
 	int resize_edges;
-	int redraw_scheduled;
 	int redraw_needed;
+	int redraw_task_scheduled;
 	struct task redraw_task;
 	int resize_needed;
 	int type;
@@ -240,7 +242,6 @@ struct window {
 
 	struct surface *main_surface;
 	struct wl_shell_surface *shell_surface;
-	struct wl_callback *frame_cb;
 
 	struct frame *frame;
 
@@ -1307,6 +1308,9 @@ static void frame_destroy(struct frame *frame);
 static void
 surface_destroy(struct surface *surface)
 {
+	if (surface->frame_cb)
+		wl_callback_destroy(surface->frame_cb);
+
 	if (surface->input_region)
 		wl_region_destroy(surface->input_region);
 
@@ -1333,8 +1337,7 @@ window_destroy(struct window *window)
 	struct window_output *window_output;
 	struct window_output *window_output_tmp;
 
-	if (window->redraw_scheduled)
-		wl_list_remove(&window->redraw_task.link);
+	wl_list_remove(&window->redraw_task.link);
 
 	wl_list_for_each(input, &display->input_list, link) {
 		if (input->pointer_focus == window)
@@ -1361,8 +1364,6 @@ window_destroy(struct window *window)
 
 	wl_list_remove(&window->link);
 
-	if (window->frame_cb)
-		wl_callback_destroy(window->frame_cb);
 	free(window->title);
 	free(window);
 }
@@ -1589,10 +1590,14 @@ widget_set_axis_handler(struct widget *widget,
 	widget->axis_handler = handler;
 }
 
+static void
+window_schedule_redraw_task(struct window *window);
+
 void
 widget_schedule_redraw(struct widget *widget)
 {
-	window_schedule_redraw(widget->window);
+	widget->surface->redraw_needed = 1;
+	window_schedule_redraw_task(widget->window);
 }
 
 cairo_surface_t *
@@ -3329,6 +3334,7 @@ idle_resize(struct window *window)
 	struct surface *surface;
 
 	window->resize_needed = 0;
+	window->redraw_needed = 1;
 
 	widget_set_allocation(window->main_surface->widget,
 			      window->pending_allocation.x,
@@ -3445,14 +3451,14 @@ widget_redraw(struct widget *widget)
 static void
 frame_callback(void *data, struct wl_callback *callback, uint32_t time)
 {
-	struct window *window = data;
+	struct surface *surface = data;
 
-	assert(callback == window->frame_cb);
+	assert(callback == surface->frame_cb);
 	wl_callback_destroy(callback);
-	window->frame_cb = 0;
-	window->redraw_scheduled = 0;
-	if (window->redraw_needed)
-		window_schedule_redraw(window);
+	surface->frame_cb = NULL;
+
+	if (surface->redraw_needed || surface->window->redraw_needed)
+		window_schedule_redraw_task(surface->window);
 }
 
 static const struct wl_callback_listener listener = {
@@ -3460,6 +3466,29 @@ static const struct wl_callback_listener listener = {
 };
 
 static void
+surface_redraw(struct surface *surface)
+{
+	if (!surface->window->redraw_needed && !surface->redraw_needed)
+		return;
+
+	/* Whole-window redraw forces a redraw even if the previous has
+	 * not yet hit the screen.
+	 */
+	if (surface->frame_cb) {
+		if (!surface->window->redraw_needed)
+			return;
+
+		wl_callback_destroy(surface->frame_cb);
+	}
+
+	surface->frame_cb = wl_surface_frame(surface->surface);
+	wl_callback_add_listener(surface->frame_cb, &listener, surface);
+
+	surface->redraw_needed = 0;
+	widget_redraw(surface->widget);
+}
+
+static void
 idle_redraw(struct task *task, uint32_t events)
 {
 	struct window *window = container_of(task, struct window, redraw_task);
@@ -3469,30 +3498,39 @@ idle_redraw(struct task *task, uint32_t events)
 		idle_resize(window);
 
 	wl_list_for_each(surface, &window->subsurface_list, link)
-		widget_redraw(surface->widget);
+		surface_redraw(surface);
 
 	window->redraw_needed = 0;
 	wl_list_init(&window->redraw_task.link);
+	window->redraw_task_scheduled = 0;
 
-	window->frame_cb = wl_surface_frame(window->main_surface->surface);
-	wl_callback_add_listener(window->frame_cb, &listener, window);
 	window_flush(window);
 
 	wl_list_for_each(surface, &window->subsurface_list, link)
 		surface_set_commit_mode_default(surface);
 }
 
-void
-window_schedule_redraw(struct window *window)
+static void
+window_schedule_redraw_task(struct window *window)
 {
-	window->redraw_needed = 1;
-	if (!window->redraw_scheduled) {
+	if (!window->redraw_task_scheduled) {
 		window->redraw_task.run = idle_redraw;
 		display_defer(window->display, &window->redraw_task);
-		window->redraw_scheduled = 1;
+		window->redraw_task_scheduled = 1;
 	}
 }
 
+void
+window_schedule_redraw(struct window *window)
+{
+	struct surface *surface;
+
+	wl_list_for_each(surface, &window->subsurface_list, link)
+		surface->redraw_needed = 1;
+
+	window_schedule_redraw_task(window);
+}
+
 int
 window_is_fullscreen(struct window *window)
 {
-- 
1.7.12.4



More information about the wayland-devel mailing list