[gtk/matthiasc/color-profile-rebased: 40/52] gsk: Do texture conversions in shaders




commit f4db51060c3aa8730addc7766e5518a067f1a117
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun May 8 14:42:32 2022 -0400

    gsk: Do texture conversions in shaders
    
    Do linearization and premultiplication of textures
    in a shader after uploading the unconverted data.
    This moves move work from the cpu to the gpu.
    
    Conversions between other color spaces are still
    done with lcms on the cpu.

 gsk/gl/gskglcommandqueue.c                  |  67 +++++++++++++----
 gsk/gl/gskglcommandqueueprivate.h           |   9 ++-
 gsk/gl/gskgldriver.c                        | 110 ++++++++++++++++++++++------
 gsk/gl/gskglprograms.defs                   |   8 ++
 gsk/gl/resources/linearize_premultiply.glsl |  21 ++++++
 gsk/gl/resources/preamble.glsl              |   5 +-
 gsk/gl/resources/premultiply.glsl           |  20 +++++
 gsk/meson.build                             |   2 +
 8 files changed, 201 insertions(+), 41 deletions(-)
---
diff --git a/gsk/gl/gskglcommandqueue.c b/gsk/gl/gskglcommandqueue.c
index 9f1710b8f4..736063891d 100644
--- a/gsk/gl/gskglcommandqueue.c
+++ b/gsk/gl/gskglcommandqueue.c
@@ -530,7 +530,7 @@ gsk_gl_command_queue_begin_draw (GskGLCommandQueue   *self,
   GskGLCommandBatch *batch;
 
   g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
-  g_assert (self->in_draw == FALSE);
+  g_assert (!self->in_draw);
   g_assert (width <= G_MAXUINT16);
   g_assert (height <= G_MAXUINT16);
 
@@ -568,6 +568,7 @@ gsk_gl_command_queue_end_draw (GskGLCommandQueue *self)
   GskGLCommandBatch *batch;
 
   g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_assert (self->in_draw);
   g_assert (self->batches.len > 0);
 
   if (will_ignore_batch (self))
@@ -1352,23 +1353,35 @@ gsk_gl_command_queue_create_framebuffer (GskGLCommandQueue *self)
 
 static void
 gsk_gl_command_queue_do_upload_texture (GskGLCommandQueue *self,
-                                        GdkTexture        *texture)
+                                        GdkTexture        *texture,
+                                        GskConversion     *conversion)
 {
   GdkGLContext *context;
   const guchar *data;
   gsize stride;
   GdkMemoryTexture *memtex;
   GdkMemoryFormat data_format;
+  GdkColorSpace *data_space;
   int width, height;
   GLenum gl_internalformat;
   GLenum gl_format;
   GLenum gl_type;
   gsize bpp;
   gboolean use_es;
+  gboolean convert_locally = FALSE;
 
   context = gdk_gl_context_get_current ();
   use_es = gdk_gl_context_get_use_es (context);
   data_format = gdk_texture_get_format (texture);
+  data_space = gdk_texture_get_color_space (texture);
+
+  if (data_space == gdk_color_space_get_srgb ())
+    *conversion = GSK_CONVERSION_LINEARIZE;
+  else if (data_space == gdk_color_space_get_srgb_linear ())
+    *conversion = 0;
+  else /* FIXME: do colorspace conversion in a shader */
+    convert_locally = TRUE;
+
   width = gdk_texture_get_width (texture);
   height = gdk_texture_get_height (texture);
 
@@ -1378,21 +1391,38 @@ gsk_gl_command_queue_do_upload_texture (GskGLCommandQueue *self,
                                     &gl_format,
                                     &gl_type))
     {
-      if (gdk_memory_format_prefers_high_depth (data_format))
-        data_format = GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED;
+      if (gdk_gl_context_get_api (context) == GDK_GL_API_GL)
+        {
+          *conversion |= GSK_CONVERSION_PREMULTIPLY;
+        }
       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 ();
+          convert_locally = TRUE;
+          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 ();
+            }
         }
     }
 
