diff options
author | Matthias Clasen <mclasen@redhat.com> | 2018-01-17 19:15:14 -0500 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2018-01-17 19:15:14 -0500 |
commit | 4f50bf23d34debfdf442dc835c1abbcf51f8980b (patch) | |
tree | 80b85b3a62ad4574a0bdd1da7306349cfe4e634c | |
parent | 18dc994de731c8c5d58e7c9ad72379018944f38d (diff) | |
download | gtk+-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.
-rw-r--r-- | gtk/gtkglarea.c | 190 |
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 { |