diff --git a/src/compositor.c b/src/compositor.c
index 8bf55dc5e2a71e29391a398911febbc2e9950dea..9e1531049a4401cd5824c571a4869e7c35baf660 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -525,7 +525,8 @@ weston_surface_state_init(struct weston_surface_state *state)
 	state->sx = 0;
 	state->sy = 0;
 
-	pixman_region32_init(&state->damage);
+	pixman_region32_init(&state->damage_surface);
+	pixman_region32_init(&state->damage_buffer);
 	pixman_region32_init(&state->opaque);
 	region_init_infinite(&state->input);
 
@@ -552,7 +553,8 @@ weston_surface_state_fini(struct weston_surface_state *state)
 
 	pixman_region32_fini(&state->input);
 	pixman_region32_fini(&state->opaque);
-	pixman_region32_fini(&state->damage);
+	pixman_region32_fini(&state->damage_surface);
+	pixman_region32_fini(&state->damage_buffer);
 
 	if (state->buffer)
 		wl_list_remove(&state->buffer_destroy_listener.link);
@@ -2569,8 +2571,23 @@ surface_damage(struct wl_client *client,
 	if (width <= 0 || height <= 0)
 		return;
 
-	pixman_region32_union_rect(&surface->pending.damage,
-				   &surface->pending.damage,
+	pixman_region32_union_rect(&surface->pending.damage_surface,
+				   &surface->pending.damage_surface,
+				   x, y, width, height);
+}
+
+static void
+surface_damage_buffer(struct wl_client *client,
+		      struct wl_resource *resource,
+		      int32_t x, int32_t y, int32_t width, int32_t height)
+{
+	struct weston_surface *surface = wl_resource_get_user_data(resource);
+
+	if (width <= 0 || height <= 0)
+		return;
+
+	pixman_region32_union_rect(&surface->pending.damage_buffer,
+				   &surface->pending.damage_buffer,
 				   x, y, width, height);
 }
 
@@ -2733,6 +2750,40 @@ weston_surface_build_buffer_matrix(struct weston_surface *surface,
 	weston_matrix_scale(matrix, vp->buffer.scale, vp->buffer.scale, 1);
 }
 
+/* Translate pending damage in buffer co-ordinates to surface
+ * co-ordinates and union it with a pixman_region32_t.
+ * This should only be called after the buffer is attached.
+ */
+static void
+apply_damage_buffer(pixman_region32_t *dest,
+		    struct weston_surface *surface,
+		    struct weston_surface_state *state)
+{
+	struct weston_buffer *buffer = surface->buffer_ref.buffer;
+
+	/* wl_surface.damage_buffer needs to be clipped to the buffer,
+	 * translated into surface co-ordinates and unioned with
+	 * any other surface damage.
+	 * None of this makes sense if there is no buffer though.
+	 */
+	if (buffer && pixman_region32_not_empty(&state->damage_buffer)) {
+		pixman_region32_t buffer_damage;
+
+		pixman_region32_intersect_rect(&state->damage_buffer,
+					       &state->damage_buffer,
+					       0, 0, buffer->width,
+					       buffer->height);
+		pixman_region32_init(&buffer_damage);
+		weston_matrix_transform_region(&buffer_damage,
+					       &surface->buffer_to_surface_matrix,
+					       &state->damage_buffer);
+		pixman_region32_union(dest, dest, &buffer_damage);
+		pixman_region32_fini(&buffer_damage);
+	}
+	/* We should clear this on commit even if there was no buffer */
+	pixman_region32_clear(&state->damage_buffer);
+}
+
 static void
 weston_surface_commit_state(struct weston_surface *surface,
 			    struct weston_surface_state *state)
@@ -2766,15 +2817,20 @@ weston_surface_commit_state(struct weston_surface *surface,
 	state->newly_attached = 0;
 	state->buffer_viewport.changed = 0;
 
-	/* wl_surface.damage */
+	/* wl_surface.damage and wl_surface.damage_buffer */
 	if (weston_timeline_enabled_ &&
-	    pixman_region32_not_empty(&state->damage))
+	    (pixman_region32_not_empty(&state->damage_surface) ||
+	     pixman_region32_not_empty(&state->damage_buffer)))
 		TL_POINT("core_commit_damage", TLP_SURFACE(surface), TLP_END);
+
 	pixman_region32_union(&surface->damage, &surface->damage,
-			      &state->damage);
+			      &state->damage_surface);
+
+	apply_damage_buffer(&surface->damage, surface, state);
+
 	pixman_region32_intersect_rect(&surface->damage, &surface->damage,
 				       0, 0, surface->width, surface->height);
-	pixman_region32_clear(&state->damage);
+	pixman_region32_clear(&state->damage_surface);
 
 	/* wl_surface.set_opaque_region */
 	pixman_region32_init(&opaque);
@@ -2893,7 +2949,8 @@ static const struct wl_surface_interface surface_interface = {
 	surface_set_input_region,
 	surface_commit,
 	surface_set_buffer_transform,
-	surface_set_buffer_scale
+	surface_set_buffer_scale,
+	surface_damage_buffer
 };
 
 static void
@@ -3022,11 +3079,12 @@ weston_subsurface_commit_to_cache(struct weston_subsurface *sub)
 	 * translated to correspond to the new surface coordinate system
 	 * origin.
 	 */
-	pixman_region32_translate(&sub->cached.damage,
+	pixman_region32_translate(&sub->cached.damage_surface,
 				  -surface->pending.sx, -surface->pending.sy);
-	pixman_region32_union(&sub->cached.damage, &sub->cached.damage,
-			      &surface->pending.damage);
-	pixman_region32_clear(&surface->pending.damage);
+	pixman_region32_union(&sub->cached.damage_surface,
+			      &sub->cached.damage_surface,
+			      &surface->pending.damage_surface);
+	pixman_region32_clear(&surface->pending.damage_surface);
 
 	if (surface->pending.newly_attached) {
 		sub->cached.newly_attached = 1;
@@ -3040,6 +3098,8 @@ weston_subsurface_commit_to_cache(struct weston_subsurface *sub)
 	sub->cached.sx += surface->pending.sx;
 	sub->cached.sy += surface->pending.sy;
 
+	apply_damage_buffer(&sub->cached.damage_surface, surface, &surface->pending);
+
 	sub->cached.buffer_viewport.changed |=
 		surface->pending.buffer_viewport.changed;
 	sub->cached.buffer_viewport.buffer =
@@ -4536,7 +4596,7 @@ weston_compositor_create(struct wl_display *display, void *user_data)
 	ec->output_id_pool = 0;
 	ec->repaint_msec = DEFAULT_REPAINT_WINDOW;
 
-	if (!wl_global_create(ec->wl_display, &wl_compositor_interface, 3,
+	if (!wl_global_create(ec->wl_display, &wl_compositor_interface, 4,
 			      ec, compositor_bind))
 		goto fail;
 
diff --git a/src/compositor.h b/src/compositor.h
index 1d04481851f3fe72914859454e8021482fde6176..43db92ea8cef2b41f07906b18b2b285ed6f00614 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -931,7 +931,9 @@ struct weston_surface_state {
 	int32_t sy;
 
 	/* wl_surface.damage */
-	pixman_region32_t damage;
+	pixman_region32_t damage_surface;
+	/* wl_surface.damage_buffer */
+	pixman_region32_t damage_buffer;
 
 	/* wl_surface.set_opaque_region */
 	pixman_region32_t opaque;