From 88b709d5ec6012c1e877a497551e52543297c2f0 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 24 Sep 2020 15:40:17 +0200 Subject: [PATCH 1/6] texture: Unvirtualize download_surface We only have one implementation, so why do all this vfunc work? --- gdk/gdktexture.c | 41 +++++++++++++++++------------------------ gdk/gdktextureprivate.h | 1 - 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/gdk/gdktexture.c b/gdk/gdktexture.c index a6db7d5e17..c28bd9575f 100644 --- a/gdk/gdktexture.c +++ b/gdk/gdktexture.c @@ -130,28 +130,6 @@ gdk_texture_real_download (GdkTexture *self, GDK_TEXTURE_WARN_NOT_IMPLEMENTED_METHOD (self, download); } -static cairo_surface_t * -gdk_texture_real_download_surface (GdkTexture *texture) -{ - cairo_surface_t *surface; - cairo_status_t surface_status; - - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, - texture->width, texture->height); - - surface_status = cairo_surface_status (surface); - if (surface_status != CAIRO_STATUS_SUCCESS) - g_warning ("%s: surface error: %s", __FUNCTION__, - cairo_status_to_string (surface_status)); - - gdk_texture_download (texture, - cairo_image_surface_get_data (surface), - cairo_image_surface_get_stride (surface)); - cairo_surface_mark_dirty (surface); - - return surface; -} - static void gdk_texture_set_property (GObject *gobject, guint prop_id, @@ -216,7 +194,6 @@ gdk_texture_class_init (GdkTextureClass *klass) GObjectClass *gobject_class = G_OBJECT_CLASS (klass); klass->download = gdk_texture_real_download; - klass->download_surface = gdk_texture_real_download_surface; gobject_class->set_property = gdk_texture_set_property; gobject_class->get_property = gdk_texture_get_property; @@ -438,7 +415,23 @@ gdk_texture_get_height (GdkTexture *texture) cairo_surface_t * gdk_texture_download_surface (GdkTexture *texture) { - return GDK_TEXTURE_GET_CLASS (texture)->download_surface (texture); + cairo_surface_t *surface; + cairo_status_t surface_status; + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + texture->width, texture->height); + + surface_status = cairo_surface_status (surface); + if (surface_status != CAIRO_STATUS_SUCCESS) + g_warning ("%s: surface error: %s", __FUNCTION__, + cairo_status_to_string (surface_status)); + + gdk_texture_download (texture, + cairo_image_surface_get_data (surface), + cairo_image_surface_get_stride (surface)); + cairo_surface_mark_dirty (surface); + + return surface; } void diff --git a/gdk/gdktextureprivate.h b/gdk/gdktextureprivate.h index 07cb5436a3..3e2e9f3a49 100644 --- a/gdk/gdktextureprivate.h +++ b/gdk/gdktextureprivate.h @@ -28,7 +28,6 @@ struct _GdkTextureClass { const GdkRectangle *area, guchar *data, gsize stride); - cairo_surface_t * (* download_surface) (GdkTexture *texture); }; gpointer gdk_texture_new (const GdkTextureClass *klass, From 8e59cdabac9596acd2d2bc502ae583f321f4f4d1 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 24 Sep 2020 15:01:51 +0200 Subject: [PATCH 2/6] Add GDK_MEMORY_R8G8B8A8_PREMULTIPLIED This is the default OpenGL format, and in fact the only pixel format that GLES supports uploading as. Actually, the premultiplied part is really just about how we use the textures, but all textures in GTK are premultiplied. --- gdk/gdkmemorytexture.c | 29 +++++++++++++++++++---------- gdk/gdkmemorytexture.h | 3 +++ testsuite/gdk/memorytexture.c | 1 + 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/gdk/gdkmemorytexture.c b/gdk/gdkmemorytexture.c index 08215820b5..5ed0b988dc 100644 --- a/gdk/gdkmemorytexture.c +++ b/gdk/gdkmemorytexture.c @@ -45,6 +45,7 @@ gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format) { case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED: case GDK_MEMORY_A8R8G8B8_PREMULTIPLIED: + case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED: case GDK_MEMORY_B8G8R8A8: case GDK_MEMORY_A8R8G8B8: case GDK_MEMORY_R8G8B8A8: @@ -199,6 +200,9 @@ convert_swizzle ## A ## R ## G ## B (guchar *dest_data, \ } SWIZZLE(3,2,1,0) +SWIZZLE(2,1,0,3) +SWIZZLE(3,0,1,2) +SWIZZLE(1,2,3,0) #define SWIZZLE_OPAQUE(A,R,G,B) \ static void \ @@ -267,6 +271,10 @@ SWIZZLE_PREMULTIPLY (3,2,1,0, 3,0,1,2) SWIZZLE_PREMULTIPLY (0,1,2,3, 3,0,1,2) SWIZZLE_PREMULTIPLY (3,2,1,0, 0,3,2,1) SWIZZLE_PREMULTIPLY (0,1,2,3, 0,3,2,1) +SWIZZLE_PREMULTIPLY (3,0,1,2, 3,2,1,0) +SWIZZLE_PREMULTIPLY (3,0,1,2, 0,1,2,3) +SWIZZLE_PREMULTIPLY (3,0,1,2, 3,0,1,2) +SWIZZLE_PREMULTIPLY (3,0,1,2, 0,3,2,1) typedef void (* ConversionFunc) (guchar *dest_data, gsize dest_stride, @@ -275,16 +283,17 @@ typedef void (* ConversionFunc) (guchar *dest_data, gsize width, gsize height); -static ConversionFunc converters[GDK_MEMORY_N_FORMATS][2] = +static ConversionFunc converters[GDK_MEMORY_N_FORMATS][3] = { - { convert_memcpy, convert_swizzle3210 }, - { convert_swizzle3210, convert_memcpy }, - { convert_swizzle_premultiply_3210_3210, convert_swizzle_premultiply_0123_3210 }, - { convert_swizzle_premultiply_3210_0123, convert_swizzle_premultiply_0123_0123 }, - { convert_swizzle_premultiply_3210_3012, convert_swizzle_premultiply_0123_3012 }, - { convert_swizzle_premultiply_3210_0321, convert_swizzle_premultiply_0123_0321 }, - { convert_swizzle_opaque_3210, convert_swizzle_opaque_0123 }, - { convert_swizzle_opaque_3012, convert_swizzle_opaque_0321 } + { convert_memcpy, convert_swizzle3210, convert_swizzle2103 }, + { convert_swizzle3210, convert_memcpy, convert_swizzle3012 }, + { convert_swizzle2103, convert_swizzle1230, convert_memcpy }, + { convert_swizzle_premultiply_3210_3210, convert_swizzle_premultiply_0123_3210, convert_swizzle_premultiply_3012_3210, }, + { convert_swizzle_premultiply_3210_0123, convert_swizzle_premultiply_0123_0123, convert_swizzle_premultiply_3012_0123 }, + { convert_swizzle_premultiply_3210_3012, convert_swizzle_premultiply_0123_3012, convert_swizzle_premultiply_3012_3012 }, + { convert_swizzle_premultiply_3210_0321, convert_swizzle_premultiply_0123_0321, convert_swizzle_premultiply_3012_0321 }, + { convert_swizzle_opaque_3210, convert_swizzle_opaque_0123, convert_swizzle_opaque_3012 }, + { convert_swizzle_opaque_3012, convert_swizzle_opaque_0321, convert_swizzle_opaque_3210 } }; void @@ -297,7 +306,7 @@ gdk_memory_convert (guchar *dest_data, gsize width, gsize height) { - g_assert (dest_format < 2); + g_assert (dest_format < 3); g_assert (src_format < GDK_MEMORY_N_FORMATS); converters[src_format][dest_format] (dest_data, dest_stride, src_data, src_stride, width, height); diff --git a/gdk/gdkmemorytexture.h b/gdk/gdkmemorytexture.h index eea2fda324..b24e8c6796 100644 --- a/gdk/gdkmemorytexture.h +++ b/gdk/gdkmemorytexture.h @@ -34,6 +34,8 @@ G_BEGIN_DECLS * The color values are premultiplied with the alpha value. * @GDK_MEMORY_A8R8G8B8_PREMULTIPLIED: 4 bytes; for alpha, red, green, blue. * The color values are premultiplied with the alpha value. + * @GDK_MEMORY_R8G8B8A8_PREMULTIPLIED: 4 bytes; for red, green, blue, alpha + * The color values are premultiplied with the alpha value. * @GDK_MEMORY_B8G8R8A8: 4 bytes; for blue, green, red, alpha. * @GDK_MEMORY_A8R8G8B8: 4 bytes; for alpha, red, green, blue. * @GDK_MEMORY_R8G8B8A8: 4 bytes; for red, green, blue, alpha. @@ -58,6 +60,7 @@ G_BEGIN_DECLS typedef enum { GDK_MEMORY_B8G8R8A8_PREMULTIPLIED, GDK_MEMORY_A8R8G8B8_PREMULTIPLIED, + GDK_MEMORY_R8G8B8A8_PREMULTIPLIED, GDK_MEMORY_B8G8R8A8, GDK_MEMORY_A8R8G8B8, GDK_MEMORY_R8G8B8A8, diff --git a/testsuite/gdk/memorytexture.c b/testsuite/gdk/memorytexture.c index 381d61aac7..950de41b0a 100644 --- a/testsuite/gdk/memorytexture.c +++ b/testsuite/gdk/memorytexture.c @@ -37,6 +37,7 @@ typedef struct _TestData { static MemoryData tests[GDK_MEMORY_N_FORMATS] = { { 4, FALSE, { RGBA(FF,00,00,FF), RGBA(00,FF,00,FF), RGBA(00,00,FF,FF), RGBA(00,00,00,00), RGBA(66,22,44,AA) } }, { 4, FALSE, { RGBA(FF,00,00,FF), RGBA(FF,00,FF,00), RGBA(FF,FF,00,00), RGBA(00,00,00,00), RGBA(AA,44,22,66) } }, + { 4, FALSE, { RGBA(00,00,FF,FF), RGBA(00,FF,00,FF), RGBA(FF,00,00,FF), RGBA(00,00,00,00), RGBA(44,22,66,AA) } }, { 4, FALSE, { RGBA(FF,00,00,FF), RGBA(00,FF,00,FF), RGBA(00,00,FF,FF), RGBA(00,00,00,00), RGBA(99,33,66,AA) } }, { 4, FALSE, { RGBA(FF,00,00,FF), RGBA(FF,00,FF,00), RGBA(FF,FF,00,00), RGBA(00,00,00,00), RGBA(AA,66,33,99) } }, { 4, FALSE, { RGBA(00,00,FF,FF), RGBA(00,FF,00,FF), RGBA(FF,00,00,FF), RGBA(00,00,00,00), RGBA(66,33,99,AA) } }, From 1001995d496573072171ed2200fa0c470ffac476 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 24 Sep 2020 15:03:48 +0200 Subject: [PATCH 3/6] Correctly upload textures for GLES GLES doesn't support the GL_BGRA + GL_UNSIGNED_INT_24_8 hack that we use on desktop OpenGL to upload textures directly in the cairo pixel format. This adds the required conversions to all the places that currently need it. We also add a data_format to the internal gdk_gl_context_upload_texture() function to make it clearer what the format are. Currently it is always the cairo image surface format, but eventually we want to support other formats so that we can avoid some of the unnecessary conversions we do. Also, the current gdk_gl_context_upload_texture() code always converts to a cairo format and uploads that like we did before. Later commits will allow this to use other upload formats that gl supports to avoid conversions. --- gdk/gdkgl.c | 1 + gdk/gdkglcontext.c | 79 +++++++++++++++++++++++++++------------ gdk/gdkglcontextprivate.h | 2 + gsk/gl/gskglglyphcache.c | 29 +++++++++++--- gsk/gl/gskgliconcache.c | 58 +++++++++++++++++----------- 5 files changed, 119 insertions(+), 50 deletions(-) diff --git a/gdk/gdkgl.c b/gdk/gdkgl.c index 309f792613..2fe5cc863e 100644 --- a/gdk/gdkgl.c +++ b/gdk/gdkgl.c @@ -482,6 +482,7 @@ gdk_cairo_surface_upload_to_gl (cairo_surface_t *surface, rect.width, rect.height, cairo_image_surface_get_stride (tmp), + GDK_MEMORY_DEFAULT, target); cairo_surface_unmap_image (surface, tmp); diff --git a/gdk/gdkglcontext.c b/gdk/gdkglcontext.c index 2843ef05f5..9d658660e2 100644 --- a/gdk/gdkglcontext.c +++ b/gdk/gdkglcontext.c @@ -87,6 +87,7 @@ #include "gdkglcontextprivate.h" #include "gdkdisplayprivate.h" +#include "gdkmemorytextureprivate.h" #include "gdkinternals.h" #include "gdkintl.h" @@ -227,49 +228,81 @@ gdk_gl_context_upload_texture (GdkGLContext *context, int width, int height, int stride, + GdkMemoryFormat data_format, guint texture_target) { GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); + guchar *copy = NULL; + guint gl_format; + guint gl_type; g_return_if_fail (GDK_IS_GL_CONTEXT (context)); + if (priv->use_es) + { + /* GLES only supports rgba, so convert if necessary */ + if (data_format != GDK_MEMORY_R8G8B8A8_PREMULTIPLIED) + { + copy = g_malloc (width * height * 4); + gdk_memory_convert (copy, width * 4, + GDK_MEMORY_R8G8B8A8_PREMULTIPLIED, + data, stride, data_format, + width, height); + stride = width * 4; + data = copy; + } + + gl_format = GL_RGBA; + gl_type = GL_UNSIGNED_BYTE; + } + else + { + if (data_format == GDK_MEMORY_DEFAULT) /* Cairo surface format */ + { + gl_format = GL_BGRA; + gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; + } + else /* Fall-back, convert to cairo-surface-format */ + { + copy = g_malloc (width * height * 4); + gdk_memory_convert (copy, width * 4, + GDK_MEMORY_DEFAULT, + data, stride, data_format, + width, height); + stride = width * 4; + data = copy; + gl_format = GL_BGRA; + gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; + } + } + + /* GL_UNPACK_ROW_LENGTH is available on desktop GL, OpenGL ES >= 3.0, or if * the GL_EXT_unpack_subimage extension for OpenGL ES 2.0 is available */ - if (!priv->use_es || - (priv->use_es && (priv->gl_version >= 30 || priv->has_unpack_subimage))) + if (stride == width * 4) + { + glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, data); + } + else if (!priv->use_es || + (priv->use_es && (priv->gl_version >= 30 || priv->has_unpack_subimage))) { glPixelStorei (GL_UNPACK_ALIGNMENT, 4); glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / 4); - if (priv->use_es) - glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, - data); - else - glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, - data); + glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, data); glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); } else { int i; - - if (priv->use_es) - { - glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - - for (i = 0; i < height; i++) - glTexSubImage2D (texture_target, 0, 0, i, width, 1, GL_RGBA, GL_UNSIGNED_BYTE, data + (i * stride)); - } - else - { - glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); - - for (i = 0; i < height; i++) - glTexSubImage2D (texture_target, 0, 0, i, width, 1, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data + (i * stride)); - } + glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, NULL); + for (i = 0; i < height; i++) + glTexSubImage2D (texture_target, 0, 0, i, width, 1, gl_format, gl_type, data + (i * stride)); } + + g_free (copy); } static gboolean diff --git a/gdk/gdkglcontextprivate.h b/gdk/gdkglcontextprivate.h index 8991332b56..e785fbc63d 100644 --- a/gdk/gdkglcontextprivate.h +++ b/gdk/gdkglcontextprivate.h @@ -23,6 +23,7 @@ #include "gdkglcontext.h" #include "gdkdrawcontextprivate.h" +#include "gdkmemorytexture.h" G_BEGIN_DECLS @@ -84,6 +85,7 @@ void gdk_gl_context_upload_texture (GdkGLContext int width, int height, int stride, + GdkMemoryFormat data_format, guint texture_target); GdkGLContextPaintData * gdk_gl_context_get_paint_data (GdkGLContext *context); gboolean gdk_gl_context_use_texture_rectangle (GdkGLContext *context); diff --git a/gsk/gl/gskglglyphcache.c b/gsk/gl/gskglglyphcache.c index 4d23eb60be..b147dcef21 100644 --- a/gsk/gl/gskglglyphcache.c +++ b/gsk/gl/gskglglyphcache.c @@ -7,6 +7,7 @@ #include "gskgltextureatlasprivate.h" #include "gdk/gdkglcontextprivate.h" +#include "gdk/gdkmemorytextureprivate.h" #include #include @@ -186,6 +187,10 @@ upload_glyph (GlyphCacheKey *key, GskGLCachedGlyph *value) { GskImageRegion r; + guchar *pixel_data; + guchar *free_data = NULL; + guint gl_format; + guint gl_type; gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (), "Uploading glyph %d", @@ -197,15 +202,27 @@ upload_glyph (GlyphCacheKey *key, glBindTexture (GL_TEXTURE_2D, value->texture_id); if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ())) - glTexSubImage2D (GL_TEXTURE_2D, 0, r.x, r.y, r.width, r.height, - GL_RGBA, GL_UNSIGNED_BYTE, - r.data); + { + pixel_data = free_data = g_malloc (r.width * r.height * 4); + gdk_memory_convert (pixel_data, r.width * 4, + GDK_MEMORY_R8G8B8A8_PREMULTIPLIED, + r.data, r.width * 4, + GDK_MEMORY_DEFAULT, r.width, r.height); + gl_format = GL_RGBA; + gl_type = GL_UNSIGNED_BYTE; + } else - glTexSubImage2D (GL_TEXTURE_2D, 0, r.x, r.y, r.width, r.height, - GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, - r.data); + { + pixel_data = r.data; + gl_format = GL_BGRA; + gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; + } + + glTexSubImage2D (GL_TEXTURE_2D, 0, r.x, r.y, r.width, r.height, + gl_format, gl_type, pixel_data); glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); g_free (r.data); + g_free (free_data); } gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ()); diff --git a/gsk/gl/gskgliconcache.c b/gsk/gl/gskgliconcache.c index 5097ae597e..4bbbdbb4da 100644 --- a/gsk/gl/gskgliconcache.c +++ b/gsk/gl/gskgliconcache.c @@ -1,6 +1,7 @@ #include "gskgliconcacheprivate.h" #include "gskgltextureatlasprivate.h" #include "gdk/gdktextureprivate.h" +#include "gdk/gdkmemorytextureprivate.h" #include "gdk/gdkglcontextprivate.h" #include @@ -134,7 +135,10 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self, int packed_y = 0; cairo_surface_t *surface; unsigned char *surface_data; + unsigned char *pixel_data; + guchar *free_data = NULL; guint gl_format; + guint gl_type; gsk_gl_texture_atlases_pack (self->atlases, width + 2, height + 2, &atlas, &packed_x, &packed_y); @@ -158,36 +162,47 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self, "Uploading texture"); if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ())) - gl_format = GL_RGBA; + { + pixel_data = free_data = g_malloc (width * height * 4); + gdk_memory_convert (pixel_data, width * 4, + GDK_MEMORY_R8G8B8A8_PREMULTIPLIED, + surface_data, cairo_image_surface_get_stride (surface), + GDK_MEMORY_DEFAULT, width, height); + gl_format = GL_RGBA; + gl_type = GL_UNSIGNED_BYTE; + } else - gl_format = GL_BGRA; + { + pixel_data = surface_data; + gl_format = GL_BGRA; + gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; + } glBindTexture (GL_TEXTURE_2D, atlas->texture_id); glTexSubImage2D (GL_TEXTURE_2D, 0, packed_x + 1, packed_y + 1, width, height, - gl_format, - GL_UNSIGNED_BYTE, - surface_data); + gl_format, gl_type, + pixel_data); /* Padding top */ glTexSubImage2D (GL_TEXTURE_2D, 0, packed_x + 1, packed_y, width, 1, - gl_format, GL_UNSIGNED_BYTE, - surface_data); + gl_format, gl_type, + pixel_data); /* Padding left */ glTexSubImage2D (GL_TEXTURE_2D, 0, packed_x, packed_y + 1, 1, height, - gl_format, GL_UNSIGNED_BYTE, - surface_data); + gl_format, gl_type, + pixel_data); /* Padding top left */ glTexSubImage2D (GL_TEXTURE_2D, 0, packed_x, packed_y, 1, 1, - gl_format, GL_UNSIGNED_BYTE, - surface_data); + gl_format, gl_type, + pixel_data); /* Padding right */ glPixelStorei (GL_UNPACK_ROW_LENGTH, width); @@ -195,14 +210,14 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self, glTexSubImage2D (GL_TEXTURE_2D, 0, packed_x + width + 1, packed_y + 1, 1, height, - gl_format, GL_UNSIGNED_BYTE, - surface_data); + gl_format, gl_type, + pixel_data); /* Padding top right */ glTexSubImage2D (GL_TEXTURE_2D, 0, packed_x + width + 1, packed_y, 1, 1, - gl_format, GL_UNSIGNED_BYTE, - surface_data); + gl_format, gl_type, + pixel_data); /* Padding bottom */ glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0); glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); @@ -210,22 +225,22 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self, glTexSubImage2D (GL_TEXTURE_2D, 0, packed_x + 1, packed_y + 1 + height, width, 1, - gl_format, GL_UNSIGNED_BYTE, - surface_data); + gl_format, gl_type, + pixel_data); /* Padding bottom left */ glTexSubImage2D (GL_TEXTURE_2D, 0, packed_x, packed_y + 1 + height, 1, 1, - gl_format, GL_UNSIGNED_BYTE, - surface_data); + gl_format, gl_type, + pixel_data); /* Padding bottom right */ glPixelStorei (GL_UNPACK_ROW_LENGTH, width); glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1); glTexSubImage2D (GL_TEXTURE_2D, 0, packed_x + 1 + width, packed_y + 1 + height, 1, 1, - gl_format, GL_UNSIGNED_BYTE, - surface_data); + gl_format, gl_type, + pixel_data); /* Reset this */ glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0); glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); @@ -236,6 +251,7 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self, *out_icon_data = icon_data; cairo_surface_destroy (surface); + g_free (free_data); #if 0 { From c71921a6be34fcad9807bd4c7e5ca5d09616475b Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 24 Sep 2020 17:01:04 +0200 Subject: [PATCH 4/6] gdk_gl_context_upload_texture() avoid conversion for pixbuf format The gdk-pixbuf non-rgba format can be directly uploaded without conversion. The rgba format needs alpha premultiplication though, which is not supported by GL during upload. --- gdk/gdkglcontext.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/gdk/gdkglcontext.c b/gdk/gdkglcontext.c index 9d658660e2..f9512b0343 100644 --- a/gdk/gdkglcontext.c +++ b/gdk/gdkglcontext.c @@ -235,6 +235,7 @@ gdk_gl_context_upload_texture (GdkGLContext *context, guchar *copy = NULL; guint gl_format; guint gl_type; + guint bpp; g_return_if_fail (GDK_IS_GL_CONTEXT (context)); @@ -252,6 +253,7 @@ gdk_gl_context_upload_texture (GdkGLContext *context, data = copy; } + bpp = 4; gl_format = GL_RGBA; gl_type = GL_UNSIGNED_BYTE; } @@ -261,6 +263,13 @@ gdk_gl_context_upload_texture (GdkGLContext *context, { gl_format = GL_BGRA; gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; + bpp = 4; + } + else if (data_format == GDK_MEMORY_R8G8B8) /* Pixmap non-alpha data */ + { + gl_format = GL_RGB; + gl_type = GL_UNSIGNED_BYTE; + bpp = 3; } else /* Fall-back, convert to cairo-surface-format */ { @@ -270,25 +279,25 @@ gdk_gl_context_upload_texture (GdkGLContext *context, data, stride, data_format, width, height); stride = width * 4; + bpp = 4; data = copy; gl_format = GL_BGRA; gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; } } - /* GL_UNPACK_ROW_LENGTH is available on desktop GL, OpenGL ES >= 3.0, or if * the GL_EXT_unpack_subimage extension for OpenGL ES 2.0 is available */ - if (stride == width * 4) + if (stride == width * bpp) { glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, data); } - else if (!priv->use_es || - (priv->use_es && (priv->gl_version >= 30 || priv->has_unpack_subimage))) + else if ((!priv->use_es || + (priv->use_es && (priv->gl_version >= 30 || priv->has_unpack_subimage)))) { - glPixelStorei (GL_UNPACK_ALIGNMENT, 4); - glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / 4); + glPixelStorei (GL_UNPACK_ALIGNMENT, bpp); + glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / bpp); glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, data); From 90fc671ef8ffcddaf7dac4a2a72c9094f7ef57d3 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 24 Sep 2020 16:58:46 +0200 Subject: [PATCH 5/6] 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 #include @@ -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); From a299bbfc58aadb75a416cc4abe9e95f5a59efe1f Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 25 Sep 2020 10:08:21 +0200 Subject: [PATCH 6/6] Drop gdk_cairo_surface_upload_to_gl() helper There is nothing really special about this code, its just a helper for uploading pixel data to opengl, and we're not really in the business of doing opengl-specific helper functions. --- docs/reference/gdk/gdk4-sections.txt | 1 - gdk/gdkcairo.h | 7 ---- gdk/gdkgl.c | 49 ---------------------------- 3 files changed, 57 deletions(-) diff --git a/docs/reference/gdk/gdk4-sections.txt b/docs/reference/gdk/gdk4-sections.txt index 0bfe140b17..4c2b0dc3b2 100644 --- a/docs/reference/gdk/gdk4-sections.txt +++ b/docs/reference/gdk/gdk4-sections.txt @@ -300,7 +300,6 @@ gdk_cairo_rectangle gdk_cairo_region gdk_cairo_region_create_from_surface gdk_cairo_draw_from_gl -gdk_cairo_surface_upload_to_gl
diff --git a/gdk/gdkcairo.h b/gdk/gdkcairo.h index e2dc85c41c..40ab46baf6 100644 --- a/gdk/gdkcairo.h +++ b/gdk/gdkcairo.h @@ -61,13 +61,6 @@ void gdk_cairo_draw_from_gl (cairo_t *cr, int width, int height); -GDK_AVAILABLE_IN_ALL -void gdk_cairo_surface_upload_to_gl (cairo_surface_t *surface, - int target, - int width, - int height, - GdkGLContext *context); - G_END_DECLS #endif /* __GDK_CAIRO_H__ */ diff --git a/gdk/gdkgl.c b/gdk/gdkgl.c index 2fe5cc863e..7ae8decfed 100644 --- a/gdk/gdkgl.c +++ b/gdk/gdkgl.c @@ -438,52 +438,3 @@ out: if (clip_region) cairo_region_destroy (clip_region); } - -/** - * gdk_cairo_surface_upload_to_gl: - * @surface: a Cairo surface - * @target: a GL texture target - * @width: the width of the texture @target - * @height: the height of the texture @target - * @context: (nullable): a #GdkGLContext, or %NULL to use the currently - * bound context - * - * Uploads the contents of a Cairo @surface to a GL texture @target. - */ -void -gdk_cairo_surface_upload_to_gl (cairo_surface_t *surface, - int target, - int width, - int height, - GdkGLContext *context) -{ - cairo_rectangle_int_t rect; - cairo_surface_t *tmp; - double device_x_offset, device_y_offset; - - g_return_if_fail (surface != NULL); - g_return_if_fail (context == NULL || GDK_IS_GL_CONTEXT (context)); - - if (context == NULL) - context = gdk_gl_context_get_current (); - - cairo_surface_flush (surface); - - cairo_surface_get_device_offset (surface, &device_x_offset, &device_y_offset); - - rect.x = (int) device_x_offset; - rect.y = (int) device_y_offset; - rect.width = width; - rect.height = height; - tmp = cairo_surface_map_to_image (surface, &rect); - - gdk_gl_context_upload_texture (context, - cairo_image_surface_get_data (tmp), - rect.width, - rect.height, - cairo_image_surface_get_stride (tmp), - GDK_MEMORY_DEFAULT, - target); - - cairo_surface_unmap_image (surface, tmp); -}