gsk: Add more flexible texture upload
authorMatthias Clasen <mclasen@redhat.com>
Wed, 22 Mar 2023 02:55:03 +0000 (22:55 -0400)
committerMatthias Clasen <mclasen@redhat.com>
Wed, 22 Mar 2023 04:15:04 +0000 (00:15 -0400)
Add a command queue function to create a
GL texture by uploading several GdkTextures
at given positions.
This lets us 'piece together' textures.

gsk/gl/gskglcommandqueue.c
gsk/gl/gskglcommandqueueprivate.h

index c0cee895d4e9aa7746cbb6d9a7116ab2cf2e605e..436b8afcadc4225151e1c94168c94c61d2438ce3 100644 (file)
@@ -1412,9 +1412,41 @@ gsk_gl_command_queue_create_framebuffer (GskGLCommandQueue *self)
   return fbo_id;
 }
 
+static GdkMemoryFormat
+memory_format_gl_format (GdkMemoryFormat  data_format,
+                         gboolean         use_es,
+                         guint           *gl_internalformat,
+                         guint           *gl_format,
+                         guint           *gl_type)
+{
+  if (!gdk_memory_format_gl_format (data_format,
+                                    use_es,
+                                    gl_internalformat,
+                                    gl_format,
+                                    gl_type))
+    {
+      if (gdk_memory_format_prefers_high_depth (data_format))
+        data_format = GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED;
+      else
+        data_format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
+      if (!gdk_memory_format_gl_format (data_format,
+                                        use_es,
+                                        gl_internalformat,
+                                        gl_format,
+                                        gl_type))
+        {
+          g_assert_not_reached ();
+        }
+    }
+
+  return data_format;
+}
+
 static void
