[gtk/wip/chergert/glproto] add render target abstraction



commit 9e1eca9b06aed8f1b8c536471e5eddf4dc11c70e
Author: Christian Hergert <chergert redhat com>
Date:   Wed Jan 13 11:05:43 2021 -0800

    add render target abstraction
    
    this will make things a bit easier to manage and release without so much
    manual control over texture/framebuffer management

 gsk/next/gskgldriver.c        | 172 +++++++++++++++++++++++++++++++++---------
 gsk/next/gskgldriverprivate.h |  17 ++++-
 gsk/next/gskglrenderer.c      |  12 +--
 gsk/next/gskglrenderjob.c     |  77 ++++++++++---------
 gsk/next/gskgltypesprivate.h  |   2 +-
 5 files changed, 195 insertions(+), 85 deletions(-)
---
diff --git a/gsk/next/gskgldriver.c b/gsk/next/gskgldriver.c
index 64cb9894d4..4e9a2d7ddb 100644
--- a/gsk/next/gskgldriver.c
+++ b/gsk/next/gskgldriver.c
@@ -163,6 +163,7 @@ gsk_next_driver_dispose (GObject *object)
   g_clear_pointer (&self->textures, g_hash_table_unref);
   g_clear_pointer (&self->key_to_texture_id, g_hash_table_unref);
   g_clear_pointer (&self->texture_id_to_key, g_hash_table_unref);
+  g_clear_pointer (&self->render_targets, g_ptr_array_unref);
 
   G_OBJECT_CLASS (gsk_next_driver_parent_class)->dispose (object);
 }
@@ -186,6 +187,7 @@ gsk_next_driver_init (GskNextDriver *self)
                                                    g_free,
                                                    NULL);
   gsk_gl_texture_pool_init (&self->texture_pool);
+  self->render_targets = g_ptr_array_new ();
 }
 
 static gboolean
@@ -351,12 +353,23 @@ gsk_next_driver_end_frame (GskNextDriver *self)
   g_return_if_fail (GSK_IS_NEXT_DRIVER (self));
   g_return_if_fail (self->in_frame == TRUE);
 
+  gdk_gl_context_make_current (self->command_queue->context);
+
   gsk_gl_command_queue_end_frame (self->command_queue);
 
   gsk_gl_texture_library_end_frame (GSK_GL_TEXTURE_LIBRARY (self->icons));
   gsk_gl_texture_library_end_frame (GSK_GL_TEXTURE_LIBRARY (self->glyphs));
   gsk_gl_texture_library_end_frame (GSK_GL_TEXTURE_LIBRARY (self->shadows));
 
