[gtk: 5/7] gl backend: Avoid roundtripping via surface when updloading




commit 90fc671ef8ffcddaf7dac4a2a72c9094f7ef57d3
Author: Alexander Larsson <alexl redhat com>
Date:   Thu Sep 24 16:58:46 2020 +0200

    gl backend: Avoid roundtripping via surface when updloading
    
    Do custom uploads rather than using gdk_cairo_surface_upload_to_gl(),
    because this way we avoids a roundtrip (memcpy and possibly conversion)
    to the cairo image surface format.

 gdk/gdkmemorytexture.c        |   2 +-
 gdk/gdkmemorytextureprivate.h |   2 +
 gsk/gl/gskgldriver.c          | 107 ++++++++++++++++++++++++++++--------------
 gsk/gl/gskgldriverprivate.h   |   4 +-
 gsk/gl/gskglrenderer.c        |  13 +++--
 5 files changed, 86 insertions(+), 42 deletions(-)
---
diff --git a/gdk/gdkmemorytexture.c b/gdk/gdkmemorytexture.c
index 5ed0b988dc..663d0409f9 100644
--- a/gdk/gdkmemorytexture.c
+++ b/gdk/gdkmemorytexture.c
@@ -38,7 +38,7 @@ struct _GdkMemoryTextureClass
 
 G_DEFINE_TYPE (GdkMemoryTexture, gdk_memory_texture, GDK_TYPE_TEXTURE)
 
-static gsize
+gsize
 gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format)
 {
   switch (format)
diff --git a/gdk/gdkmemorytextureprivate.h b/gdk/gdkmemorytextureprivate.h
index caa4b2014c..740a59da6e 100644
--- a/gdk/gdkmemorytextureprivate.h
+++ b/gdk/gdkmemorytextureprivate.h
@@ -31,6 +31,8 @@ G_BEGIN_DECLS
 
 #define GDK_MEMORY_CAIRO_FORMAT_ARGB32 GDK_MEMORY_DEFAULT
 
+gsize                   gdk_memory_format_bytes_per_pixel   (GdkMemoryFormat    format);
+
 GdkMemoryFormat         gdk_memory_texture_get_format       (GdkMemoryTexture  *self);
 const guchar *          gdk_memory_texture_get_data         (GdkMemoryTexture  *self);
 gsize                   gdk_memory_texture_get_stride       (GdkMemoryTexture  *self);
diff --git a/gsk/gl/gskgldriver.c b/gsk/gl/gskgldriver.c
index 8d101b93df..a383344eae 100644
--- a/gsk/gl/gskgldriver.c
+++ b/gsk/gl/gskgldriver.c
@@ -7,6 +7,7 @@
 #include "gdk/gdkglcontextprivate.h"
 #include "gdk/gdktextureprivate.h"
 #include "gdk/gdkgltextureprivate.h"
+#include "gdkmemorytextureprivate.h"
 
 #include <gdk/gdk.h>
 #include <epoxy/gl.h>
@@ -58,6 +59,54 @@ struct _GskGLDriver
 
 G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT)
 