-  memtex = gdk_memory_texture_from_texture (texture, data_format, gdk_color_space_get_srgb ());
+  if (convert_locally)
+    {
+      memtex = gdk_memory_texture_from_texture (texture, data_format, gdk_color_space_get_srgb_linear ());
+      *conversion = 0;
+    }
+  else
+    {
+      memtex = gdk_memory_texture_from_texture (texture, gdk_texture_get_format (texture), 
gdk_texture_get_color_space (texture));
+    }
+
   data = gdk_memory_texture_get_data (memtex);
   stride = gdk_memory_texture_get_stride (memtex);
   bpp = gdk_memory_format_bytes_per_pixel (data_format);
@@ -1431,11 +1461,13 @@ int
 gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
                                      GdkTexture        *texture,
                                      int                min_filter,
-                                     int                mag_filter)
+                                     int                mag_filter,
+                                     GskConversion     *conversion)
 {
   G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
   cairo_surface_t *surface = NULL;
   int width, height;
+  int format;
   int texture_id;
 
   g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
@@ -1445,6 +1477,9 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
 
   width = gdk_texture_get_width (texture);
   height = gdk_texture_get_height (texture);
+
+  *conversion = 0;
+
   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. "
@@ -1453,7 +1488,9 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
       width = MAX (width, self->max_texture_size);
       height = MAX (height, self->max_texture_size);
     }
-  texture_id = gsk_gl_command_queue_create_texture (self, width, height, GL_RGBA8, min_filter, mag_filter);
+
+  format = gdk_memory_format_prefers_high_depth (gdk_texture_get_format (texture)) ? GL_RGBA16F : GL_RGBA8;
+  texture_id = gsk_gl_command_queue_create_texture (self, width, height, format, min_filter, mag_filter);
   if (texture_id == -1)
     return texture_id;
 
@@ -1463,7 +1500,7 @@ 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);
+  gsk_gl_command_queue_do_upload_texture (self, texture, conversion);
 
   /* Restore previous texture state if any */
   if (self->attachments->textures[0].id > 0)
diff --git a/gsk/gl/gskglcommandqueueprivate.h b/gsk/gl/gskglcommandqueueprivate.h
index 48911305a8..a6ffc9f96d 100644
--- a/gsk/gl/gskglcommandqueueprivate.h
+++ b/gsk/gl/gskglcommandqueueprivate.h
@@ -280,10 +280,17 @@ void                gsk_gl_command_queue_execute              (GskGLCommandQueue
                                                                guint                 scale_factor,
                                                                const cairo_region_t *scissor,
                                                                guint                 default_framebuffer);