+  for (guint i = 0; i < self->render_targets->len; i++)
+    {
+      GskGLRenderTarget *render_target = g_ptr_array_index (self->render_targets, i);
+
+      gsk_next_driver_autorelease_framebuffer (self, render_target->framebuffer_id);
+      glDeleteTextures (1, &render_target->texture_id);
+      g_slice_free (GskGLRenderTarget, render_target);
+    }
+
   if (self->autorelease_framebuffers->len > 0)
     {
       glDeleteFramebuffers (self->autorelease_framebuffers->len,
@@ -378,43 +391,6 @@ gsk_next_driver_get_context (GskNextDriver *self)
   return gsk_gl_command_queue_get_context (self->command_queue);
 }
 
-/**
- * gsk_next_driver_create_render_target:
- * @self: a #GskNextDriver
- * @width: the width for the render target
- * @height: the height for the render target
- * @min_filter: the min filter to use for the texture
- * @mag_filter: the mag filter to use for the texture
- * @out_fbo_id: (out): location for framebuffer id
- * @out_texture_id: (out): location for texture id
- *
- * Creates a new render target where @out_texture_id is bound
- * to the framebuffer @out_fbo_id using glFramebufferTexture2D().
- *
- * Returns: %TRUE if successful; otherwise %FALSE and @out_fbo_id and
- *   @out_texture_id are undefined.
- */
-gboolean
-gsk_next_driver_create_render_target (GskNextDriver *self,
-                                      int            width,
-                                      int            height,
-                                      int            min_filter,
-                                      int            mag_filter,
-                                      guint         *out_fbo_id,
-                                      guint         *out_texture_id)
-{
-  g_return_val_if_fail (GSK_IS_NEXT_DRIVER (self), FALSE);
-  g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self->command_queue), FALSE);
-
-  return gsk_gl_command_queue_create_render_target (self->command_queue,
-                                                    width,
-                                                    height,
-                                                    min_filter,
-                                                    mag_filter,
-                                                    out_fbo_id,
-                                                    out_texture_id);
-}
-
 /**
  * gsk_next_driver_lookup_texture:
  * @self: a #GskNextDriver
@@ -682,3 +658,125 @@ gsk_next_driver_release_texture (GskNextDriver *self,
 
   gsk_gl_texture_pool_put (&self->texture_pool, texture);
 }
+
+/**
+ * gsk_next_driver_create_render_target:
+ * @self: a #GskNextDriver
+ * @width: the width for the render target
+ * @height: the height for the render target
+ * @min_filter: the min filter to use for the texture
+ * @mag_filter: the mag filter to use for the texture
+ * @out_render_target: (out): a location for the render target
+ *
+ * Creates a new render target which contains a framebuffer and a texture
+ * bound to that framebuffer of the size @width x @height and using the
+ * appropriate filters.
+ *
+ * Use gsk_next_driver_release_render_target() when you are finished with
+ * the render target to release it. You may steal the texture from the
+ * render target when releasing it.
+ *
+ * Returns: %TRUE if successful; otherwise %FALSE and @out_fbo_id and
+ *   @out_texture_id are undefined.
+ */
+gboolean
+gsk_next_driver_create_render_target (GskNextDriver      *self,
+                                      int                 width,
+                                      int                 height,
+                                      int                 min_filter,
+                                      int                 mag_filter,
+                                      GskGLRenderTarget **out_render_target)
+{
+  guint framebuffer_id;
+  guint texture_id;
+
+  g_return_val_if_fail (GSK_IS_NEXT_DRIVER (self), FALSE);
+  g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self->command_queue), FALSE);
+  g_return_val_if_fail (out_render_target != NULL, FALSE);
+
+  if (self->render_targets->len > 0)
+    {
+      for (guint i = self->render_targets->len; i > 0; i--)
+        {
+          GskGLRenderTarget *render_target = g_ptr_array_index (self->render_targets, i-1);
+
+          if (render_target->width == width &&
+              render_target->height == height &&
+              render_target->min_filter == min_filter &&
+              render_target->mag_filter == mag_filter)
+            {
+              *out_render_target = g_ptr_array_steal_index_fast (self->render_targets, i-1);
+              return TRUE;
+            }
+        }
+    }
+
+  if (gsk_gl_command_queue_create_render_target (self->command_queue,
+                                                 width, height,
+                                                 min_filter, mag_filter,
+                                                 &framebuffer_id, &texture_id))
+    {
+      GskGLRenderTarget *render_target;
+
+      render_target = g_slice_new0 (GskGLRenderTarget);
+      render_target->min_filter = min_filter;
+      render_target->mag_filter = mag_filter;
+      render_target->width = width;
+      render_target->height = height;
+      render_target->framebuffer_id = framebuffer_id;
+      render_target->texture_id = texture_id;
+
+      *out_render_target = render_target;
+
+      return TRUE;
+    }
+
+  *out_render_target = NULL;
+
+  return FALSE;
+}
+
+/**
+ * gsk_next_driver_release_render_target:
+ * @self: a #GskNextDriver
+ * @render_target: a #GskGLRenderTarget created with
+ *   gsk_next_driver_create_render_target().
+ * @release_texture: if the texture should also be released
+ *
+ * Releases a render target that was previously created. An attempt may
+ * be made to cache the render target so that future creations of render
+ * targets are performed faster.
+ *
+ * If @release_texture is %FALSE, the backing texture id is returned and
+ * the framebuffer is released. Otherwise, both the texture and framebuffer
+ * are released or cached until the end of the frame.
+ *
+ * This may be called when building the render job as the texture or
+ * framebuffer will not be removed immediately.
+ *
+ * Returns: a texture id if @release_texture is %FALSE, otherwise zero.
+ */
+guint
+gsk_next_driver_release_render_target (GskNextDriver     *self,
+                                       GskGLRenderTarget *render_target,
+                                       gboolean           release_texture)
+{
+  guint texture_id;
+
+  g_return_val_if_fail (GSK_IS_NEXT_DRIVER (self), 0);
+  g_return_val_if_fail (render_target != NULL, 0);
+
+  if (release_texture)
+    {
+      texture_id = 0;
+      g_ptr_array_add (self->render_targets, render_target);
+    }
+  else
+    {
+      texture_id = render_target->texture_id;
+      gsk_next_driver_autorelease_framebuffer (self, render_target->framebuffer_id);
+      g_slice_free (GskGLRenderTarget, render_target);
+    }
+
+  return texture_id;
+}
diff --git a/gsk/next/gskgldriverprivate.h b/gsk/next/gskgldriverprivate.h
index 587c3d1e42..13f4429891 100644
--- a/gsk/next/gskgldriverprivate.h
+++ b/gsk/next/gskgldriverprivate.h
@@ -59,6 +59,16 @@ typedef struct {
 
 G_DECLARE_FINAL_TYPE (GskNextDriver, gsk_next_driver, GSK, NEXT_DRIVER, GObject)
 
+struct _GskGLRenderTarget
+{
+  guint framebuffer_id;
+  guint texture_id;
+  int min_filter;
+  int mag_filter;
+  int width;
+  int height;
+};
+
 struct _GskNextDriver
 {
   GObject parent_instance;
@@ -77,6 +87,7 @@ struct _GskNextDriver
   GHashTable *texture_id_to_key;
 
   GArray *autorelease_framebuffers;
+  GPtrArray *render_targets;
 
 #define GSK_GL_NO_UNIFORMS
 #define GSK_GL_ADD_UNIFORM(pos, KEY, name)
@@ -103,8 +114,10 @@ gboolean       gsk_next_driver_create_render_target    (GskNextDriver        *se
                                                         int                   height,
                                                         int                   min_filter,
                                                         int                   mag_filter,
-                                                        guint                *out_fbo_id,
-                                                        guint                *out_texture_id);
+                                                        GskGLRenderTarget   **render_target);
+guint          gsk_next_driver_release_render_target   (GskNextDriver        *self,
+                                                        GskGLRenderTarget    *render_target,
+                                                        gboolean              release_texture);
 void           gsk_next_driver_begin_frame             (GskNextDriver        *self);
 void           gsk_next_driver_end_frame               (GskNextDriver        *self);
 guint          gsk_next_driver_lookup_texture          (GskNextDriver        *self,
diff --git a/gsk/next/gskglrenderer.c b/gsk/next/gskglrenderer.c
index b7b1b13811..430c92ddbc 100644
--- a/gsk/next/gskglrenderer.c
+++ b/gsk/next/gskglrenderer.c
@@ -244,10 +244,10 @@ gsk_gl_renderer_render_texture (GskRenderer           *renderer,
                                 const graphene_rect_t *viewport)
 {
   GskNextRenderer *self = (GskNextRenderer *)renderer;
+  GskGLRenderTarget *render_target;
   GskGLRenderJob *job;
   GdkGLContext *context;
-  GLuint fbo_id;
-  GLuint texture_id;
+  guint texture_id;
   int width;
   int height;
 
@@ -263,15 +263,15 @@ gsk_gl_renderer_render_texture (GskRenderer           *renderer,
   if (!gsk_next_driver_create_render_target (self->driver,
                                              width, height,
                                              GL_NEAREST, GL_NEAREST,
-                                             &fbo_id, &texture_id))
+                                             &render_target))
     return NULL;
 
-  gsk_next_driver_autorelease_framebuffer (self->driver, fbo_id);
-
-  job = gsk_gl_render_job_new (self->driver, viewport, 1, NULL, fbo_id);
+  job = gsk_gl_render_job_new (self->driver, viewport, 1, NULL, render_target->framebuffer_id);
   gsk_gl_render_job_render_flipped (job, root);
   gsk_gl_render_job_free (job);
 
+  texture_id = gsk_next_driver_release_render_target (self->driver, render_target, FALSE);
+
   return create_texture_from_texture (context, texture_id, width, height);
 }
 
diff --git a/gsk/next/gskglrenderjob.c b/gsk/next/gskglrenderjob.c
index e8e9baacec..87527a3688 100644
--- a/gsk/next/gskglrenderjob.c
+++ b/gsk/next/gskglrenderjob.c
@@ -1799,8 +1799,8 @@ blur_offscreen (GskGLRenderJob       *job,
                 float                 blur_radius_y)
 {
   const GskRoundedRect new_clip = GSK_ROUNDED_RECT_INIT (0, 0, texture_to_blur_width, 
texture_to_blur_height);
-  guint pass1_texture_id, pass1_framebuffer_id;
-  guint pass2_texture_id, pass2_framebuffer_id;
+  GskGLRenderTarget *pass1;
+  GskGLRenderTarget *pass2;
   GskGLRenderState state;
 
   g_assert (blur_radius_x > 0);
@@ -1809,26 +1809,22 @@ blur_offscreen (GskGLRenderJob       *job,
   g_assert (offscreen->area.size.width > 0);
   g_assert (offscreen->area.size.height > 0);
 
-  gsk_next_driver_create_render_target (job->driver,
-                                        MAX (texture_to_blur_width, 1),
-                                        MAX (texture_to_blur_height, 1),
-                                        GL_NEAREST, GL_NEAREST,
-                                        &pass1_framebuffer_id,
-                                        &pass1_texture_id);
-
-  gsk_next_driver_autorelease_framebuffer (job->driver, pass1_framebuffer_id);
+  if (!gsk_next_driver_create_render_target (job->driver,
+                                             MAX (texture_to_blur_width, 1),
+                                             MAX (texture_to_blur_height, 1),
+                                             GL_NEAREST, GL_NEAREST,
+                                             &pass1))
+    return 0;
 
   if (texture_to_blur_width <= 0 || texture_to_blur_height <= 0)
-    return pass1_texture_id;
-
-  gsk_next_driver_create_render_target (job->driver,
-                                        texture_to_blur_width,
-                                        texture_to_blur_height,
-                                        GL_NEAREST, GL_NEAREST,
-                                        &pass2_framebuffer_id,
-                                        &pass2_texture_id);
+    return gsk_next_driver_release_render_target (job->driver, pass1, FALSE);
 
-  gsk_next_driver_autorelease_framebuffer (job->driver, pass2_framebuffer_id);
+  if (!gsk_next_driver_create_render_target (job->driver,
+                                             texture_to_blur_width,
+                                             texture_to_blur_height,
+                                             GL_NEAREST, GL_NEAREST,
+                                             &pass2))
+    return gsk_next_driver_release_render_target (job->driver, pass1, FALSE);
 
   gsk_gl_render_state_save (&state, job);
 
@@ -1838,7 +1834,7 @@ blur_offscreen (GskGLRenderJob       *job,
   gsk_gl_render_job_push_clip (job, &new_clip);
 
   /* Bind new framebuffer and clear it */
-  gsk_gl_command_queue_bind_framebuffer (job->command_queue, pass1_framebuffer_id);
+  gsk_gl_command_queue_bind_framebuffer (job->command_queue, pass1->framebuffer_id);
   gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
 
   /* Begin drawing the first horizontal pass, using offscreen as the
@@ -1869,7 +1865,7 @@ blur_offscreen (GskGLRenderJob       *job,
   gsk_gl_program_end_draw (job->driver->blur);
 
   /* Bind second pass framebuffer and clear it */
-  gsk_gl_command_queue_bind_framebuffer (job->command_queue, pass2_framebuffer_id);
+  gsk_gl_command_queue_bind_framebuffer (job->command_queue, pass2->framebuffer_id);
   gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
 
   /* Draw using blur program with first pass as source texture */
@@ -1883,7 +1879,7 @@ blur_offscreen (GskGLRenderJob       *job,
                                       UNIFORM_SHARED_SOURCE,
                                       GL_TEXTURE_2D,
                                       GL_TEXTURE0,
-                                      pass1_texture_id);
+                                      pass1->texture_id);
   gsk_gl_program_set_uniform1f (job->driver->blur,
                                 UNIFORM_BLUR_RADIUS,
                                 blur_radius_y);
@@ -1902,9 +1898,9 @@ blur_offscreen (GskGLRenderJob       *job,
 
   gsk_gl_render_state_restore (&state, job);
 
-  /* TODO: Release bound fbo/texture pair */
+  gsk_next_driver_release_render_target (job->driver, pass1, TRUE);
 
-  return pass2_texture_id;
+  return gsk_next_driver_release_render_target (job->driver, pass2, FALSE);
 }
 
 static void
@@ -2441,29 +2437,31 @@ gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob       *job,
       }
   }
 
-  guint framebuffer_id;
-  guint texture_id;
+  GskGLRenderTarget *render_target;
   GskGLRenderState state;
 
   gsk_gl_render_state_save (&state, job);
 
-  gsk_next_driver_create_render_target (job->driver,
-                                        width, height,
-                                        filter, filter,
-                                        &texture_id, &framebuffer_id);
+  if (!gsk_next_driver_create_render_target (job->driver,
+                                             width, height,
+                                             filter, filter,
+                                             &render_target))
+    g_assert_not_reached ();
 
   if (gdk_gl_context_has_debug (job->command_queue->context))
     {
       gdk_gl_context_label_object_printf (job->command_queue->context,
-                                          GL_TEXTURE, texture_id,
+                                          GL_TEXTURE,
+                                          render_target->texture_id,
                                           "Offscreen<%s> %d",
                                           g_type_name_from_instance ((GTypeInstance *) node),
-                                          texture_id);
+                                          render_target->texture_id);
       gdk_gl_context_label_object_printf (job->command_queue->context,
-                                          GL_FRAMEBUFFER, framebuffer_id,
+                                          GL_FRAMEBUFFER,
+                                          render_target->framebuffer_id,
                                           "Offscreen<%s> FB %d",
                                           g_type_name_from_instance ((GTypeInstance *) node),
-                                          framebuffer_id);
+                                          render_target->framebuffer_id);
     }
 
   job->viewport.origin.x = offscreen->bounds->origin.x * scale_x;
