summaryrefslogtreecommitdiff
path: root/gtk/gtkglarea.c
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2018-01-17 19:15:14 -0500
committerMatthias Clasen <mclasen@redhat.com>2018-01-17 19:15:14 -0500
commit4f50bf23d34debfdf442dc835c1abbcf51f8980b (patch)
tree80b85b3a62ad4574a0bdd1da7306349cfe4e634c /gtk/gtkglarea.c
parent18dc994de731c8c5d58e7c9ad72379018944f38d (diff)
downloadgtk+-4f50bf23d34debfdf442dc835c1abbcf51f8980b.tar.gz
glarea: Add a pool for GL textures
Handle the situation that a GL texture might remain in use (e.g. by a slow frame, or by the recorder) In that case, we can't modify it but must use a new one. Keep a pool of GL textures for this eventuality.
Diffstat (limited to 'gtk/gtkglarea.c')
-rw-r--r--gtk/gtkglarea.c190
1 files changed, 151 insertions, 39 deletions
diff --git a/gtk/gtkglarea.c b/gtk/gtkglarea.c
index 7c1452150a..efe4899cab 100644
--- a/gtk/gtkglarea.c
+++ b/gtk/gtkglarea.c
@@ -143,6 +143,13 @@
*/
typedef struct {
+ guint id;
+ int width;
+ int height;
+ GdkTexture *holder;
+} Texture;
+
+typedef struct {
GdkGLContext *context;
GError *error;
@@ -151,8 +158,9 @@ typedef struct {
int required_gl_version;
guint frame_buffer;
- guint texture;
guint depth_stencil_buffer;
+ Texture *texture;
+ GList *textures;
gboolean has_depth_buffer;
gboolean has_stencil_buffer;
@@ -187,6 +195,7 @@ enum {
};
static void gtk_gl_area_allocate_buffers (GtkGLArea *area);
+static void gtk_gl_area_allocate_texture (GtkGLArea *area);
static guint area_signals[LAST_SIGNAL] = { 0, };
@@ -367,11 +376,6 @@ gtk_gl_area_ensure_buffers (GtkGLArea *area)
glGenFramebuffersEXT (1, &priv->frame_buffer);
- if (priv->texture == 0)
- {
- glGenTextures (1, &priv->texture);
- }
-
if ((priv->has_depth_buffer || priv->has_stencil_buffer))
{
if (priv->depth_stencil_buffer == 0)
@@ -387,6 +391,67 @@ gtk_gl_area_ensure_buffers (GtkGLArea *area)
gtk_gl_area_allocate_buffers (area);
}
+static void
+delete_one_texture (gpointer data)
+{
+ Texture *texture = data;
+
+ if (texture->id != 0)
+ {
+ glDeleteTextures (1, &texture->id);
+ texture->id = 0;
+ }
+ g_free (texture);
+}
+
+static void
+gtk_gl_area_ensure_texture (GtkGLArea *area)
+{
+ GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
+ GtkWidget *widget = GTK_WIDGET (area);
+
+ gtk_widget_realize (widget);
+
+ if (priv->context == NULL)
+ return;
+
+ if (priv->texture == NULL)
+ {
+ GList *l, *link;
+
+ l = priv->textures;
+ while (l)
+ {
+ Texture *texture = l->data;
+ link = l;
+ l = l->next;
+
+ if (texture->holder)
+ continue;
+
+ priv->textures = g_list_delete_link (priv->textures, link);
+
+ if (priv->texture == NULL)
+ priv->texture = texture;
+ else
+ delete_one_texture (texture);
+ }
+ }
+
+ if (priv->texture == NULL)
+ {
+ priv->texture = g_new (Texture, 1);
+
+ priv->texture->width = 0;
+ priv->texture->height = 0;
+ priv->texture->holder = NULL;
+
+ glGenTextures (1, &priv->texture->id);
+ }
+
+ gtk_gl_area_allocate_texture (area);
+}
+
/*
* Allocates space of the right type and size for all the buffers
*/
@@ -404,9 +469,41 @@ gtk_gl_area_allocate_buffers (GtkGLArea *area)
width = gtk_widget_get_width (widget) * scale;
height = gtk_widget_get_height (widget) * scale;
- if (priv->texture)
+ if (priv->has_depth_buffer || priv->has_stencil_buffer)
{
- glBindTexture (GL_TEXTURE_2D, priv->texture);
+ glBindRenderbuffer (GL_RENDERBUFFER, priv->depth_stencil_buffer);
+ if (priv->has_stencil_buffer)
+ glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
+ else
+ glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
+ }
+
+ priv->needs_render = TRUE;
+}
+
+static void
+gtk_gl_area_allocate_texture (GtkGLArea *area)
+{
+ GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
+ GtkWidget *widget = GTK_WIDGET (area);
+ int scale, width, height;
+
+ if (priv->context == NULL)
+ return;
+
+ if (priv->texture == NULL)
+ return;
+
+ g_assert (priv->texture->holder == NULL);
+
+ scale = gtk_widget_get_scale_factor (widget);
+ width = gtk_widget_get_width (widget) * scale;
+ height = gtk_widget_get_height (widget) * scale;
+
+ if (priv->texture->width != width ||
+ priv->texture->height != height)
+ {
+ glBindTexture (GL_TEXTURE_2D, priv->texture->id);
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);
@@ -416,18 +513,10 @@ gtk_gl_area_allocate_buffers (GtkGLArea *area)
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
else
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
- }
- if (priv->has_depth_buffer || priv->has_stencil_buffer)
- {
- glBindRenderbuffer (GL_RENDERBUFFER, priv->depth_stencil_buffer);
- if (priv->has_stencil_buffer)
- glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
- else
- glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
+ priv->texture->width = width;
+ priv->texture->height = height;
}
-
- priv->needs_render = TRUE;
}
/**
@@ -456,6 +545,11 @@ gtk_gl_area_attach_buffers (GtkGLArea *area)
gtk_gl_area_make_current (area);
+ if (priv->texture == NULL)
+ gtk_gl_area_ensure_texture (area);
+ else if (priv->needs_resize)
+ gtk_gl_area_allocate_texture (area);
+
if (!priv->have_buffers)
gtk_gl_area_ensure_buffers (area);
else if (priv->needs_resize)
@@ -463,9 +557,9 @@ gtk_gl_area_attach_buffers (GtkGLArea *area)
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, priv->frame_buffer);
- if (priv->texture)
+ if (priv->texture != NULL)
glFramebufferTexture2D (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
- GL_TEXTURE_2D, priv->texture, 0);
+ GL_TEXTURE_2D, priv->texture->id, 0);
if (priv->depth_stencil_buffer)
{
@@ -488,12 +582,6 @@ gtk_gl_area_delete_buffers (GtkGLArea *area)
priv->have_buffers = FALSE;
- if (priv->texture != 0)
- {
- glDeleteTextures (1, &priv->texture);
- priv->texture = 0;
- }
-
if (priv->depth_stencil_buffer != 0)
{
glDeleteRenderbuffersEXT (1, &priv->depth_stencil_buffer);
@@ -509,6 +597,22 @@ gtk_gl_area_delete_buffers (GtkGLArea *area)
}
static void
+gtk_gl_area_delete_textures (GtkGLArea *area)
+{
+ GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area);
+
+ delete_one_texture (priv->texture);
+ priv->texture = NULL;
+
+ /* FIXME: we need to explicitly release all outstanding
+ * textures here, otherwise release_texture will get called
+ * later and access freed memory.
+ */
+ g_list_free_full (priv->textures, delete_one_texture);
+ priv->textures = NULL;
+}
+
+static void
gtk_gl_area_unrealize (GtkWidget *widget)
{
GtkGLArea *area = GTK_GL_AREA (widget);
@@ -522,6 +626,8 @@ gtk_gl_area_unrealize (GtkWidget *widget)
gtk_gl_area_delete_buffers (area);
}
+ gtk_gl_area_delete_textures (area);
+
/* Make sure to unset the context if current */
if (priv->context == gdk_gl_context_get_current ())
gdk_gl_context_clear_current ();
@@ -573,6 +679,13 @@ gtk_gl_area_draw_error_screen (GtkGLArea *area,
}
static void
+release_texture (gpointer data)
+{
+ Texture *texture = data;
+ texture->holder = NULL;
+}
+
+static void
gtk_gl_area_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
@@ -613,7 +726,7 @@ gtk_gl_area_snapshot (GtkWidget *widget,
status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
if (status == GL_FRAMEBUFFER_COMPLETE_EXT)
{
- GdkTexture *texture;
+ Texture *texture;
if (priv->needs_render || priv->auto_render)
{
@@ -628,25 +741,24 @@ gtk_gl_area_snapshot (GtkWidget *widget,
priv->needs_render = FALSE;
- /* FIXME: the texture may be kept in use for a longer time (eg by
- * the backend, or by the inspector, so we really need a pool of
- * GL textures here, and use a destroy notify on the texture to
- * put the GL texture back in the pool when it is no longer in use.
- */
- texture = gdk_texture_new_for_gl (priv->context,
- priv->texture,
- gtk_widget_get_width (widget),
- gtk_widget_get_height (widget),
- NULL, NULL);
+ texture = priv->texture;
+ priv->texture = NULL;
+ priv->textures = g_list_prepend (priv->textures, texture);
+
+ texture->holder = gdk_texture_new_for_gl (priv->context,
+ texture->id,
+ texture->width,
+ texture->height,
+ release_texture, texture);
gtk_snapshot_append_texture (snapshot,
- texture,
+ texture->holder,
&GRAPHENE_RECT_INIT (0, 0,
gtk_widget_get_width (widget),
gtk_widget_get_height (widget)),
"GL Area");
- g_object_unref (texture);
+ g_object_unref (texture->holder);
}
else
{