diff options
-rw-r--r-- | gdk/gdk.h | 1 | ||||
-rw-r--r-- | gdk/gdkglcontext.c | 19 | ||||
-rw-r--r-- | gdk/gdkglcontextprivate.h | 2 | ||||
-rw-r--r-- | gdk/gdkgltexture.c | 38 | ||||
-rw-r--r-- | gdk/gdkgltexture.h | 2 | ||||
-rw-r--r-- | gdk/gdkgltexturebuilder.c | 655 | ||||
-rw-r--r-- | gdk/gdkgltexturebuilder.h | 87 | ||||
-rw-r--r-- | gdk/gdkgltextureprivate.h | 7 | ||||
-rw-r--r-- | gdk/meson.build | 2 | ||||
-rw-r--r-- | gsk/gl/gskglcommandqueue.c | 13 | ||||
-rw-r--r-- | gsk/gl/gskglcommandqueueprivate.h | 41 | ||||
-rw-r--r-- | gsk/gl/gskgldriver.c | 42 | ||||
-rw-r--r-- | gsk/gl/gskglprogramprivate.h | 16 | ||||
-rw-r--r-- | gsk/gl/gskglrenderjob.c | 42 | ||||
-rw-r--r-- | gtk/gtkglarea.c | 67 | ||||
-rw-r--r-- | modules/media/gtkgstsink.c | 33 | ||||
-rw-r--r-- | testsuite/gdk/gltexture.c | 9 |
17 files changed, 1009 insertions, 67 deletions
@@ -54,6 +54,7 @@ #include <gdk/gdkframetimings.h> #include <gdk/gdkglcontext.h> #include <gdk/gdkgltexture.h> +#include <gdk/gdkgltexturebuilder.h> #include <gdk/gdkkeys.h> #include <gdk/gdkkeysyms.h> #include <gdk/gdkmemorytexture.h> diff --git a/gdk/gdkglcontext.c b/gdk/gdkglcontext.c index 9d06a3c266..7f8cf27abd 100644 --- a/gdk/gdkglcontext.c +++ b/gdk/gdkglcontext.c @@ -106,6 +106,7 @@ typedef struct { guint has_khr_debug : 1; guint use_khr_debug : 1; guint has_half_float : 1; + guint has_sync : 1; guint has_unpack_subimage : 1; guint has_debug_output : 1; guint extensions_checked : 1; @@ -1553,6 +1554,10 @@ gdk_gl_context_check_extensions (GdkGLContext *context) priv->has_half_float = gdk_gl_context_check_version (context, "3.0", "3.0") || epoxy_has_gl_extension ("OES_vertex_half_float"); + priv->has_sync = gdk_gl_context_check_version (context, "3.2", "3.0") || + epoxy_has_gl_extension ("GL_ARB_sync") || + epoxy_has_gl_extension ("GK_APPLE_sync"); + #ifdef G_ENABLE_DEBUG { int max_texture_size; @@ -1564,7 +1569,8 @@ gdk_gl_context_check_extensions (GdkGLContext *context) "* Extensions checked:\n" " - GL_KHR_debug: %s\n" " - GL_EXT_unpack_subimage: %s\n" - " - OES_vertex_half_float: %s", + " - half float: %s\n" + " - sync: %s", gdk_gl_context_get_use_es (context) ? "OpenGL ES" : "OpenGL", gdk_gl_version_get_major (&priv->gl_version), gdk_gl_version_get_minor (&priv->gl_version), priv->is_legacy ? "legacy" : "core", @@ -1572,7 +1578,8 @@ gdk_gl_context_check_extensions (GdkGLContext *context) max_texture_size, priv->has_khr_debug ? "yes" : "no", priv->has_unpack_subimage ? "yes" : "no", - priv->has_half_float ? "yes" : "no"); + priv->has_half_float ? "yes" : "no", + priv->has_sync ? "yes" : "no"); } #endif @@ -1800,6 +1807,14 @@ gdk_gl_context_has_vertex_half_float (GdkGLContext *self) return priv->has_half_float; } +gboolean +gdk_gl_context_has_sync (GdkGLContext *self) +{ + GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self); + + return priv->has_sync; +} + /* This is currently private! */ /* When using GL/ES, don't flip the 'R' and 'B' bits on Windows/ANGLE for glReadPixels() */ gboolean diff --git a/gdk/gdkglcontextprivate.h b/gdk/gdkglcontextprivate.h index 2c23153992..f09d5756b1 100644 --- a/gdk/gdkglcontextprivate.h +++ b/gdk/gdkglcontextprivate.h @@ -148,6 +148,8 @@ gboolean gdk_gl_context_use_es_bgra (GdkGLContext gboolean gdk_gl_context_has_vertex_half_float (GdkGLContext *self) G_GNUC_PURE; +gboolean gdk_gl_context_has_sync (GdkGLContext *self) G_GNUC_PURE; + double gdk_gl_context_get_scale (GdkGLContext *self); G_END_DECLS diff --git a/gdk/gdkgltexture.c b/gdk/gdkgltexture.c index ef9913ce12..b409ae852d 100644 --- a/gdk/gdkgltexture.c +++ b/gdk/gdkgltexture.c @@ -39,6 +39,7 @@ struct _GdkGLTexture { GdkGLContext *context; guint id; gboolean has_mipmap; + gpointer sync; GdkTexture *saved; @@ -99,6 +100,10 @@ gdk_gl_texture_invoke_callback (gpointer data) context = gdk_display_get_gl_context (gdk_gl_context_get_display (invoke->self->context)); gdk_gl_context_make_current (context); + + if (invoke->self->sync && context != invoke->self->context) + glWaitSync (invoke->self->sync, 0, GL_TIMEOUT_IGNORED); + glBindTexture (GL_TEXTURE_2D, invoke->self->id); invoke->func (invoke->self, context, invoke->data); @@ -296,6 +301,12 @@ gdk_gl_texture_has_mipmap (GdkGLTexture *self) return self->has_mipmap; } +gpointer +gdk_gl_texture_get_sync (GdkGLTexture *self) +{ + return self->sync; +} + /** * gdk_gl_texture_release: * @self: a `GdkTexture` wrapping a GL texture @@ -321,6 +332,30 @@ gdk_gl_texture_release (GdkGLTexture *self) drop_gl_resources (self); } +GdkTexture * +gdk_gl_texture_new_from_builder (GdkGLTextureBuilder *builder, + GDestroyNotify destroy, + gpointer data) +{ + GdkGLTexture *self; + + self = g_object_new (GDK_TYPE_GL_TEXTURE, + "width", gdk_gl_texture_builder_get_width (builder), + "height", gdk_gl_texture_builder_get_height (builder), + NULL); + + self->context = g_object_ref (gdk_gl_texture_builder_get_context (builder)); + self->id = gdk_gl_texture_builder_get_id (builder); + GDK_TEXTURE (self)->format = gdk_gl_texture_builder_get_format (builder); + self->has_mipmap = gdk_gl_texture_builder_get_has_mipmap (builder); + if (gdk_gl_context_has_sync (self->context)) + self->sync = gdk_gl_texture_builder_get_sync (builder); + self->destroy = destroy; + self->data = data; + + return GDK_TEXTURE (self); +} + static void gdk_gl_texture_determine_format (GdkGLTexture *self) { @@ -463,6 +498,9 @@ gdk_gl_texture_determine_format (GdkGLTexture *self) * * Return value: (transfer full) (type GdkGLTexture): A newly-created * `GdkTexture` + * + * Deprecated: 4.12: [class@Gdk.GLTextureBuilder] supercedes this function + * and provides extended functionality for creating GL textures. */ GdkTexture * gdk_gl_texture_new (GdkGLContext *context, diff --git a/gdk/gdkgltexture.h b/gdk/gdkgltexture.h index 55e379d8c4..6f1cebdf47 100644 --- a/gdk/gdkgltexture.h +++ b/gdk/gdkgltexture.h @@ -38,7 +38,7 @@ typedef struct _GdkGLTextureClass GdkGLTextureClass; GDK_AVAILABLE_IN_ALL GType gdk_gl_texture_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL +GDK_DEPRECATED_IN_4_12_FOR(GdkGLTextureBuilder) GdkTexture * gdk_gl_texture_new (GdkGLContext *context, guint id, int width, diff --git a/gdk/gdkgltexturebuilder.c b/gdk/gdkgltexturebuilder.c new file mode 100644 index 0000000000..ceaf44a9c8 --- /dev/null +++ b/gdk/gdkgltexturebuilder.c @@ -0,0 +1,655 @@ +/* + * Copyright © 2023 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: Benjamin Otte <otte@gnome.org> + */ + +#include "config.h" + +#include "gdkgltexturebuilder.h" + +#include "gdkenumtypes.h" +#include "gdkglcontext.h" +#include "gdkgltextureprivate.h" + +struct _GdkGLTextureBuilder +{ + GObject parent_instance; + + GdkGLContext *context; + guint id; + int width; + int height; + GdkMemoryFormat format; + gboolean has_mipmap; + gpointer sync; +}; + +struct _GdkGLTextureBuilderClass +{ + GObjectClass parent_class; +}; + +/** + * GdkGLTextureBuilder: + * + * `GdkGLTextureBuilder` is a buider used to construct [class@Gdk.Texture] objects from + * GL textures. + * + * The operation is quite simple: Create a texture builder, set all the necessary + * properties - keep in mind that the properties [property@Gdk.GLTextureBuilder:context], + * [property@Gdk.GLTextureBuilder:id], [property@Gdk.GLTextureBuilder:width], and + * [property@Gdk.GLTextureBuilder:height] are mandatory - and then call + * [method@Gdk.GLTextureBuilder.build] to create the new texture. + * + * `GdkGLTextureBuilder` can be used for quick one-shot construction of + * textures as well as kept around and reused to construct multiple textures. + * + * Since: 4.12 + */ + +enum +{ + PROP_0, + PROP_CONTEXT, + PROP_FORMAT, + PROP_HAS_MIPMAP, + PROP_HEIGHT, + PROP_ID, + PROP_SYNC, + PROP_WIDTH, + + N_PROPS +}; + +G_DEFINE_TYPE (GdkGLTextureBuilder, gdk_gl_texture_builder, G_TYPE_OBJECT) + +static GParamSpec *properties[N_PROPS] = { NULL, }; + +static void +gdk_gl_texture_builder_dispose (GObject *object) +{ + GdkGLTextureBuilder *self = GDK_GL_TEXTURE_BUILDER (object); + + g_clear_object (&self->context); + + G_OBJECT_CLASS (gdk_gl_texture_builder_parent_class)->dispose (object); +} + +static void +gdk_gl_texture_builder_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GdkGLTextureBuilder *self = GDK_GL_TEXTURE_BUILDER (object); + + switch (property_id) + { + case PROP_CONTEXT: + g_value_set_object (value, self->context); + break; + + case PROP_FORMAT: + g_value_set_enum (value, self->format); + break; + + case PROP_HAS_MIPMAP: + g_value_set_boolean (value, self->has_mipmap); + break; + + case PROP_HEIGHT: + g_value_set_int (value, self->height); + break; + + case PROP_ID: + g_value_set_uint (value, self->id); + break; + + case PROP_SYNC: + g_value_set_pointer (value, self->sync); + break; + + case PROP_WIDTH: + g_value_set_int (value, self->width); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gdk_gl_texture_builder_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GdkGLTextureBuilder *self = GDK_GL_TEXTURE_BUILDER (object); + + switch (property_id) + { + case PROP_CONTEXT: + gdk_gl_texture_builder_set_context (self, g_value_get_object (value)); + break; + + case PROP_FORMAT: + gdk_gl_texture_builder_set_format (self, g_value_get_enum (value)); + break; + + case PROP_HAS_MIPMAP: + gdk_gl_texture_builder_set_has_mipmap (self, g_value_get_boolean (value)); + break; + + case PROP_HEIGHT: + gdk_gl_texture_builder_set_height (self, g_value_get_int (value)); + break; + + case PROP_ID: + gdk_gl_texture_builder_set_id (self, g_value_get_uint (value)); + break; + + case PROP_SYNC: + gdk_gl_texture_builder_set_sync (self, g_value_get_pointer (value)); + break; + + case PROP_WIDTH: + gdk_gl_texture_builder_set_width (self, g_value_get_int (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gdk_gl_texture_builder_class_init (GdkGLTextureBuilderClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->dispose = gdk_gl_texture_builder_dispose; + gobject_class->get_property = gdk_gl_texture_builder_get_property; + gobject_class->set_property = gdk_gl_texture_builder_set_property; + + /** + * GdkGLTextureBuilder:context: (attributes org.gdk.Property.get=gdk_gl_texture_builder_get_context org.gdk.Property.set=gdk_gl_texture_builder_set_context) + * + * The context owning the texture. + * + * Since: 4.12 + */ + properties[PROP_CONTEXT] = + g_param_spec_object ("context", NULL, NULL, + GDK_TYPE_GL_CONTEXT, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + /** + * GdkGLTextureBuilder:format: (attributes org.gdk.Property.get=gdk_gl_texture_builder_get_format org.gdk.Property.set=gdk_gl_texture_builder_set_format) + * + * The format when downloading the texture. + * + * Since: 4.12 + */ + properties[PROP_FORMAT] = + g_param_spec_enum ("format", NULL, NULL, + GDK_TYPE_MEMORY_FORMAT, + GDK_MEMORY_R8G8B8A8_PREMULTIPLIED, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + /** + * GdkGLTextureBuilder:has-mipmap: (attributes org.gdk.Property.get=gdk_gl_texture_builder_get_has_mipmap org.gdk.Property.set=gdk_gl_texture_builder_set_has_mipmap) + * + * If the texture has a mipmap. + * + * Since: 4.12 + */ + properties[PROP_HAS_MIPMAP] = + g_param_spec_boolean ("has-mipmap", NULL, NULL, + FALSE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + /** + * GdkGLTextureBuilder:height: (attributes org.gdk.Property.get=gdk_gl_texture_builder_get_height org.gdk.Property.set=gdk_gl_texture_builder_set_height) + * + * The height of the texture. + * + * Since: 4.12 + */ + properties[PROP_HEIGHT] = + g_param_spec_int ("height", NULL, NULL, + G_MININT, G_MAXINT, 0, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + /** + * GdkGLTextureBuilder:id: (attributes org.gdk.Property.get=gdk_gl_texture_builder_get_id org.gdk.Property.set=gdk_gl_texture_builder_set_id) + * + * The texture ID to use. + * + * Since: 4.12 + */ + properties[PROP_ID] = + g_param_spec_uint ("id", NULL, NULL, + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + /** + * GdkGLTextureBuilder:sync: (attributes org.gdk.Property.get=gdk_gl_texture_builder_get_sync org.gdk.Property.set=gdk_gl_texture_builder_set_sync) + * + * An optional `GLSync` object. + * + * If this is set, GTK will wait on it before using the texture. + * + * Since: 4.12 + */ + properties[PROP_SYNC] = + g_param_spec_pointer ("sync", NULL, NULL, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + /** + * GdkGLTextureBuilder:width: (attributes org.gdk.Property.get=gdk_gl_texture_builder_get_width org.gdk.Property.set=gdk_gl_texture_builder_set_width) + * + * The width of the texture. + * + * Since: 4.12 + */ + properties[PROP_WIDTH] = + g_param_spec_int ("width", NULL, NULL, + G_MININT, G_MAXINT, 0, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (gobject_class, N_PROPS, properties); +} + +static void +gdk_gl_texture_builder_init (GdkGLTextureBuilder *self) +{ + self->format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED; +} + +/** + * gdk_gl_texture_builder_new: (constructor): + * + * Creates a new texture builder. + * + * Returns: the new `GdkTextureBuilder` + * + * Since: 4.12 + **/ +GdkGLTextureBuilder * +gdk_gl_texture_builder_new (void) +{ + return g_object_new (GDK_TYPE_GL_TEXTURE_BUILDER, NULL); +} + +/** + * gdk_gl_texture_builder_get_context: (attributes org.gdk.Method.get_property=context) + * @self: a `GdkGLTextureBuilder` + * + * Gets the context previously set via gdk_gl_texture_builder_set_context() or + * %NULL if none was set. + * + * Returns: (transfer none) (nullable): The context + * + * Since: 4.12 + */ +GdkGLContext * +gdk_gl_texture_builder_get_context (GdkGLTextureBuilder *self) +{ + g_return_val_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self), NULL); + + return self->context; +} + +/** + * gdk_gl_texture_builder_set_context: (attributes org.gdk.Method.set_property=context) + * @self: a `GdkGLTextureBuilder` + * @context: (nullable): The context the texture beongs to or %NULL to unset + * + * Sets the context to be used for the texture. This is the context that owns + * the texture. + * + * The context must be set before calling [method@Gdk.GLTextureBuilder.build]. + * + * Since: 4.12 + */ +void +gdk_gl_texture_builder_set_context (GdkGLTextureBuilder *self, + GdkGLContext *context) +{ + g_return_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self)); + g_return_if_fail (context == NULL || GDK_IS_GL_CONTEXT (context)); + + if (!g_set_object (&self->context, context)) + return; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CONTEXT]); +} + +/** + * gdk_gl_texture_builder_get_height: (attributes org.gdk.Method.get_property=height) + * @self: a `GdkGLTextureBuilder` + * + * Gets the height previously set via gdk_gl_texture_builder_set_height() or + * 0 if the height wasn't set. + * + * Returns: The height + * + * Since: 4.12 + */ +int +gdk_gl_texture_builder_get_height (GdkGLTextureBuilder *self) +{ + g_return_val_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self), 0); + + return self->height; +} + +/** + * gdk_gl_texture_builder_set_height: (attributes org.gdk.Method.set_property=height) + * @self: a `GdkGLTextureBuilder` + * @height: The texture's height or 0 to unset + * + * Sets the height of the texture. + * + * The height must be set before calling [method@Gdk.GLTextureBuilder.build]. + * + * Since: 4.12 + */ +void +gdk_gl_texture_builder_set_height (GdkGLTextureBuilder *self, + int height) +{ + g_return_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self)); + + if (self->height == height) + return; + + self->height = height; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HEIGHT]); +} + +/** + * gdk_gl_texture_builder_get_id: (attributes org.gdk.Method.get_property=id) + * @self: a `GdkGLTextureBuilder` + * + * Gets the texture id previously set via gdk_gl_texture_builder_set_id() or + * 0 if the id wasn't set. + * + * Returns: The id + * + * Since: 4.12 + */ +guint +gdk_gl_texture_builder_get_id (GdkGLTextureBuilder *self) +{ + g_return_val_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self), 0); + + return self->id; +} + +/** + * gdk_gl_texture_builder_set_id: (attributes org.gdk.Method.set_property=id) + * @self: a `GdkGLTextureBuilder` + * @id: The texture id to be used for creating the texture + * + * Sets the texture id of the texture. The texture id must remain unmodified + * until the texture was finalized. See [method@Gdk.GLTextureBuilder.build] + * for a longer discussion. + * + * The id must be set before calling [method@Gdk.GLTextureBuilder.build]. + * + * Since: 4.12 + */ +void +gdk_gl_texture_builder_set_id (GdkGLTextureBuilder *self, + guint id) +{ + g_return_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self)); + + if (self->id == id) + return; + + self->id = id; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ID]); +} + +/** + * gdk_gl_texture_builder_get_width: (attributes org.gdk.Method.get_property=width) + * @self: a `GdkGLTextureBuilder` + * + * Gets the width previously set via gdk_gl_texture_builder_set_width() or + * 0 if the width wasn't set. + * + * Returns: The width + * + * Since: 4.12 + */ +int +gdk_gl_texture_builder_get_width (GdkGLTextureBuilder *self) +{ + g_return_val_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self), 0); + + return self->width; +} + +/** + * gdk_gl_texture_builder_set_width: (attributes org.gdk.Method.set_property=width) + * @self: a `GdkGLTextureBuilder` + * @width: The texture's width or 0 to unset + * + * Sets the width of the texture. + * + * The width must be set before calling [method@Gdk.GLTextureBuilder.build]. + * + * Since: 4.12 + */ +void +gdk_gl_texture_builder_set_width (GdkGLTextureBuilder *self, + int width) +{ + g_return_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self)); + + if (self->width == width) + return; + + self->width = width; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_WIDTH]); +} + +/** + * gdk_gl_texture_builder_get_has_mipmap: (attributes org.gdk.Method.get_property=has-mipmap) + * @self: a `GdkGLTextureBuilder` + * + * Gets whether the texture has a mipmap. + * + * Returns: Whether the texture has a mipmap + * + * Since: 4.12 + */ +gboolean +gdk_gl_texture_builder_get_has_mipmap (GdkGLTextureBuilder *self) +{ + g_return_val_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self), FALSE); + + return self->has_mipmap; +} + +/** + * gdk_gl_texture_builder_set_has_mipmap: (attributes org.gdk.Method.set_property=has-mipmap) + * @self: a `GdkGLTextureBuilder` + * @has_mipmap: Whether the texture has a mipmap + * + * Sets whether the texture has a mipmap. This allows the renderer and other users of the + * generated texture to use a higher quality downscaling. + * + * Typically, the `glGenerateMipmap` function is used to generate a mimap. + * + * Since: 4.12 + */ +void +gdk_gl_texture_builder_set_has_mipmap (GdkGLTextureBuilder *self, + gboolean has_mipmap) +{ + g_return_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self)); + + if (self->has_mipmap == has_mipmap) + return; + + self->has_mipmap = has_mipmap; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HAS_MIPMAP]); +} + +/** + * gdk_gl_texture_builder_get_sync: (attributes org.gdk.Method.get_property=sync) + * @self: a `GdkGLTextureBuilder` + * + * Gets the `GLsync` previously set via gdk_gl_texture_builder_set_sync(). + * + * Returns: (nullable): the `GLSync` + * + * Since: 4.12 + */ +gpointer +gdk_gl_texture_builder_get_sync (GdkGLTextureBuilder *self) +{ + g_return_val_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self), NULL); + + return self->sync; +} + +/** + * gdk_gl_texture_builder_set_sync: (attributes org.gdk.Method.set_property=sync) + * @self: a `GdkGLTextureBuilder` + * @sync: (nullable): the GLSync object + * + * Sets the GLSync object to use for the texture. + * + * GTK will wait on this object before using the created `GdkTexture`. + * + * The `destroy` function that is passed to [method@Gdk.GLTextureBuilder.build] + * is responsible for freeing the sync object when it is no longer needed. + * The texture builder does not destroy it and it is the callers + * responsibility to make sure it doesn't leak. + * + * Since: 4.12 + */ +void +gdk_gl_texture_builder_set_sync (GdkGLTextureBuilder *self, + gpointer sync) +{ + g_return_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self)); + + if (self->sync == sync) + return; + + self->sync = sync; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SYNC]); +} + +/** + * gdk_gl_texture_builder_get_format: (attributes org.gdk.Method.get_property=format) + * @self: a `GdkGLTextureBuilder` + * + * Gets the format previously set via gdk_gl_texture_builder_set_format(). + * + * Returns: The format + * + * Since: 4.12 + */ +GdkMemoryFormat +gdk_gl_texture_builder_get_format (GdkGLTextureBuilder *self) +{ + g_return_val_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self), GDK_MEMORY_R8G8B8A8_PREMULTIPLIED); + + return self->format; +} + +/** + * gdk_gl_texture_builder_set_format: (attributes org.gdk.Method.set_property=format) + * @self: a `GdkGLTextureBuilder` + * @format: The texture's format + * + * Sets the format of the texture. The default is `GDK_MEMORY_R8G8B8A8_PREMULTIPLIED`. + * + * The format is the preferred format the texture data should be downloaded to. The + * format must be supported by the GL version of [property@Gdk.GLTextureBuilder:context]. + * + * Setting the right format is particularly useful when using high bit depth textures + * to preserve the bit depth, to set the correct value for unpremultiplied textures + * and to make sure opaque textures are treated as such. + * + * Since: 4.12 + */ +void +gdk_gl_texture_builder_set_format (GdkGLTextureBuilder *self, + GdkMemoryFormat format) +{ + g_return_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self)); + + if (self->format == format) + return; + + self->format = format; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FORMAT]); +} + +/** + * gdk_gl_texture_builder_build: + * @self: a `GdkGLTextureBuilder` + * @destroy: (nullable): destroy function to be called when the texture is + * released + * @data: user data to pass to the destroy function + * + * Builds a new `GdkTexture` with the values set up in the builder. + * + * The `destroy` function gets called when the returned texture gets released; + * either when the texture is finalized or by an explicit call to + * [method@Gdk.GLTexture.release]. It should release all GL resources associated + * with the texture, such as the [property@Gdk.GLTextureBuilder:id] and the + * [property@Gdk.GLTextureBuilder:sync]. + * + * Note that it is a programming error to call this function if any mandatory + * property has not been set. + * + * It is possible to call this function multiple times to create multiple textures, + * possibly with changing properties in between. + * + * Returns: (transfer full): a newly built `GdkTexture` + * + * Since: 4.12 + */ +GdkTexture * +gdk_gl_texture_builder_build (GdkGLTextureBuilder *self, + GDestroyNotify destroy, + gpointer data) +{ + g_return_val_if_fail (GDK_IS_GL_TEXTURE_BUILDER (self), NULL); + g_return_val_if_fail (destroy == NULL || data != NULL, NULL); + g_return_val_if_fail (self->context != NULL, NULL); + g_return_val_if_fail (self->id != 0, NULL); + g_return_val_if_fail (self->width > 0, NULL); + g_return_val_if_fail (self->height > 0, NULL); + + return gdk_gl_texture_new_from_builder (self, destroy, data); +} + diff --git a/gdk/gdkgltexturebuilder.h b/gdk/gdkgltexturebuilder.h new file mode 100644 index 0000000000..03ee185e0c --- /dev/null +++ b/gdk/gdkgltexturebuilder.h @@ -0,0 +1,87 @@ +/* + * Copyright © 2023 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: Benjamin Otte <otte@gnome.org> + */ + +#pragma once + +#if !defined (__GDK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only <gdk/gdk.h> can be included directly." +#endif + +#include <gdk/gdkenums.h> +#include <gdk/gdktypes.h> +#include <gdk/gdkversionmacros.h> + +G_BEGIN_DECLS + +#define GDK_TYPE_GL_TEXTURE_BUILDER (gdk_gl_texture_builder_get_type ()) +GDK_AVAILABLE_IN_4_12 +GDK_DECLARE_INTERNAL_TYPE (GdkGLTextureBuilder, gdk_gl_texture_builder, GDK, GL_TEXTURE_BUILDER, GObject) + +GDK_AVAILABLE_IN_4_12 +GdkGLTextureBuilder * gdk_gl_texture_builder_new (void); + +GDK_AVAILABLE_IN_4_12 +GdkGLContext * gdk_gl_texture_builder_get_context (GdkGLTextureBuilder *self) G_GNUC_PURE; +GDK_AVAILABLE_IN_4_12 +void gdk_gl_texture_builder_set_context (GdkGLTextureBuilder *self, + GdkGLContext *context); + +GDK_AVAILABLE_IN_4_12 +guint gdk_gl_texture_builder_get_id (GdkGLTextureBuilder *self) G_GNUC_PURE; +GDK_AVAILABLE_IN_4_12 +void gdk_gl_texture_builder_set_id (GdkGLTextureBuilder *self, + guint id); + +GDK_AVAILABLE_IN_4_12 +int gdk_gl_texture_builder_get_width (GdkGLTextureBuilder *self) G_GNUC_PURE; +GDK_AVAILABLE_IN_4_12 +void gdk_gl_texture_builder_set_width (GdkGLTextureBuilder *self, + int width); + +GDK_AVAILABLE_IN_4_12 +int gdk_gl_texture_builder_get_height (GdkGLTextureBuilder *self) G_GNUC_PURE; +GDK_AVAILABLE_IN_4_12 +void gdk_gl_texture_builder_set_height (GdkGLTextureBuilder *self, + int height); + +GDK_AVAILABLE_IN_4_12 +GdkMemoryFormat gdk_gl_texture_builder_get_format (GdkGLTextureBuilder *self) G_GNUC_PURE; +GDK_AVAILABLE_IN_4_12 +void gdk_gl_texture_builder_set_format (GdkGLTextureBuilder *self, + GdkMemoryFormat format); + +GDK_AVAILABLE_IN_4_12 +gboolean gdk_gl_texture_builder_get_has_mipmap (GdkGLTextureBuilder *self) G_GNUC_PURE; +GDK_AVAILABLE_IN_4_12 +void gdk_gl_texture_builder_set_has_mipmap (GdkGLTextureBuilder *self, + gboolean has_mipmap); + +GDK_AVAILABLE_IN_4_12 +gpointer gdk_gl_texture_builder_get_sync (GdkGLTextureBuilder *self) G_GNUC_PURE; +GDK_AVAILABLE_IN_4_12 +void gdk_gl_texture_builder_set_sync (GdkGLTextureBuilder *self, + gpointer sync); + +GDK_AVAILABLE_IN_4_12 +GdkTexture * gdk_gl_texture_builder_build (GdkGLTextureBuilder *self, + GDestroyNotify destroy, + gpointer data); + +G_END_DECLS + diff --git a/gdk/gdkgltextureprivate.h b/gdk/gdkgltextureprivate.h index 47fa71bd0a..c2841fb55e 100644 --- a/gdk/gdkgltextureprivate.h +++ b/gdk/gdkgltextureprivate.h @@ -2,13 +2,20 @@ #include "gdkgltexture.h" +#include "gdkgltexturebuilder.h" #include "gdktextureprivate.h" G_BEGIN_DECLS +GdkTexture * gdk_gl_texture_new_from_builder (GdkGLTextureBuilder *builder, + GDestroyNotify destroy, + gpointer data); + + GdkGLContext * gdk_gl_texture_get_context (GdkGLTexture *self); guint gdk_gl_texture_get_id (GdkGLTexture *self); gboolean gdk_gl_texture_has_mipmap (GdkGLTexture *self); +gpointer gdk_gl_texture_get_sync (GdkGLTexture *self); G_END_DECLS diff --git a/gdk/meson.build b/gdk/meson.build index 0e97663c08..c83af4873d 100644 --- a/gdk/meson.build +++ b/gdk/meson.build @@ -29,6 +29,7 @@ gdk_public_sources = files([ 'gdkglcontext.c', 'gdkglobals.c', 'gdkgltexture.c', + 'gdkgltexturebuilder.c', 'gdkhsla.c', 'gdkkeys.c', 'gdkkeyuni.c', @@ -86,6 +87,7 @@ gdk_public_headers = files([ 'gdkframetimings.h', 'gdkglcontext.h', 'gdkgltexture.h', + 'gdkgltexturebuilder.h', 'gdkkeys.h', 'gdkkeysyms.h', 'gdkmemorytexture.h', diff --git a/gsk/gl/gskglcommandqueue.c b/gsk/gl/gskglcommandqueue.c index d286c001ba..774b957785 100644 --- a/gsk/gl/gskglcommandqueue.c +++ b/gsk/gl/gskglcommandqueue.c @@ -427,6 +427,7 @@ gsk_gl_command_queue_dispose (GObject *object) gsk_gl_command_batches_clear (&self->batches); gsk_gl_command_binds_clear (&self->batch_binds); gsk_gl_command_uniforms_clear (&self->batch_uniforms); + gsk_gl_syncs_clear (&self->syncs); gsk_gl_buffer_destroy (&self->vertices); @@ -449,6 +450,7 @@ gsk_gl_command_queue_init (GskGLCommandQueue *self) gsk_gl_command_batches_init (&self->batches, 128); gsk_gl_command_binds_init (&self->batch_binds, 1024); gsk_gl_command_uniforms_init (&self->batch_uniforms, 2048); + gsk_gl_syncs_init (&self->syncs, 10); gsk_gl_buffer_init (&self->vertices, GL_ARRAY_BUFFER, sizeof (GskGLDrawVertex)); } @@ -1159,17 +1161,25 @@ gsk_gl_command_queue_execute (GskGLCommandQueue *self, if G_UNLIKELY (batch->draw.bind_count > 0) { const GskGLCommandBind *bind = &self->batch_binds.items[batch->draw.bind_offset]; - for (guint i = 0; i < batch->draw.bind_count; i++) { if (textures[bind->texture] != bind->id) { + GskGLSync *s; + if (active != bind->texture) { active = bind->texture; glActiveTexture (GL_TEXTURE0 + bind->texture); } + s = gsk_gl_syncs_get_sync (&self->syncs, bind->id); + if (s && s->sync) + { + glWaitSync ((GLsync) s->sync, 0, GL_TIMEOUT_IGNORED); + s->sync = NULL; + } + glBindTexture (GL_TEXTURE_2D, bind->id); textures[bind->texture] = bind->id; if (!self->has_samplers) @@ -1315,6 +1325,7 @@ gsk_gl_command_queue_end_frame (GskGLCommandQueue *self) self->batches.len = 0; self->batch_binds.len = 0; self->batch_uniforms.len = 0; + self->syncs.len = 0; self->n_uploads = 0; self->tail_batch_index = -1; self->in_frame = FALSE; diff --git a/gsk/gl/gskglcommandqueueprivate.h b/gsk/gl/gskglcommandqueueprivate.h index fd72f68aff..df3afb1eea 100644 --- a/gsk/gl/gskglcommandqueueprivate.h +++ b/gsk/gl/gskglcommandqueueprivate.h @@ -168,9 +168,15 @@ typedef union _GskGLCommandBatch G_STATIC_ASSERT (sizeof (GskGLCommandBatch) == 32); +typedef struct _GskGLSync { + guint id; + gpointer sync; +} GskGLSync; + DEFINE_INLINE_ARRAY (GskGLCommandBatches, gsk_gl_command_batches, GskGLCommandBatch) DEFINE_INLINE_ARRAY (GskGLCommandBinds, gsk_gl_command_binds, GskGLCommandBind) DEFINE_INLINE_ARRAY (GskGLCommandUniforms, gsk_gl_command_uniforms, GskGLCommandUniform) +DEFINE_INLINE_ARRAY (GskGLSyncs, gsk_gl_syncs, GskGLSync) struct _GskGLCommandQueue { @@ -233,6 +239,10 @@ struct _GskGLCommandQueue */ GLuint samplers[GSK_GL_N_FILTERS * GSK_GL_N_FILTERS]; + /* Array of sync objects to wait on. + */ + GskGLSyncs syncs; + /* Discovered max texture size when loading the command queue so that we * can either scale down or slice textures to fit within this size. Assumed * to be both height and width. @@ -371,5 +381,36 @@ gsk_gl_command_queue_bind_framebuffer (GskGLCommandQueue *self, return ret; } +static inline GskGLSync * +gsk_gl_syncs_get_sync (GskGLSyncs *syncs, + guint id) +{ + for (unsigned int i = 0; i < syncs->len; i++) + { + GskGLSync *sync = &syncs->items[i]; + if (sync->id == id) + return sync; + } + return NULL; +} + +static inline void +gsk_gl_syncs_add_sync (GskGLSyncs *syncs, + guint id, + gpointer sync) +{ + GskGLSync *s; + + s = gsk_gl_syncs_get_sync (syncs, id); + if (s) + g_assert (s->sync == sync); + else + { + s = gsk_gl_syncs_append (syncs); + s->id = id; + s->sync = sync; + } +} + G_END_DECLS diff --git a/gsk/gl/gskgldriver.c b/gsk/gl/gskgldriver.c index 672f44a5ee..d0af5e76da 100644 --- a/gsk/gl/gskgldriver.c +++ b/gsk/gl/gskgldriver.c @@ -1126,14 +1126,20 @@ write_atlas_to_png (GskGLDriver *driver, GskGLTextureAtlas *atlas, const char *filename) { + GdkGLTextureBuilder *builder; GdkTexture *texture; - texture = gdk_gl_texture_new (gsk_gl_driver_get_context (driver), - atlas->texture_id, - atlas->width, atlas->height, - NULL, NULL); + builder = gdk_gl_texture_builder_new (); + gdk_gl_texture_builder_set_context (builder, gsk_gl_driver_get_context (driver)); + gdk_gl_texture_builder_set_id (builder, atlas->texture_id); + gdk_gl_texture_builder_set_width (builder, atlas->width); + gdk_gl_texture_builder_set_height (builder, atlas->height); + + texture = gdk_gl_texture_builder_build (builder, NULL, NULL); gdk_texture_save_to_png (texture, filename); + g_object_unref (texture); + g_object_unref (builder); } void @@ -1557,6 +1563,7 @@ typedef struct _GskGLTextureState { GdkGLContext *context; GLuint texture_id; + GLsync sync; } GskGLTextureState; static void @@ -1569,6 +1576,8 @@ create_texture_from_texture_destroy (gpointer data) gdk_gl_context_make_current (state->context); glDeleteTextures (1, &state->texture_id); + if (state->sync) + glDeleteSync (state->sync); g_clear_object (&state->context); g_free (state); } @@ -1578,8 +1587,9 @@ gsk_gl_driver_create_gdk_texture (GskGLDriver *self, guint texture_id) { GskGLTextureState *state; + GdkGLTextureBuilder *builder; GskGLTexture *texture; - int width, height; + GdkTexture *result; g_return_val_if_fail (GSK_IS_GL_DRIVER (self), NULL); g_return_val_if_fail (self->command_queue != NULL, NULL); @@ -1594,19 +1604,25 @@ gsk_gl_driver_create_gdk_texture (GskGLDriver *self, state = g_new0 (GskGLTextureState, 1); state->texture_id = texture_id; state->context = g_object_ref (self->command_queue->context); + if (gdk_gl_context_has_sync (self->command_queue->context)) + state->sync = glFenceSync (GL_SYNC_GPU_COMMANDS_COMPLETE, 0); g_hash_table_steal (self->textures, GUINT_TO_POINTER (texture_id)); - width = texture->width; - height = texture->height; + builder = gdk_gl_texture_builder_new (); + gdk_gl_texture_builder_set_context (builder, self->command_queue->context); + gdk_gl_texture_builder_set_id (builder, texture_id); + gdk_gl_texture_builder_set_width (builder, texture->width); + gdk_gl_texture_builder_set_height (builder, texture->height); + gdk_gl_texture_builder_set_sync (builder, state->sync); + + result = gdk_gl_texture_builder_build (builder, + create_texture_from_texture_destroy, + state); texture->texture_id = 0; gsk_gl_texture_free (texture); + g_object_unref (builder); - return gdk_gl_texture_new (self->command_queue->context, - texture_id, - width, - height, - create_texture_from_texture_destroy, - state); + return result; } diff --git a/gsk/gl/gskglprogramprivate.h b/gsk/gl/gskglprogramprivate.h index 50c191eb4b..6bfbe48f02 100644 --- a/gsk/gl/gskglprogramprivate.h +++ b/gsk/gl/gskglprogramprivate.h @@ -286,6 +286,22 @@ gsk_gl_program_set_uniform_texture (GskGLProgram *self, } static inline void +gsk_gl_program_set_uniform_texture_with_sync (GskGLProgram *self, + guint key, + guint stamp, + GLenum texture_target, + GLenum texture_slot, + guint texture_id, + GLint min_filter, + GLint max_filter, + gpointer sync) +{ + gsk_gl_program_set_uniform_texture_with_filter (self, key, stamp, texture_target, texture_slot, texture_id, + min_filter, max_filter); + gsk_gl_syncs_add_sync (&self->driver->command_queue->syncs, texture_id, sync); +} + +static inline void gsk_gl_program_set_uniform_matrix (GskGLProgram *self, guint key, guint stamp, diff --git a/gsk/gl/gskglrenderjob.c b/gsk/gl/gskglrenderjob.c index 1eb82e80da..051619528c 100644 --- a/gsk/gl/gskglrenderjob.c +++ b/gsk/gl/gskglrenderjob.c @@ -191,6 +191,7 @@ typedef struct _GskGLRenderOffscreen /* Return location for texture ID */ guint texture_id; + gpointer sync; /* Whether to force creating a new texture, even if the * input already is a texture @@ -3570,6 +3571,9 @@ gsk_gl_render_job_upload_texture (GskGLRenderJob *job, offscreen->texture_id = gsk_gl_driver_load_texture (job->driver, texture, ensure_mipmap); init_full_texture_region (offscreen); offscreen->has_mipmap = ensure_mipmap; + + if (gl_texture && offscreen->texture_id == gdk_gl_texture_get_id (gl_texture)) + offscreen->sync = gdk_gl_texture_get_sync (gl_texture); } } @@ -3597,13 +3601,15 @@ gsk_gl_render_job_visit_texture (GskGLRenderJob *job, g_assert (offscreen.was_offscreen == FALSE); gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit)); - gsk_gl_program_set_uniform_texture_with_filter (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - offscreen.texture_id, - offscreen.has_mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR, - GL_LINEAR); + + gsk_gl_program_set_uniform_texture_with_sync (job->current_program, + UNIFORM_SHARED_SOURCE, 0, + GL_TEXTURE_2D, + GL_TEXTURE0, + offscreen.texture_id, + offscreen.has_mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR, + GL_LINEAR, + offscreen.sync); gsk_gl_render_job_draw_offscreen (job, bounds, &offscreen); gsk_gl_render_job_end_draw (job); } @@ -3733,21 +3739,29 @@ gsk_gl_render_job_visit_texture_scale_node (GskGLRenderJob *job, if G_LIKELY (texture->width <= max_texture_size && texture->height <= max_texture_size) { + gpointer sync; + texture_id = gsk_gl_driver_load_texture (job->driver, texture, filter == GSK_SCALING_FILTER_TRILINEAR); + if (GDK_IS_GL_TEXTURE (texture) && texture_id == gdk_gl_texture_get_id (GDK_GL_TEXTURE (texture))) + sync = gdk_gl_texture_get_sync (GDK_GL_TEXTURE (texture)); + else + sync = NULL; + u0 = (clip_rect.origin.x - bounds->origin.x) / bounds->size.width; v0 = (clip_rect.origin.y - bounds->origin.y) / bounds->size.height; u1 = (clip_rect.origin.x + clip_rect.size.width - bounds->origin.x) / bounds->size.width; v1 = (clip_rect.origin.y + clip_rect.size.height - bounds->origin.y) / bounds->size.height; gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit)); - gsk_gl_program_set_uniform_texture_with_filter (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - texture_id, - min_filter, - mag_filter); + gsk_gl_program_set_uniform_texture_with_sync (job->current_program, + UNIFORM_SHARED_SOURCE, 0, + GL_TEXTURE_2D, + GL_TEXTURE0, + texture_id, + min_filter, + mag_filter, + sync); gsk_gl_render_job_draw_coords (job, 0, 0, clip_rect.size.width, clip_rect.size.height, u0, v0, u1, v1, diff --git a/gtk/gtkglarea.c b/gtk/gtkglarea.c index 14f353cb28..f547a4995f 100644 --- a/gtk/gtkglarea.c +++ b/gtk/gtkglarea.c @@ -32,6 +32,8 @@ #include "gtksnapshot.h" #include "gtkrenderlayoutprivate.h" #include "gtkcssnodeprivate.h" +#include "gdk/gdkgltextureprivate.h" +#include "gdk/gdkglcontextprivate.h" #include <epoxy/gl.h> @@ -143,9 +145,7 @@ */ typedef struct { - guint id; - int width; - int height; + GdkGLTextureBuilder *builder; GdkTexture *holder; } Texture; @@ -403,15 +403,16 @@ static void delete_one_texture (gpointer data) { Texture *texture = data; + guint id; if (texture->holder) gdk_gl_texture_release (GDK_GL_TEXTURE (texture->holder)); - if (texture->id != 0) - { - glDeleteTextures (1, &texture->id); - texture->id = 0; - } + id = gdk_gl_texture_builder_get_id (texture->builder); + if (id != 0) + glDeleteTextures (1, &id); + + g_object_unref (texture->builder); g_free (texture); } @@ -452,13 +453,20 @@ gtk_gl_area_ensure_texture (GtkGLArea *area) if (priv->texture == NULL) { - priv->texture = g_new (Texture, 1); + GLuint id; - priv->texture->width = 0; - priv->texture->height = 0; + priv->texture = g_new (Texture, 1); priv->texture->holder = NULL; - glGenTextures (1, &priv->texture->id); + priv->texture->builder = gdk_gl_texture_builder_new (); + gdk_gl_texture_builder_set_context (priv->texture->builder, priv->context); + if (gdk_gl_context_get_api (priv->context) == GDK_GL_API_GLES) + gdk_gl_texture_builder_set_format (priv->texture->builder, GDK_MEMORY_R8G8B8A8_PREMULTIPLIED); + else + gdk_gl_texture_builder_set_format (priv->texture->builder, GDK_MEMORY_B8G8R8A8_PREMULTIPLIED); + + glGenTextures (1, &id); + gdk_gl_texture_builder_set_id (priv->texture->builder, id); } gtk_gl_area_allocate_texture (area); @@ -512,10 +520,10 @@ gtk_gl_area_allocate_texture (GtkGLArea *area) width = gtk_widget_get_width (widget) * scale; height = gtk_widget_get_height (widget) * scale; - if (priv->texture->width != width || - priv->texture->height != height) + if (gdk_gl_texture_builder_get_width (priv->texture->builder) != width || + gdk_gl_texture_builder_get_height (priv->texture->builder) != height) { - glBindTexture (GL_TEXTURE_2D, priv->texture->id); + glBindTexture (GL_TEXTURE_2D, gdk_gl_texture_builder_get_id (priv->texture->builder)); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -526,8 +534,8 @@ gtk_gl_area_allocate_texture (GtkGLArea *area) else glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); - priv->texture->width = width; - priv->texture->height = height; + gdk_gl_texture_builder_set_width (priv->texture->builder, width); + gdk_gl_texture_builder_set_height (priv->texture->builder, height); } } @@ -571,7 +579,7 @@ gtk_gl_area_attach_buffers (GtkGLArea *area) if (priv->texture != NULL) glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, priv->texture->id, 0); + GL_TEXTURE_2D, gdk_gl_texture_builder_get_id (priv->texture->builder), 0); if (priv->depth_stencil_buffer) { @@ -691,6 +699,15 @@ static void release_texture (gpointer data) { Texture *texture = data; + gpointer sync; + + sync = gdk_gl_texture_builder_get_sync (texture->builder); + if (sync) + { + glDeleteSync (sync); + gdk_gl_texture_builder_set_sync (texture->builder, NULL); + } + texture->holder = NULL; } @@ -736,6 +753,7 @@ gtk_gl_area_snapshot (GtkWidget *widget, if (status == GL_FRAMEBUFFER_COMPLETE) { Texture *texture; + gpointer sync = NULL; if (priv->needs_render || priv->auto_render) { @@ -754,11 +772,14 @@ gtk_gl_area_snapshot (GtkWidget *widget, priv->texture = NULL; priv->textures = g_list_prepend (priv->textures, texture); - texture->holder = gdk_gl_texture_new (priv->context, - texture->id, - texture->width, - texture->height, - release_texture, texture); + if (gdk_gl_context_has_sync (priv->context)) + sync = glFenceSync (GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + + gdk_gl_texture_builder_set_sync (texture->builder, sync); + + texture->holder = gdk_gl_texture_builder_build (texture->builder, + release_texture, + texture); /* Our texture is rendered by OpenGL, so it is upside down, * compared to what GSK expects, so flip it back. diff --git a/modules/media/gtkgstsink.c b/modules/media/gtkgstsink.c index 552c23c23d..aee5cc452a 100644 --- a/modules/media/gtkgstsink.c +++ b/modules/media/gtkgstsink.c @@ -287,21 +287,30 @@ gtk_gst_sink_texture_from_buffer (GtkGstSink *self, gst_video_frame_map (frame, &self->v_info, buffer, GST_MAP_READ | GST_MAP_GL)) { GstGLSyncMeta *sync_meta; + GdkGLTextureBuilder *builder; sync_meta = gst_buffer_get_gl_sync_meta (buffer); - if (sync_meta) { + if (sync_meta) gst_gl_sync_meta_set_sync_point (sync_meta, self->gst_context); - gst_gl_context_activate (self->gst_gdk_context, TRUE); - gst_gl_sync_meta_wait (sync_meta, self->gst_gdk_context); - gst_gl_context_activate (self->gst_gdk_context, FALSE); - } - - texture = gdk_gl_texture_new (self->gdk_context, - *(guint *) frame->data[0], - frame->info.width, - frame->info.height, - (GDestroyNotify) video_frame_free, - frame); + + /* Note: using the gdk_context here is a (harmless) lie, + * since the texture really originates in the gst_context. + * But that is not a GdkGLContext. It is harmless, because + * we are never using the texture in the gdk_context, so we + * never make the (erroneous) decision to ignore the sync. + */ + builder = gdk_gl_texture_builder_new (); + gdk_gl_texture_builder_set_context (builder, self->gdk_context); + gdk_gl_texture_builder_set_id (builder, *(guint *) frame->data[0]); + gdk_gl_texture_builder_set_width (builder, frame->info.width); + gdk_gl_texture_builder_set_height (builder, frame->info.height); + gdk_gl_texture_builder_set_sync (builder, sync_meta ? sync_meta->data : NULL); + + texture = gdk_gl_texture_builder_build (builder, + (GDestroyNotify) video_frame_free, + frame); + + g_object_unref (builder); *pixel_aspect_ratio = ((double) frame->info.par_n) / ((double) frame->info.par_d); } diff --git a/testsuite/gdk/gltexture.c b/testsuite/gdk/gltexture.c index 475667daef..1b964e7c75 100644 --- a/testsuite/gdk/gltexture.c +++ b/testsuite/gdk/gltexture.c @@ -32,6 +32,7 @@ test_gltexture (int test) GdkDisplay *display; GdkGLContext *context; GdkGLContext *context2 = NULL; + GdkGLTextureBuilder *builder; GdkTexture *texture; cairo_surface_t *surface; GError *error = NULL; @@ -80,7 +81,12 @@ test_gltexture (int test) gdk_gl_context_make_current (context2); } - texture = gdk_gl_texture_new (context, id, 64, 64, NULL, NULL); + builder = gdk_gl_texture_builder_new (); + gdk_gl_texture_builder_set_id (builder, id); + gdk_gl_texture_builder_set_context (builder, context); + gdk_gl_texture_builder_set_width (builder, 64); + gdk_gl_texture_builder_set_height (builder, 64); + texture = gdk_gl_texture_builder_build (builder, NULL, NULL); data = g_malloc0 (64 * 64 * 4); gdk_texture_download (texture, data, 64 * 4); @@ -89,6 +95,7 @@ test_gltexture (int test) g_free (data); g_object_unref (texture); + g_object_unref (builder); cairo_surface_destroy (surface); |