@@ -2471,7 +2469,7 @@ gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob       *job,
   job->viewport.size.width = width;
   job->viewport.size.height = height;
 
-  gsk_gl_command_queue_bind_framebuffer (job->command_queue, framebuffer_id);
+  gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id);
   gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
 
   init_projection_matrix (&job->projection, &job->viewport);
@@ -2493,13 +2491,14 @@ gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob       *job,
   gsk_gl_render_state_restore (&state, job);
 
   offscreen->was_offscreen = TRUE;
-  offscreen->texture_id = texture_id;
-  init_full_texture_region (&offscreen->area);
+  offscreen->texture_id = gsk_next_driver_release_render_target (job->driver,
+                                                                 render_target,
+                                                                 FALSE);
 
-  gsk_next_driver_autorelease_framebuffer (job->driver, framebuffer_id);
+  init_full_texture_region (&offscreen->area);
 
   if (!offscreen->do_not_cache)
-    gsk_next_driver_cache_texture (job->driver, &key, texture_id);
+    gsk_next_driver_cache_texture (job->driver, &key, offscreen->texture_id);
 
   return TRUE;
 }
diff --git a/gsk/next/gskgltypesprivate.h b/gsk/next/gskgltypesprivate.h
index f29f457cff..03aa07383a 100644
--- a/gsk/next/gskgltypesprivate.h
+++ b/gsk/next/gskgltypesprivate.h
@@ -35,7 +35,7 @@ typedef struct _GskGLBuffer GskGLBuffer;
 typedef struct _GskGLCommandQueue GskGLCommandQueue;
 typedef struct _GskGLCompiler GskGLCompiler;
 typedef struct _GskGLDrawVertex GskGLDrawVertex;
-typedef struct _GskGLFramebuffer GskGLFramebuffer;
+typedef struct _GskGLRenderTarget GskGLRenderTarget;
 typedef struct _GskGLGlyphLibrary GskGLGlyphLibrary;
 typedef struct _GskGLIconLibrary GskGLIconLibrary;
 typedef struct _GskGLProgram GskGLProgram;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]