+
+typedef enum {
+  GSK_CONVERSION_LINEARIZE   = 1 << 0,
+  GSK_CONVERSION_PREMULTIPLY = 1 << 1,
+} GskConversion;
+
 int                 gsk_gl_command_queue_upload_texture       (GskGLCommandQueue    *self,
                                                                GdkTexture           *texture,
                                                                int                   min_filter,
-                                                               int                   mag_filter);
+                                                               int                   mag_filter,
+                                                               GskConversion        *remaining);
 int                 gsk_gl_command_queue_create_texture       (GskGLCommandQueue    *self,
                                                                int                   width,
                                                                int                   height,
diff --git a/gsk/gl/gskgldriver.c b/gsk/gl/gskgldriver.c
index 81e69062c0..d631f81f4c 100644
--- a/gsk/gl/gskgldriver.c
+++ b/gsk/gl/gskgldriver.c
@@ -713,9 +713,10 @@ draw_offscreen (GskGLCommandQueue *command_queue,
 }
 
 static void
-set_viewport_for_size (GskGLProgram *program,
-                         float         width,
-                         float         height)
+set_viewport_for_size (GskGLDriver  *self,
+                       GskGLProgram *program,
+                       float         width,
+                       float         height)
 {
   float viewport[4] = { 0, 0, width, height };
 
@@ -724,15 +725,17 @@ set_viewport_for_size (GskGLProgram *program,
                                UNIFORM_SHARED_VIEWPORT, 0,
                                1,
                                (const float *)&viewport);
+  self->stamps[UNIFORM_SHARED_VIEWPORT]++;
 }
 
 #define ORTHO_NEAR_PLANE   -10000
 #define ORTHO_FAR_PLANE     10000
 
 static void
-set_projection_for_size (GskGLProgram *program,
-                       float            width,
-                       float            height)
+set_projection_for_size (GskGLDriver  *self,
+                         GskGLProgram *program,
+                         float         width,
+                         float         height)
 {
   graphene_matrix_t projection;
 
@@ -743,10 +746,12 @@ set_projection_for_size (GskGLProgram *program,
                                    program->program_info,
                                    UNIFORM_SHARED_PROJECTION, 0,
                                    &projection);
+  self->stamps[UNIFORM_SHARED_PROJECTION]++;
 }
 
 static void
-reset_modelview (GskGLProgram *program)
+reset_modelview (GskGLDriver  *self,
+                 GskGLProgram *program)
 {
   graphene_matrix_t modelview;
 
@@ -756,25 +761,38 @@ reset_modelview (GskGLProgram *program)
                                    program->program_info,
                                    UNIFORM_SHARED_MODELVIEW, 0,
                                    &modelview);
+  self->stamps[UNIFORM_SHARED_MODELVIEW]++;
 }
 
 static GskGLTexture *
-gsk_gl_driver_convert_texture (GskGLDriver *self,
-                               int          texture_id,
-                               int          width,
-                               int          height,
-                               int          format)
+gsk_gl_driver_convert_texture (GskGLDriver   *self,
+                               int            texture_id,
+                               int            width,
+                               int            height,
+                               int            format,
+                               int            min_filter,
+                               int            max_filter,
+                               GskConversion  conversion)
 {
   GskGLRenderTarget *target;
   int prev_fbo;
-  GskGLProgram *program = self->linearize_no_clip;
+  GskGLProgram *program;
+
+  if (conversion == (GSK_CONVERSION_LINEARIZE | GSK_CONVERSION_PREMULTIPLY))
+    program = self->linearize_premultiply_no_clip;
+  else if (conversion & GSK_CONVERSION_LINEARIZE)
+    program = self->linearize_no_clip;
+  else if (conversion & GSK_CONVERSION_PREMULTIPLY)
+    program = self->premultiply_no_clip;
+  else
+    g_assert_not_reached ();
 
   gdk_gl_context_make_current (self->command_queue->context);
 
   gsk_gl_driver_create_render_target (self,
                                       width, height,
                                       format,
-                                      GL_LINEAR, GL_LINEAR,
+                                      min_filter, max_filter,
                                       &target);
 
   prev_fbo = gsk_gl_command_queue_bind_framebuffer (self->command_queue, target->framebuffer_id);
@@ -784,9 +802,9 @@ gsk_gl_driver_convert_texture (GskGLDriver *self,
                                    program->program_info,
                                    width, height);
 
-  set_projection_for_size (program, width, height);
-  set_viewport_for_size (program, width, height);
-  reset_modelview (program);
+  set_projection_for_size (self, program, width, height);
+  set_viewport_for_size (self, program, width, height);
+  reset_modelview (self, program);
 
   gsk_gl_program_set_uniform_texture (program,
                                       UNIFORM_SHARED_SOURCE, 0,
@@ -839,6 +857,7 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
   int height;
   int width;
   int format;
+  GskConversion conversion;
 
   g_return_val_if_fail (GSK_IS_GL_DRIVER (self), 0);
   g_return_val_if_fail (GDK_IS_TEXTURE (texture), 0);
@@ -879,13 +898,18 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
                * is nonlinear sRGB. Eventually, we should figure out how
                * to convert from other color spaces to linear sRGB
                */
-
-              t = gsk_gl_driver_convert_texture (self, gl_texture_id, width, height, format);
-              if (gdk_texture_set_render_data (texture, self, t, (GDestroyNotify)gsk_gl_texture_destroyed))
+              t = gsk_gl_driver_convert_texture (self,
+                                                 gl_texture_id,
+                                                 width, height,
+                                                 format,
+                                                 min_filter, mag_filter,
+                                                 GSK_CONVERSION_LINEARIZE);
+              if (gdk_texture_set_render_data (texture, self, t, gsk_gl_texture_destroyed))
                 t->user = texture;
 
               return t->texture_id;
             }
+          /* FIXME: do colorspace conversion in a shader */
         }
     }
 
