summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Otte <otte.benjamin@googlemail.com>2023-04-28 15:58:10 +0000
committerBenjamin Otte <otte.benjamin@googlemail.com>2023-04-28 15:58:10 +0000
commit76777cdd1801b450a0ba5fb6d8147045d888c866 (patch)
treefd198c14e9ecf01f8f4ed0124065ffefea331438
parentbdec7782b4d30da8482f9d1f6a330e3fd1af2e8d (diff)
parent92a9f8cd7edb029a18f0897c173adba7c6213a18 (diff)
downloadgtk+-76777cdd1801b450a0ba5fb6d8147045d888c866.tar.gz
Merge branch 'wip/otte/texturebuilder' into 'main'
Add GdkGLTextureBuilder See merge request GNOME/gtk!5862
-rw-r--r--gdk/gdk.h1
-rw-r--r--gdk/gdkglcontext.c19
-rw-r--r--gdk/gdkglcontextprivate.h2
-rw-r--r--gdk/gdkgltexture.c38
-rw-r--r--gdk/gdkgltexture.h2
-rw-r--r--gdk/gdkgltexturebuilder.c655
-rw-r--r--gdk/gdkgltexturebuilder.h87
-rw-r--r--gdk/gdkgltextureprivate.h7
-rw-r--r--gdk/meson.build2
-rw-r--r--gsk/gl/gskglcommandqueue.c13
-rw-r--r--gsk/gl/gskglcommandqueueprivate.h41
-rw-r--r--gsk/gl/gskgldriver.c42
-rw-r--r--gsk/gl/gskglprogramprivate.h16
-rw-r--r--gsk/gl/gskglrenderjob.c42
-rw-r--r--gtk/gtkglarea.c67
-rw-r--r--modules/media/gtkgstsink.c33
-rw-r--r--testsuite/gdk/gltexture.c9
17 files changed, 1009 insertions, 67 deletions
diff --git a/gdk/gdk.h b/gdk/gdk.h
index 49667bafce..4201b72a86 100644
--- a/gdk/gdk.h
+++ b/gdk/gdk.h
@@ -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);