-gsk_gl_command_queue_do_upload_texture (GskGLCommandQueue *self,
-                                        GdkTexture        *texture)
+gsk_gl_command_queue_do_upload_texture_chunk (GskGLCommandQueue *self,
+                                              GdkTexture        *texture,
+                                              int                x,
+                                              int                y)
 {
   GdkGLContext *context;
   const guchar *data;
@@ -1435,25 +1467,11 @@ gsk_gl_command_queue_do_upload_texture (GskGLCommandQueue *self,
   width = gdk_texture_get_width (texture);
   height = gdk_texture_get_height (texture);
 
-  if (!gdk_memory_format_gl_format (data_format,
-                                    use_es,
-                                    &gl_internalformat,
-                                    &gl_format,
-                                    &gl_type))
-    {
-      if (gdk_memory_format_prefers_high_depth (data_format))
-        data_format = GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED;
-      else
-        data_format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
-      if (!gdk_memory_format_gl_format (data_format,
-                                        use_es,
-                                        &gl_internalformat,
-                                        &gl_format,
-                                        &gl_type))
-        {
-          g_assert_not_reached ();
-        }
-    }
+  data_format = memory_format_gl_format (data_format,
+                                         use_es,
+                                         &gl_internalformat,
+                                         &gl_format,
+                                         &gl_type);
 
   gdk_texture_downloader_init (&downloader, texture);
   gdk_texture_downloader_set_format (&downloader, data_format);
@@ -1469,43 +1487,53 @@ gsk_gl_command_queue_do_upload_texture (GskGLCommandQueue *self,
    */
   if (stride == width * bpp)
     {
-      glTexImage2D (GL_TEXTURE_2D, 0, gl_internalformat, width, height, 0, gl_format, gl_type, data);
+      glTexSubImage2D (GL_TEXTURE_2D, 0, x, y, width, height, gl_format, gl_type, data);
     }
   else if (stride % bpp == 0 &&
            (gdk_gl_context_check_version (context, 0, 0, 3, 0) || gdk_gl_context_has_unpack_subimage (context)))
     {
       glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / bpp);
 
-      glTexImage2D (GL_TEXTURE_2D, 0, gl_internalformat, width, height, 0, gl_format, gl_type, data);
+      glTexSubImage2D (GL_TEXTURE_2D, 0, x, y, width, height, gl_format, gl_type, data);
 
       glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
     }
   else
     {
-      int i;
-      glTexImage2D (GL_TEXTURE_2D, 0, gl_internalformat, width, height, 0, gl_format, gl_type, NULL);
-      for (i = 0; i < height; i++)
-        glTexSubImage2D (GL_TEXTURE_2D, 0, 0, i, width, 1, gl_format, gl_type, data + (i * stride));
+      for (int i = 0; i < height; i++)
+        glTexSubImage2D (GL_TEXTURE_2D, 0, x, y + i, width, 1, gl_format, gl_type, data + (i * stride));
     }
+
   glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
 
   g_bytes_unref (bytes);
 }
 
 int
-gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
-                                     GdkTexture        *texture)
+gsk_gl_command_queue_upload_texture_chunks (GskGLCommandQueue    *self,
+                                            unsigned int          n_chunks,
+                                            GskGLTextureChunk    *chunks)
 {
   G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
-  cairo_surface_t *surface = NULL;
   int width, height;
+  GdkMemoryFormat data_format;
+  GLenum gl_internalformat;
+  GLenum gl_format;
+  GLenum gl_type;
+  gboolean use_es;
   int texture_id;
 
   g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
-  g_assert (!GDK_IS_GL_TEXTURE (texture));
 
-  width = gdk_texture_get_width (texture);
-  height = gdk_texture_get_height (texture);
+  width = height = 0;
+  for (unsigned int i = 0; i < n_chunks; i++)
+    {
+      GskGLTextureChunk *c = &chunks[i];
+
+      width = MAX (width, c->x + gdk_texture_get_width (c->texture));
+      height = MAX (height, c->y + gdk_texture_get_height (c->texture));
+    }
+
   if (width > self->max_texture_size || height > self->max_texture_size)
     {
       g_warning ("Attempt to create texture of size %ux%u but max size is %d. "
@@ -1514,6 +1542,7 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
       width = MIN (width, self->max_texture_size);
       height = MIN (height, self->max_texture_size);
     }
+
   texture_id = gsk_gl_command_queue_create_texture (self, width, height, GL_RGBA8);
   if (texture_id == -1)
     return texture_id;
@@ -1524,15 +1553,28 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
   glActiveTexture (GL_TEXTURE0);
   glBindTexture (GL_TEXTURE_2D, texture_id);
 
-  gsk_gl_command_queue_do_upload_texture (self, texture);
+  /* Initialize the texture */
+  use_es = gdk_gl_context_get_use_es (gdk_gl_context_get_current ());
+  data_format = gdk_texture_get_format (chunks[0].texture);
+  memory_format_gl_format (data_format,
+                           use_es,
+                           &gl_internalformat,
+                           &gl_format,
+                           &gl_type);
+
+  glTexImage2D (GL_TEXTURE_2D, 0, gl_internalformat, width, height, 0, gl_format, gl_type, NULL);
+
+  for (unsigned int i = 0; i < n_chunks; i++)
+    {
+      GskGLTextureChunk *c = &chunks[i];
+      gsk_gl_command_queue_do_upload_texture_chunk (self, c->texture, c->x, c->y);
+    }
 
   /* Restore previous texture state if any */
   if (self->attachments->textures[0].id > 0)
     glBindTexture (self->attachments->textures[0].target,
                    self->attachments->textures[0].id);
 
-  g_clear_pointer (&surface, cairo_surface_destroy);
-
   if (gdk_profiler_is_running ())
     gdk_profiler_add_markf (start_time, GDK_PROFILER_CURRENT_TIME-start_time,
                             "Upload Texture",
@@ -1541,6 +1583,13 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
   return texture_id;
 }
 
+int
+gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
+                                     GdkTexture        *texture)
+{
+  return gsk_gl_command_queue_upload_texture_chunks (self, 1, &(GskGLTextureChunk){ texture, 0, 0});
+}
+
 void
 gsk_gl_command_queue_set_profiler (GskGLCommandQueue *self,
                                    GskProfiler       *profiler)
index 388745ea6f9149d27461c205ab4cf5aea2a6c6e5..cab15afbaa882b7d8be2c4f0e7d2335d69372d76 100644 (file)
@@ -298,6 +298,18 @@ int                 gsk_gl_command_queue_create_texture       (GskGLCommandQueue
                                                                int                   width,
                                                                int                   height,
                                                                int                   format);
+
+
+typedef struct {
+  GdkTexture *texture;
+  int x;
+  int y;
+} GskGLTextureChunk;
+
+int                 gsk_gl_command_queue_upload_texture_chunks(GskGLCommandQueue    *self,
+                                                               unsigned int          n_chunks,
+                                                               GskGLTextureChunk    *chunks);
+
 guint               gsk_gl_command_queue_create_framebuffer   (GskGLCommandQueue    *self);
 gboolean            gsk_gl_command_queue_create_render_target (GskGLCommandQueue    *self,
                                                                int                   width,