@@ -900,8 +924,8 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
 
   texture_id = gsk_gl_command_queue_upload_texture (self->command_queue,
                                                     GDK_TEXTURE (downloaded_texture),
-                                                    min_filter,
-                                                    mag_filter);
+                                                    min_filter, mag_filter,
+                                                    &conversion);
 
   t = gsk_gl_texture_new (texture_id,
                            width, height, format, min_filter, mag_filter,
@@ -909,6 +933,15 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
 
   g_hash_table_insert (self->textures, GUINT_TO_POINTER (texture_id), t);
 
+  g_clear_object (&downloaded_texture);
+
+  if (conversion)
+    t = gsk_gl_driver_convert_texture (self,
+                                       texture_id,
+                                       width, height, format,
+                                       min_filter, mag_filter,
+                                       conversion);
+
   if (gdk_texture_set_render_data (texture, self, t, gsk_gl_texture_destroyed))
     t->user = texture;
 
@@ -967,6 +1000,8 @@ gsk_gl_driver_create_texture (GskGLDriver *self,
                        GUINT_TO_POINTER (texture->texture_id),
                        texture);
 
+  texture->last_used_in_frame = self->current_frame_id;
+
   return texture;
 }
 
@@ -1334,6 +1369,7 @@ gsk_gl_driver_add_texture_slices (GskGLDriver        *self,
   GskGLTextureSlice *slices;
   GskGLTexture *t;
   guint n_slices;
+  int format;
   guint cols;
   guint rows;
   int tex_width;
@@ -1351,6 +1387,9 @@ gsk_gl_driver_add_texture_slices (GskGLDriver        *self,
 
   tex_width = texture->width;
   tex_height = texture->height;
+
+  format = gdk_memory_format_prefers_high_depth (gdk_texture_get_format (texture)) ? GL_RGBA16F : GL_RGBA8;
+
   cols = (texture->width / max_texture_size) + 1;
   rows = (texture->height / max_texture_size) + 1;
 
@@ -1377,13 +1416,38 @@ gsk_gl_driver_add_texture_slices (GskGLDriver        *self,
           int slice_index = (col * rows) + row;
           GdkTexture *subtex;
           guint texture_id;
+          GskConversion conversion;
 
           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,
-                                                            GL_NEAREST, GL_NEAREST);
+                                                            GL_NEAREST, GL_NEAREST,
+                                                            &conversion);
+
+          if (conversion)
+            {
+              t = gsk_gl_texture_new (texture_id,
+                                      slice_width, slice_height,
+                                      format,
+                                      GL_NEAREST, GL_NEAREST,
+                                      0);
+              g_hash_table_insert (self->textures, GUINT_TO_POINTER (texture_id), t);
+
+              t = gsk_gl_driver_convert_texture (self,
+                                                 texture_id,
+                                                 slice_width, slice_height,
+                                                 format,
+                                                 GL_NEAREST, GL_NEAREST,
+                                                 conversion);
+
+              texture_id = t->texture_id;
+              t->texture_id = 0;
+              g_hash_table_steal (self->textures, GUINT_TO_POINTER (texture_id));
+              gsk_gl_texture_free (t);
+            }
+
           g_object_unref (subtex);
 
           slices[slice_index].rect.x = x;
diff --git a/gsk/gl/gskglprograms.defs b/gsk/gl/gskglprograms.defs
index 9e820dc4ef..6791c90037 100644
--- a/gsk/gl/gskglprograms.defs
+++ b/gsk/gl/gskglprograms.defs
@@ -90,3 +90,11 @@ GSK_GL_DEFINE_PROGRAM (postprocessing,
 GSK_GL_DEFINE_PROGRAM (linearize,
                        GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("linearize.glsl")),
                        GSK_GL_NO_UNIFORMS)
+
+GSK_GL_DEFINE_PROGRAM (premultiply,
+                       GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("premultiply.glsl")),
+                       GSK_GL_NO_UNIFORMS)
+
+GSK_GL_DEFINE_PROGRAM (linearize_premultiply,
+                       GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("linearize_premultiply.glsl")),
+                       GSK_GL_NO_UNIFORMS)
diff --git a/gsk/gl/resources/linearize_premultiply.glsl b/gsk/gl/resources/linearize_premultiply.glsl
new file mode 100644
index 0000000000..85a37c4ca3
--- /dev/null
+++ b/gsk/gl/resources/linearize_premultiply.glsl
@@ -0,0 +1,21 @@
+
+// VERTEX_SHADER:
+// linearize_premultiply.glsl
+
+void main() {
+  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+  vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+// linearize_premultiply.glsl
+
+void main() {
+  vec4 diffuse = GskTexture(u_source, vUv);
+
+  diffuse = gsk_srgb_to_linear(diffuse);
+  diffuse = gsk_premultiply(diffuse);
+
+  gskSetOutputColor(diffuse);
+}
diff --git a/gsk/gl/resources/preamble.glsl b/gsk/gl/resources/preamble.glsl
index 68a89552e9..c32379150c 100644
--- a/gsk/gl/resources/preamble.glsl
+++ b/gsk/gl/resources/preamble.glsl
@@ -42,7 +42,8 @@ gsk_get_bounds(vec4[3] data)
   return vec4(data[0].xy, data[0].xy + data[0].zw);
 }
 
