diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index 64ba0b6f69..fe244a7a4f 100644 --- a/gsk/gl/gskglrenderer.c +++ b/gsk/gl/gskglrenderer.c @@ -188,6 +188,65 @@ get_render_region (GdkSurface *surface, return cairo_region_create_rectangle (&extents); } +static gboolean +update_area_requires_clear (GdkSurface *surface, + const cairo_region_t *update_area) +{ + cairo_rectangle_int_t rect; + guint n_rects; + + g_assert (GDK_IS_SURFACE (surface)); + + /* No opaque region, assume we have to clear */ + if (surface->opaque_region == NULL) + return TRUE; + + /* If the update_area is the whole surface, then clear it + * because many drivers optimize for this by avoiding extra + * work to reload any contents. + */ + if (update_area == NULL) + return TRUE; + + if (cairo_region_num_rectangles (update_area) == 1) + { + cairo_region_get_rectangle (update_area, 0, &rect); + + if (rect.x == 0 && + rect.y == 0 && + rect.width == surface->width && + rect.height == surface->height) + return TRUE; + } + + /* If the entire surface is opaque, then we can skip clearing + * (with the exception of full surface clearing above). + */ + if (cairo_region_num_rectangles (surface->opaque_region) == 1) + { + cairo_region_get_rectangle (surface->opaque_region, 0, &rect); + + if (rect.x == 0 && + rect.y == 0 && + rect.width == surface->width && + rect.height == surface->height) + return FALSE; + } + + /* If any update_area rectangle overlaps our transparent + * regions, then we need to clear the area. + */ + n_rects = cairo_region_num_rectangles (update_area); + for (guint i = 0; i < n_rects; i++) + { + cairo_region_get_rectangle (update_area, i, &rect); + if (cairo_region_contains_rectangle (surface->opaque_region, &rect) != CAIRO_REGION_OVERLAP_IN) + return TRUE; + } + + return FALSE; +} + static void gsk_gl_renderer_render (GskRenderer *renderer, GskRenderNode *root, @@ -198,6 +257,7 @@ gsk_gl_renderer_render (GskRenderer *renderer, graphene_rect_t viewport; GskGLRenderJob *job; GdkSurface *surface; + gboolean clear_framebuffer; float scale_factor; g_assert (GSK_IS_GL_RENDERER (renderer)); @@ -219,9 +279,10 @@ gsk_gl_renderer_render (GskRenderer *renderer, /* Must be called *AFTER* gdk_draw_context_begin_frame() */ render_region = get_render_region (surface, self->context); + clear_framebuffer = update_area_requires_clear (surface, render_region); gsk_gl_driver_begin_frame (self->driver, self->command_queue); - job = gsk_gl_render_job_new (self->driver, &viewport, scale_factor, render_region, 0); + job = gsk_gl_render_job_new (self->driver, &viewport, scale_factor, render_region, 0, clear_framebuffer); #ifdef G_ENABLE_DEBUG if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), FALLBACK)) gsk_gl_render_job_set_debug_fallback (job, TRUE); @@ -268,7 +329,7 @@ gsk_gl_renderer_render_texture (GskRenderer *renderer, &render_target)) { gsk_gl_driver_begin_frame (self->driver, self->command_queue); - job = gsk_gl_render_job_new (self->driver, viewport, 1, NULL, render_target->framebuffer_id); + job = gsk_gl_render_job_new (self->driver, viewport, 1, NULL, render_target->framebuffer_id, TRUE); #ifdef G_ENABLE_DEBUG if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), FALLBACK)) gsk_gl_render_job_set_debug_fallback (job, TRUE); diff --git a/gsk/gl/gskglrenderjob.c b/gsk/gl/gskglrenderjob.c index 0efa8ae108..65b5adf84d 100644 --- a/gsk/gl/gskglrenderjob.c +++ b/gsk/gl/gskglrenderjob.c @@ -163,6 +163,11 @@ struct _GskGLRenderJob /* If we should be rendering red zones over fallback nodes */ guint debug_fallback : 1; + /* In some cases we might want to avoid clearing the framebuffer + * because we're going to render over the existing contents. + */ + guint clear_framebuffer : 1; + /* Format we want to use for intermediate textures, determined by * looking at the format of the framebuffer we are rendering on. */ @@ -4083,7 +4088,8 @@ gsk_gl_render_job_render (GskGLRenderJob *job, start_time = GDK_PROFILER_CURRENT_TIME; gdk_gl_context_push_debug_group (job->command_queue->context, "Building command queue"); gsk_gl_command_queue_bind_framebuffer (job->command_queue, job->framebuffer); - gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport); + if (job->clear_framebuffer) + gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport); gsk_gl_render_job_visit_node (job, root); gdk_gl_context_pop_debug_group (job->command_queue->context); gdk_profiler_add_mark (start_time, GDK_PROFILER_CURRENT_TIME-start_time, "Build GL command queue", ""); @@ -4145,7 +4151,8 @@ gsk_gl_render_job_new (GskGLDriver *driver, const graphene_rect_t *viewport, float scale_factor, const cairo_region_t *region, - guint framebuffer) + guint framebuffer, + gboolean clear_framebuffer) { const graphene_rect_t *clip_rect = viewport; graphene_rect_t transformed_extents; @@ -4161,6 +4168,7 @@ gsk_gl_render_job_new (GskGLDriver *driver, job->clip = g_array_sized_new (FALSE, FALSE, sizeof (GskGLRenderClip), 16); job->modelview = g_array_sized_new (FALSE, FALSE, sizeof (GskGLRenderModelview), 16); job->framebuffer = framebuffer; + job->clear_framebuffer = !!clear_framebuffer; job->offset_x = 0; job->offset_y = 0; job->scale_x = scale_factor; diff --git a/gsk/gl/gskglrenderjobprivate.h b/gsk/gl/gskglrenderjobprivate.h index 4e92b1b40c..3b92215d3a 100644 --- a/gsk/gl/gskglrenderjobprivate.h +++ b/gsk/gl/gskglrenderjobprivate.h @@ -27,7 +27,8 @@ GskGLRenderJob *gsk_gl_render_job_new (GskGLDriver *dri const graphene_rect_t *viewport, float scale_factor, const cairo_region_t *region, - guint framebuffer); + guint framebuffer, + gboolean clear_framebuffer); void gsk_gl_render_job_free (GskGLRenderJob *job); void gsk_gl_render_job_render (GskGLRenderJob *job, GskRenderNode *root);