+static void
+upload_gdk_texture (GdkTexture      *source_texture,
+                    int              target,
+                    int              x_offset,
+                    int              y_offset,
+                    int              width,
+                    int              height)
+{
+  cairo_surface_t *surface = NULL;
+  GdkMemoryFormat data_format;
+  const guchar *data;
+  gsize data_stride;
+  gsize bpp;
+
+  g_return_if_fail (source_texture != NULL);
+  g_return_if_fail (x_offset + width <= gdk_texture_get_width (source_texture));
+  g_return_if_fail (y_offset + height <= gdk_texture_get_height (source_texture));
+
+  /* Note: GdkGLTextures are already handled before we reach this and reused as-is */
+
+  if (GDK_IS_MEMORY_TEXTURE (source_texture))
+    {
+      GdkMemoryTexture *memory_texture = GDK_MEMORY_TEXTURE (source_texture);
+      data = gdk_memory_texture_get_data (memory_texture);
+      data_format = gdk_memory_texture_get_format (memory_texture);
+      data_stride = gdk_memory_texture_get_stride (memory_texture);
+    }
+  else
+    {
+      /* Fall back to downloading to a surface */
+      surface = gdk_texture_download_surface (source_texture);
+      cairo_surface_flush (surface);
+      data = cairo_image_surface_get_data (surface);
+      data_format = GDK_MEMORY_DEFAULT;
+      data_stride = cairo_image_surface_get_stride (surface);
+    }
+
+  bpp = gdk_memory_format_bytes_per_pixel (data_format);
+
+  gdk_gl_context_upload_texture (gdk_gl_context_get_current (),
+                                 data + x_offset * bpp + y_offset * data_stride,
+                                 width, height, data_stride,
+                                 data_format, target);
+
+  if (surface)
+    cairo_surface_destroy (surface);
+}
+
 static Texture *
 texture_new (void)
 {
@@ -408,32 +457,15 @@ gsk_gl_driver_slice_texture (GskGLDriver   *self,
 
   slices = g_new0 (TextureSlice, cols * rows);
 
-  /* TODO: (Perf):
-   *   We still create a surface here, which should obviously be unnecessary
-   *   and we should eventually remove it and upload the data directly.
-   */
   for (col = 0; col < cols; col ++)
     {
       const int slice_width = MIN (max_texture_size, texture->width - x);
-      const int stride = slice_width * 4;
 
       for (row = 0; row < rows; row ++)
         {
           const int slice_height = MIN (max_texture_size, texture->height - y);
           const int slice_index = (col * rows) + row;
-          guchar *data;
           guint texture_id;
-          cairo_surface_t *surface;
-
-          data = g_malloc (sizeof (guchar) * stride * slice_height);
-
-          gdk_texture_download_area (texture,
-                                     &(GdkRectangle){x, y, slice_width, slice_height},
-                                     data, stride);
-          surface = cairo_image_surface_create_for_data (data,
-                                                         CAIRO_FORMAT_ARGB32,
-                                                         slice_width, slice_height,
-                                                         stride);
 
           glGenTextures (1, &texture_id);
 
@@ -442,7 +474,7 @@ gsk_gl_driver_slice_texture (GskGLDriver   *self,
 #endif
           glBindTexture (GL_TEXTURE_2D, texture_id);
           gsk_gl_driver_set_texture_parameters (self, GL_NEAREST, GL_NEAREST);
-          gdk_cairo_surface_upload_to_gl (surface, GL_TEXTURE_2D, slice_width, slice_height, NULL);
+          upload_gdk_texture (texture, GL_TEXTURE_2D, x, y, slice_width, slice_height);
 
 #ifdef G_ENABLE_DEBUG
           gsk_profiler_counter_inc (self->profiler, self->counters.surface_uploads);
@@ -451,9 +483,6 @@ gsk_gl_driver_slice_texture (GskGLDriver   *self,
           slices[slice_index].rect = (GdkRectangle){x, y, slice_width, slice_height};
           slices[slice_index].texture_id = texture_id;
 
-          g_free (data);
-          cairo_surface_destroy (surface);
-
           y += slice_height;
         }
 
@@ -486,7 +515,8 @@ gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
                                        int          mag_filter)
 {
   Texture *t;
-  cairo_surface_t *surface;
+  GdkTexture *downloaded_texture = NULL;
+  GdkTexture *source_texture;
 
   if (GDK_IS_GL_TEXTURE (texture))
     {
@@ -494,14 +524,20 @@ gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
 
       if (texture_context != self->gl_context)
         {
+          cairo_surface_t *surface;
+
           /* In this case, we have to temporarily make the texture's context the current one,
            * download its data into our context and then create a texture from it. */
           if (texture_context)
             gdk_gl_context_make_current (texture_context);
 
           surface = gdk_texture_download_surface (texture);
+          downloaded_texture = gdk_texture_new_for_surface (surface);
+          cairo_surface_destroy (surface);
 
           gdk_gl_context_make_current (self->gl_context);
+
+          source_texture = downloaded_texture;
         }
       else
         {
@@ -519,7 +555,7 @@ gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
             return t->texture_id;
         }
 
-      surface = gdk_texture_download_surface (texture);
+      source_texture = texture;
     }
 
   t = create_texture (self, gdk_texture_get_width (texture), gdk_texture_get_height (texture));
@@ -528,15 +564,16 @@ gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
     t->user = texture;
 
   gsk_gl_driver_bind_source_texture (self, t->texture_id);
-  gsk_gl_driver_init_texture_with_surface (self,
-                                           t->texture_id,
-                                           surface,
-                                           min_filter,
-                                           mag_filter);
+  gsk_gl_driver_init_texture (self,
+                              t->texture_id,
+                              source_texture,
+                              min_filter,
+                              mag_filter);
   gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, t->texture_id,
                                       "GdkTexture<%p> %d", texture, t->texture_id);
 
-  cairo_surface_destroy (surface);
+  if (downloaded_texture)
+    g_object_unref (downloaded_texture);
 
   return t->texture_id;
 }
@@ -769,11 +806,11 @@ filter_uses_mipmaps (int filter)
 }
 
 void
-gsk_gl_driver_init_texture_with_surface (GskGLDriver     *self,
-                                         int              texture_id,
-                                         cairo_surface_t *surface,
-                                         int              min_filter,
-                                         int              mag_filter)
+gsk_gl_driver_init_texture (GskGLDriver     *self,
+                            int              texture_id,
+                            GdkTexture      *texture,
+                            int              min_filter,
+                            int              mag_filter)
 {
   Texture *t;
 
@@ -794,7 +831,7 @@ gsk_gl_driver_init_texture_with_surface (GskGLDriver     *self,
 
   gsk_gl_driver_set_texture_parameters (self, min_filter, mag_filter);
 
-  gdk_cairo_surface_upload_to_gl (surface, GL_TEXTURE_2D, t->width, t->height, NULL);
+  upload_gdk_texture (texture, GL_TEXTURE_2D, 0, 0, t->width, t->height);
 
 #ifdef G_ENABLE_DEBUG
   gsk_profiler_counter_inc (self->profiler, self->counters.surface_uploads);
diff --git a/gsk/gl/gskgldriverprivate.h b/gsk/gl/gskgldriverprivate.h
index 406b959372..0bf9ca89d2 100644
--- a/gsk/gl/gskgldriverprivate.h
+++ b/gsk/gl/gskgldriverprivate.h
@@ -63,9 +63,9 @@ void            gsk_gl_driver_init_texture_empty        (GskGLDriver     *driver
                                                          int              texture_id,
                                                          int              min_filter,
                                                          int              max_filter);
-void            gsk_gl_driver_init_texture_with_surface (GskGLDriver     *driver,
+void            gsk_gl_driver_init_texture              (GskGLDriver     *driver,
                                                          int              texture_id,
-                                                         cairo_surface_t *surface,
+                                                         GdkTexture      *texture,
                                                          int              min_filter,
                                                          int              mag_filter);
 
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index 96aecbd70b..7998cbfd34 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -564,6 +564,7 @@ render_fallback_node (GskGLRenderer   *self,
                       GskRenderNode   *node,
                       RenderOpBuilder *builder)
 {
+  GdkTexture *texture;
   const float scale = ops_get_scale (builder);
   const int surface_width = ceilf (node->bounds.size.width * scale);
   const int surface_height = ceilf (node->bounds.size.height * scale);
@@ -645,15 +646,18 @@ render_fallback_node (GskGLRenderer   *self,
 #endif
   cairo_destroy (cr);
 
+
   /* Upload the Cairo surface to a GL texture */
   texture_id = gsk_gl_driver_create_texture (self->gl_driver,
                                              surface_width,
                                              surface_height);
   gsk_gl_driver_bind_source_texture (self->gl_driver, texture_id);
-  gsk_gl_driver_init_texture_with_surface (self->gl_driver,
-                                           texture_id,
-                                           surface,
-                                           GL_NEAREST, GL_NEAREST);
+
+  texture = gdk_texture_new_for_surface (surface);
+  gsk_gl_driver_init_texture (self->gl_driver,
+                              texture_id,
+                              texture,
+                              GL_NEAREST, GL_NEAREST);
 
   if (gdk_gl_context_has_debug (self->gl_context))
     gdk_gl_context_label_object_printf  (self->gl_context, GL_TEXTURE, texture_id,
@@ -661,6 +665,7 @@ render_fallback_node (GskGLRenderer   *self,
                                          g_type_name_from_instance ((GTypeInstance *) node),
                                          texture_id);
 
+  g_object_unref (texture);
   cairo_surface_destroy (surface);
   cairo_surface_destroy (rendered_surface);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]