-vec4 gsk_premultiply(vec4 c) {
+vec4 gsk_premultiply(vec4 c)
+{
   return vec4(c.rgb * c.a, c.a);
 }
 
@@ -62,7 +63,7 @@ vec4 gsk_srgb_to_linear(vec4 srgb)
 
 vec4 gsk_linear_to_srgb(vec4 linear_rgba)
 {
-  vec3 srgb = pow(linear_rgba.rgb , vec3(1/2.2));
+  vec3 srgb = pow(linear_rgba.rgb, vec3(1/2.2));
   return vec4(srgb, linear_rgba.a);
 }
 
diff --git a/gsk/gl/resources/premultiply.glsl b/gsk/gl/resources/premultiply.glsl
new file mode 100644
index 0000000000..b83ce9e90e
--- /dev/null
+++ b/gsk/gl/resources/premultiply.glsl
@@ -0,0 +1,20 @@
+
+// VERTEX_SHADER:
+// premultiply.glsl
+
+void main() {
+  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+  vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+// premultiply.glsl
+
+void main() {
+  vec4 diffuse = GskTexture(u_source, vUv);
+
+  diffuse = gsk_premultiply(diffuse);
+
+  gskSetOutputColor(diffuse);
+}
diff --git a/gsk/meson.build b/gsk/meson.build
index 3c3120508b..9a64967f49 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -21,6 +21,8 @@ gsk_private_gl_shaders = [
   'gl/resources/filled_border.glsl',
   'gl/resources/postprocessing.glsl',
   'gl/resources/linearize.glsl',
+  'gl/resources/premultiply.glsl',
+  'gl/resources/linearize_premultiply.glsl',
 ]
 
 gsk_public_sources = files([


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