gsk: Improve slice handling
authorMatthias Clasen <mclasen@redhat.com>
Wed, 22 Mar 2023 03:26:55 +0000 (23:26 -0400)
committerMatthias Clasen <mclasen@redhat.com>
Wed, 22 Mar 2023 04:15:32 +0000 (00:15 -0400)
Upload slices in chunks, instead of copying
enormous amounts of memory.

gsk/gl/gskgldriver.c

index 4b8aa14e45e1299f4530d3ea27f9ea2232d69615..f0b03ebcc80fd6a3d3b10c160bb974b599960104 100644 (file)
@@ -1197,6 +1197,10 @@ gsk_gl_driver_add_texture_slices (GskGLDriver        *self,
   int x = 0, y = 0;
   GdkMemoryTexture *memtex;
   int extra_pixels;
+  GdkMemoryTexture *memtex1 = NULL;
+  GdkMemoryTexture *memtex2 = NULL;
+  GdkMemoryTexture *memtex3 = NULL;
+  GdkMemoryTexture *memtex4 = NULL;
 
   g_assert (GSK_IS_GL_DRIVER (self));
   g_assert (GDK_IS_TEXTURE (texture));
@@ -1233,50 +1237,112 @@ gsk_gl_driver_add_texture_slices (GskGLDriver        *self,
 
   if (ensure_mipmap)
     {
-      guchar *data, *data2;
-      int w, h;
+      guchar *data1, *data2, *data3, *data4;
+      guchar *top_row, *bot_row, *left_row, *right_row;
+      GdkTexture *tmp;
+      int w;
       GBytes *bytes;
 
       /* We need some extra pixels around our tiles, in order for
        * GL to properly determine the right level of detail to use.
        * This number should probably depend on the scale, but for
        * now we just hardcode it.
+       *
+       * We create some auxiliary textures to hold the extra pixels:
+       *
+       *    +---------------------+
+       *    | memtex1             |
+       *    *---+-------------+---+
+       *    |   |             |   |
+       * memtex2|   memtex    |memtex3
+       *    |   |             |   |
+       *    *---+-------------+---+
+       *    | memtex4         |   |
+       *    +---------------------+
        */
+
       extra_pixels = 15;
 
-      /* FIXME a shame we have to copy the data here.
-       * It would be nicer if we could scatter-gather
-       * textures from smaller pieces.
-       */
+      top_row = g_malloc (4 * tex_width);
+      tmp = gdk_memory_texture_new_subtexture (memtex, 0, 0, tex_width, 1);
+      gdk_texture_download (tmp, top_row, 4 * tex_width);
+      g_object_unref (tmp);
+
+      bot_row = g_malloc (4 * tex_width);
+      tmp = gdk_memory_texture_new_subtexture (memtex, 0, tex_height - 1, tex_width, 1);
+      gdk_texture_download (tmp, bot_row, 4 * tex_width);
+      g_object_unref (tmp);
 
-      data = g_malloc (4 * tex_width * tex_height);
-      gdk_texture_download (GDK_TEXTURE (memtex), data, 4 * tex_width);
+      left_row = g_malloc (4 * tex_height);
+      tmp = gdk_memory_texture_new_subtexture (memtex, 0, 0, 1, tex_height);
+      gdk_texture_download (tmp, left_row, 4);
+      g_object_unref (tmp);
+
+      right_row = g_malloc (4 * tex_height);
+      tmp = gdk_memory_texture_new_subtexture (memtex, tex_width - 1, 0, 1, tex_height);
+      gdk_texture_download (tmp, right_row, 4);
+      g_object_unref (tmp);
 
       w = tex_width + 2 * extra_pixels;
-      h = tex_height + 2 * extra_pixels;
 
-      data2 = g_malloc (4 * w * h);
+      data1 = g_malloc (4 * w * extra_pixels);
+      data2 = g_malloc (4 * extra_pixels * tex_height);
+      data3 = g_malloc (4 * extra_pixels * tex_height);
+      data4 = g_malloc (4 * w * extra_pixels);
 
       for (int i = 0; i < w; i++)
         {
           int ii = CLAMP (i, extra_pixels, (tex_width - 1) + extra_pixels) - extra_pixels;
 
-          for (int j = 0; j < h; j++)
+          for (int j = 0; j < extra_pixels; j++)
             {
-              int jj = CLAMP (j, extra_pixels, (tex_height - 1) + extra_pixels) - extra_pixels;
+              data1[(j * w + i) * 4]     = top_row[ii * 4];
+              data1[(j * w + i) * 4 + 1] = top_row[ii * 4 + 1];
+              data1[(j * w + i) * 4 + 2] = top_row[ii * 4 + 2];
+              data1[(j * w + i) * 4 + 3] = top_row[ii * 4 + 3];
+
+              data4[(j * w + i) * 4]     = bot_row[ii * 4];
+              data4[(j * w + i) * 4 + 1] = bot_row[ii * 4 + 1];
+              data4[(j * w + i) * 4 + 2] = bot_row[ii * 4 + 2];
+              data4[(j * w + i) * 4 + 3] = bot_row[ii * 4 + 3];
+            }
+        }
 
-              data2[(j * w + i) * 4] = data[(jj * tex_width + ii) * 4];
-              data2[(j * w + i) * 4 + 1] = data[(jj * tex_width + ii) * 4 + 1];
-              data2[(j * w + i) * 4 + 2] = data[(jj * tex_width + ii) * 4 + 2];
-              data2[(j * w + i) * 4 + 3] = data[(jj * tex_width + ii) * 4 + 3];
+      for (int i = 0; i < extra_pixels; i++)
+        {
+          for (int j = 0; j < tex_height; j++)
+            {
+              data2[(j * extra_pixels + i) * 4]     = left_row[j * 4];
+              data2[(j * extra_pixels + i) * 4 + 1] = left_row[j * 4 + 1];
+              data2[(j * extra_pixels + i) * 4 + 2] = left_row[j * 4 + 2];
+              data2[(j * extra_pixels + i) * 4 + 3] = left_row[j * 4 + 3];
+
+              data3[(j * extra_pixels + i) * 4]     = right_row[j * 4];
+              data3[(j * extra_pixels + i) * 4 + 1] = right_row[j * 4 + 1];
+              data3[(j * extra_pixels + i) * 4 + 2] = right_row[j * 4 + 2];
+              data3[(j * extra_pixels + i) * 4 + 3] = right_row[j * 4 + 3];
             }
         }
 
-      g_free (data);
-      bytes = g_bytes_new_take (data2, 4 * w * h);
+      g_free (top_row);
+      g_free (bot_row);
+      g_free (left_row);
+      g_free (right_row);
+
+      bytes = g_bytes_new_take (data1, 4 * w * extra_pixels);
+      memtex1 = GDK_MEMORY_TEXTURE (gdk_memory_texture_new (w, extra_pixels, GDK_MEMORY_DEFAULT, bytes, 4 * w));
+      g_bytes_unref (bytes);
+
+      bytes = g_bytes_new_take (data2, 4 * extra_pixels * tex_height);
+      memtex2 = GDK_MEMORY_TEXTURE (gdk_memory_texture_new (extra_pixels, tex_height, GDK_MEMORY_DEFAULT, bytes, 4 * extra_pixels));
+      g_bytes_unref (bytes);
 
-      g_object_unref (memtex);
-      memtex = GDK_MEMORY_TEXTURE (gdk_memory_texture_new (w, h, GDK_MEMORY_DEFAULT, bytes, 4 * w));
+      bytes = g_bytes_new_take (data3, 4 * extra_pixels * tex_height);
+      memtex3 = GDK_MEMORY_TEXTURE (gdk_memory_texture_new (extra_pixels, tex_height, GDK_MEMORY_DEFAULT, bytes, 4 * extra_pixels));
+      g_bytes_unref (bytes);
+
+      bytes = g_bytes_new_take (data4, 4 * w * extra_pixels);
+      memtex4 = GDK_MEMORY_TEXTURE (gdk_memory_texture_new (w, extra_pixels, GDK_MEMORY_DEFAULT, bytes, 4 * w));
       g_bytes_unref (bytes);
     }
   else
@@ -1292,20 +1358,130 @@ gsk_gl_driver_add_texture_slices (GskGLDriver        *self,
         {
           int slice_height = row + 1 < rows ? tex_height / rows : tex_height - y;
           int slice_index = (col * rows) + row;
-          GdkTexture *subtex;
           guint texture_id;
 
-          subtex = gdk_memory_texture_new_subtexture (memtex,
-                                                      x, y,
-                                                      slice_width + 2 * extra_pixels,
-                                                      slice_height + 2 * extra_pixels);
-
-          texture_id = gsk_gl_command_queue_upload_texture (self->command_queue, subtex);
-          g_object_unref (subtex);
           if (ensure_mipmap)
             {
+              GskGLTextureChunk chunks[5];
+              unsigned int n_chunks = 0;
+
+              if (row == 0)
+                {
+                  chunks[n_chunks].texture = gdk_memory_texture_new_subtexture (memtex1,
+                                                                                x, 0,
+                                                                                slice_width + 2 * extra_pixels, extra_pixels);
+                  chunks[n_chunks].x = 0;
+                  chunks[n_chunks].y = 0;
+                  n_chunks++;
+                }
+
+              if (row == rows - 1)
+                {
+                  chunks[n_chunks].texture = gdk_memory_texture_new_subtexture (memtex4,
+                                                                                x, 0,
+                                                                                slice_width + 2 * extra_pixels, extra_pixels);
+                  chunks[n_chunks].x = 0;
+                  chunks[n_chunks].y = slice_height + extra_pixels;
+                  n_chunks++;
+                }
+
+              if (col == 0)
+                {
+                  int yy = y - extra_pixels;
+                  int hh = slice_height + 2 * extra_pixels;
+                  int y0 = 0;
+
+                  if (row == 0)
+                    {
+                      yy = 0;
+                      y0 = extra_pixels;
+                      hh -= extra_pixels;
+                    }
+                  if (row == rows - 1)
+                    {
+                      hh -= extra_pixels;
+                    }
+
+                  chunks[n_chunks].texture = gdk_memory_texture_new_subtexture (memtex2,
+                                                                                0, yy, extra_pixels, hh);
+                  chunks[n_chunks].x = 0;
+                  chunks[n_chunks].y = y0;
+                  n_chunks++;
+                }
+
+              if (col == cols - 1)
+                {
+                  int yy = y - extra_pixels;
+                  int hh = slice_height + 2 * extra_pixels;
+                  int y0 = 0;
+
+                  if (row == 0)
+                    {
+                      yy = 0;
+                      y0 = extra_pixels;
+                      hh -= extra_pixels;
+                    }
+                  if (row == rows - 1)
+                    {
+                      hh -= extra_pixels;
+                    }
+
+                  chunks[n_chunks].texture = gdk_memory_texture_new_subtexture (memtex3,
+                                                                                0, yy, extra_pixels, hh);
+                  chunks[n_chunks].x = slice_width + extra_pixels;
+                  chunks[n_chunks].y = y0;
+                  n_chunks++;
+                }
+
+              {
+                int xx = x - extra_pixels;
+                int yy = y - extra_pixels;
+                int ww = slice_width + 2 * extra_pixels;
+                int hh = slice_height + 2 * extra_pixels;
+                int x0 = 0;
+                int y0 = 0;
+                if (col == 0)
+                  {
+                    xx = 0;
+                    ww -= extra_pixels;
+                    x0 = extra_pixels;
+                  }
+                if (col == cols - 1)
+                  {
+                    ww -= extra_pixels;
+                  }
+                if (row == 0)
+                  {
+                    yy = 0;
+                    hh -= extra_pixels;
+                    y0 = extra_pixels;
+                  }
+                if (row == rows - 1)
+                  {
+                    hh -= extra_pixels;
+                  }
+
+                chunks[n_chunks].texture = gdk_memory_texture_new_subtexture (memtex, xx, yy, ww, hh);
+                chunks[n_chunks].x = x0;
+                chunks[n_chunks].y = y0;
+                n_chunks++;
+              }
+
+              texture_id = gsk_gl_command_queue_upload_texture_chunks (self->command_queue, n_chunks, chunks);
+
               glBindTexture (GL_TEXTURE_2D, texture_id);
               glGenerateMipmap (GL_TEXTURE_2D);
+
+              for (unsigned int i = 0; i < n_chunks; i++)
+                g_object_unref (chunks[i].texture);
+            }
+          else
+            {
+              GdkTexture *subtex;
+
+              subtex = gdk_memory_texture_new_subtexture (memtex, x, y, slice_width, slice_height);
+              texture_id = gsk_gl_command_queue_upload_texture (self->command_queue, subtex);
+              g_object_unref (subtex);
             }
 
           slices[slice_index].rect.x = x;
@@ -1325,6 +1501,10 @@ gsk_gl_driver_add_texture_slices (GskGLDriver        *self,
     }
 
   g_object_unref (memtex);
+  g_clear_object (&memtex1);
+  g_clear_object (&memtex2);
+  g_clear_object (&memtex3);
+  g_clear_object (&memtex4);
 
   /* Allocate one Texture for the entire thing. */
   t = gsk_gl_texture_new (0,