gl-renderer: Use eglSwapBuffersWithDamageEXT when available
authorJason Ekstrand <jason@jlekstrand.net>
Wed, 5 Feb 2014 03:36:39 +0000 (21:36 -0600)
committerKristian Høgsberg <krh@bitplanet.net>
Thu, 6 Feb 2014 01:16:37 +0000 (17:16 -0800)
Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
src/gl-renderer.c

index c8dfa4b..d03bce6 100644 (file)
@@ -124,6 +124,10 @@ struct gl_renderer {
        PFNEGLCREATEIMAGEKHRPROC create_image;
        PFNEGLDESTROYIMAGEKHRPROC destroy_image;
 
+#ifdef EGL_EXT_swap_buffers_with_damage
+       PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage;
+#endif
+
        int has_unpack_subimage;
 
        PFNEGLBINDWAYLANDDISPLAYWL bind_display;
@@ -677,6 +681,17 @@ draw_output_border_texture(struct gl_output_state *go,
        glDisableVertexAttribArray(0);
 }
 
+static int
+output_has_borders(struct weston_output *output)
+{
+       struct gl_output_state *go = get_output_state(output);
+
+       return go->borders[GL_RENDERER_BORDER_TOP].data ||
+              go->borders[GL_RENDERER_BORDER_RIGHT].data ||
+              go->borders[GL_RENDERER_BORDER_BOTTOM].data ||
+              go->borders[GL_RENDERER_BORDER_LEFT].data;
+}
+
 static void
 draw_output_borders(struct weston_output *output,
                    enum gl_border_status border_status)
@@ -732,6 +747,43 @@ draw_output_borders(struct weston_output *output,
 }
 
 static void
+output_get_border_damage(struct weston_output *output,
+                        enum gl_border_status border_status,
+                        pixman_region32_t *damage)
+{
+       struct gl_output_state *go = get_output_state(output);
+       struct gl_border_image *top, *bottom, *left, *right;
+       int full_width, full_height;
+
+       if (border_status == BORDER_STATUS_CLEAN)
+               return; /* Clean. Nothing to do. */
+
+       top = &go->borders[GL_RENDERER_BORDER_TOP];
+       bottom = &go->borders[GL_RENDERER_BORDER_BOTTOM];
+       left = &go->borders[GL_RENDERER_BORDER_LEFT];
+       right = &go->borders[GL_RENDERER_BORDER_RIGHT];
+
+       full_width = output->current_mode->width + left->width + right->width;
+       full_height = output->current_mode->height + top->height + bottom->height;
+       if (border_status & BORDER_TOP_DIRTY)
+               pixman_region32_union_rect(damage, damage,
+                                          0, 0,
+                                          full_width, top->height);
+       if (border_status & BORDER_LEFT_DIRTY)
+               pixman_region32_union_rect(damage, damage,
+                                          0, top->height,
+                                          left->width, output->current_mode->height);
+       if (border_status & BORDER_RIGHT_DIRTY)
+               pixman_region32_union_rect(damage, damage,
+                                          full_width - right->width, top->height,
+                                          right->width, output->current_mode->height);
+       if (border_status & BORDER_BOTTOM_DIRTY)
+               pixman_region32_union_rect(damage, damage,
+                                          0, full_height - bottom->height,
+                                          full_width, bottom->height);
+}
+
+static void
 output_get_damage(struct weston_output *output,
                  pixman_region32_t *buffer_damage, uint32_t *border_damage)
 {
@@ -802,6 +854,11 @@ gl_renderer_repaint_output(struct weston_output *output,
        struct gl_renderer *gr = get_renderer(compositor);
        EGLBoolean ret;
        static int errored;
+#ifdef EGL_EXT_swap_buffers_with_damage
+       int i, nrects, buffer_height;
+       EGLint *egl_damage, *d;
+       pixman_box32_t *rects;
+#endif
        pixman_region32_t buffer_damage, total_damage;
        enum gl_border_status border_damage = BORDER_STATUS_CLEAN;
 
@@ -847,7 +904,48 @@ gl_renderer_repaint_output(struct weston_output *output,
        pixman_region32_copy(&output->previous_damage, output_damage);
        wl_signal_emit(&output->frame_signal, output);
 
+#ifdef EGL_EXT_swap_buffers_with_damage
+       if (gr->swap_buffers_with_damage) {
+               pixman_region32_init(&buffer_damage);
+               weston_transformed_region(output->width, output->height,
+                                         output->transform,
+                                         output->current_scale,
+                                         output_damage, &buffer_damage);
+
+               if (output_has_borders(output)) {
+                       pixman_region32_translate(&buffer_damage,
+                                                 go->borders[GL_RENDERER_BORDER_LEFT].width,
+                                                 go->borders[GL_RENDERER_BORDER_TOP].height);
+                       output_get_border_damage(output, go->border_status,
+                                                &buffer_damage);
+               }
+
+               rects = pixman_region32_rectangles(&buffer_damage, &nrects);
+               egl_damage = malloc(nrects * 4 * sizeof(EGLint));
+
+               buffer_height = go->borders[GL_RENDERER_BORDER_TOP].height +
+                               output->current_mode->height +
+                               go->borders[GL_RENDERER_BORDER_BOTTOM].height;
+
+               d = egl_damage;
+               for (i = 0; i < nrects; ++i) {
+                       *d++ = rects[i].x1;
+                       *d++ = buffer_height - rects[i].y2;
+                       *d++ = rects[i].x2 - rects[i].x1;
+                       *d++ = rects[i].y2 - rects[i].y1;
+               }
+               ret = gr->swap_buffers_with_damage(gr->egl_display,
+                                                  go->egl_surface,
+                                                  egl_damage, nrects);
+               free(egl_damage);
+               pixman_region32_fini(&buffer_damage);
+       } else {
+               ret = eglSwapBuffers(gr->egl_display, go->egl_surface);
+       }
+#else /* ! defined EGL_EXT_swap_buffers_with_damage */
        ret = eglSwapBuffers(gr->egl_display, go->egl_surface);
+#endif
+
        if (ret == EGL_FALSE && !errored) {
                errored = 1;
                weston_log("Failed in eglSwapBuffers.\n");
@@ -1958,6 +2056,15 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface)
                weston_log("warning: EGL_EXT_buffer_age not supported. "
                           "Performance could be affected.\n");
 
+#ifdef EGL_EXT_swap_buffers_with_damage
+       if (strstr(extensions, "EGL_EXT_swap_buffers_with_damage"))
+               gr->swap_buffers_with_damage =
+                       (void *) eglGetProcAddress("eglSwapBuffersWithDamageEXT");
+       else
+               weston_log("warning: EGL_EXT_swap_buffers_with_damage not "
+                          "supported. Performance could be affected.\n");
+#endif
+
        glActiveTexture(GL_TEXTURE0);
 
        if (compile_shaders(ec))