[gtk/wip/chergert/glproto: 745/920] start on sharing driver instance across renderers




commit 3721fbe88500d758a37af2dc44f12e7705f7766c
Author: Christian Hergert <chergert redhat com>
Date:   Thu Jan 21 16:31:51 2021 -0800

    start on sharing driver instance across renderers
    
    this isn't fully working yet (popup windows seem to start with a black
    screen) but eventually they start to look closer to correct. More
    troubleshooting necessary but this is a start.

 gsk/next/gskglcommandqueue.c     |  2 --
 gsk/next/gskgldriver.c           | 55 +++++++++++++++++++++++++++++++--
 gsk/next/gskgldriverprivate.h    |  4 +--
 gsk/next/gskglrenderer.c         | 67 ++++++++++++++++++++++------------------
 gsk/next/gskglrenderjob.c        | 30 ++++++++++++++----
 gsk/next/gskglrenderjobprivate.h |  1 +
 6 files changed, 116 insertions(+), 43 deletions(-)
---
diff --git a/gsk/next/gskglcommandqueue.c b/gsk/next/gskglcommandqueue.c
index bd80a296c6..765484ea70 100644
--- a/gsk/next/gskglcommandqueue.c
+++ b/gsk/next/gskglcommandqueue.c
@@ -716,8 +716,6 @@ gsk_gl_command_queue_execute (GskGLCommandQueue    *self,
   if (self->batches->len == 0)
     return;
 
-  gsk_gl_command_queue_make_current (self);
-
   glEnable (GL_DEPTH_TEST);
   glDepthFunc (GL_LEQUAL);
 
diff --git a/gsk/next/gskgldriver.c b/gsk/next/gskgldriver.c
index 6162480830..77bde80552 100644
--- a/gsk/next/gskgldriver.c
+++ b/gsk/next/gskgldriver.c
@@ -393,9 +393,9 @@ gsk_next_driver_autorelease_framebuffer (GskNextDriver *self,
   g_array_append_val (self->autorelease_framebuffers, framebuffer_id);
 }
 
-GskNextDriver *
+static GskNextDriver *
 gsk_next_driver_new (GskGLCommandQueue  *command_queue,
-                     gboolean            debug,
+                     gboolean            debug_shaders,
                      GError            **error)
 {
   GskNextDriver *self;
@@ -409,7 +409,7 @@ gsk_next_driver_new (GskGLCommandQueue  *command_queue,
 
   self = g_object_new (GSK_TYPE_NEXT_DRIVER, NULL);
   self->command_queue = g_object_ref (command_queue);
-  self->debug = !!debug;
+  self->debug = !!debug_shaders;
 
   if (!gsk_next_driver_load_programs (self, error))
     {
@@ -424,6 +424,49 @@ gsk_next_driver_new (GskGLCommandQueue  *command_queue,
   return g_steal_pointer (&self);
 }
 
+/**
+ * gsk_next_driver_from_shared_context:
+ * @context: a shared #GdkGLContext retrieved with gdk_gl_context_get_shared_context()
+ * @debug_shaders: if debug information for shaders should be displayed
+ * @error: location for error information
+ *
+ * Retrieves a driver for a shared context. Generally this is shared across all GL
+ * contexts for a display so that fewer programs are necessary for driving output.
+ *
+ * Returns: (transfer full): a #GskNextDriver if successful; otherwise %NULL and
+ *   @error is set.
+ */
+GskNextDriver *
+gsk_next_driver_from_shared_context (GdkGLContext  *context,
+                                     gboolean       debug_shaders,
+                                     GError       **error)
+{
+  GskGLCommandQueue *command_queue = NULL;
+  GskNextDriver *driver;
+
+  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+
+  if ((driver = g_object_get_data (G_OBJECT (context), "GSK_NEXT_DRIVER")))
+    return g_object_ref (driver);
+
+  gdk_gl_context_make_current (context);
+
+  command_queue = gsk_gl_command_queue_new (context);
+
+  if (!(driver = gsk_next_driver_new (command_queue, debug_shaders, error)))
+    goto failure;
+
+  g_object_set_data_full (G_OBJECT (context),
+                          "GSK_NEXT_DRIVER",
+                          g_object_ref (driver),
+                          g_object_unref);
+
+failure:
+  g_clear_object (&command_queue);
+
+  return g_steal_pointer (&driver);
+}
+
 /**
  * gsk_next_driver_begin_frame:
  * @self: a #GskNextDriver
@@ -604,6 +647,7 @@ gsk_next_driver_load_texture (GskNextDriver   *self,
                               int              min_filter,
                               int              mag_filter)
 {
+  GdkGLContext *previous_context = NULL;
   GdkGLContext *context;
   GdkTexture *downloaded_texture = NULL;
   GdkTexture *source_texture;
@@ -632,6 +676,8 @@ gsk_next_driver_load_texture (GskNextDriver   *self,
         {
           cairo_surface_t *surface;
 
+          previous_context = gdk_gl_context_get_current ();
+
           /* In this case, we have to temporarily make the texture's
            * context the current one, download its data into our context
            * and then create a texture from it. */
@@ -681,6 +727,9 @@ gsk_next_driver_load_texture (GskNextDriver   *self,
 
   g_clear_object (&downloaded_texture);
 
+  if (previous_context)
+    gdk_gl_context_make_current (previous_context);
+
   return t->texture_id;
 }
 
diff --git a/gsk/next/gskgldriverprivate.h b/gsk/next/gskgldriverprivate.h
index c73181bb27..18b10a66be 100644
--- a/gsk/next/gskgldriverprivate.h
+++ b/gsk/next/gskgldriverprivate.h
@@ -116,8 +116,8 @@ struct _GskNextDriver
   guint in_frame : 1;
 };
 
-GskNextDriver     *gsk_next_driver_new                   (GskGLCommandQueue    *command_queue,
-                                                          gboolean              debug,
+GskNextDriver     *gsk_next_driver_from_shared_context   (GdkGLContext         *context,
+                                                          gboolean              debug_shaders,
                                                           GError              **error);
 GdkGLContext      *gsk_next_driver_get_context           (GskNextDriver        *self);
 gboolean           gsk_next_driver_create_render_target  (GskNextDriver        *self,
diff --git a/gsk/next/gskglrenderer.c b/gsk/next/gskglrenderer.c
index 2ec2477e16..59db1fea46 100644
--- a/gsk/next/gskglrenderer.c
+++ b/gsk/next/gskglrenderer.c
@@ -20,6 +20,7 @@
 
 #include "config.h"
 
+#include <gdk/gdksurfaceprivate.h>
 #include <gsk/gskdebugprivate.h>
 #include <gsk/gskrendererprivate.h>
 
@@ -38,20 +39,15 @@ struct _GskNextRenderer
 {
   GskRenderer parent_instance;
 
-  /* The GskGLCommandQueue manages how we send all drawing operations,
-   * uniform changes, and other texture related operations to the GPU.
-   * It maintains a cache of state to help reduce the number of state
-   * changes we send to the GPU. Furthermore, it can reorder batches so
-   * that we switch programs and uniforms less frequently by verifying
-   * what batches can be reordered based on clipping and stacking.
+  /* This context is used to swap buffers when we are rendering directly
+   * to a GDK surface. It is also used to locate the shared driver for
+   * the display that we use to drive the command queue.
    */
-  GskGLCommandQueue *command_queue;
+  GdkGLContext *context;
 
-  /* The driver manages our program state and command queues. It gives
-   * us a single place to manage loading all the programs as well which
-   * unfortunately cannot be shared across all renderers for a display.
-   * (Context sharing explicitly does not name program/uniform state as
-   * shareable even though on some implementations it is).
+  /* The driver manages our program state and command queues. It also
+   * deals with caching textures, shaders, shadows, glyph, and icon
+   * caches through various helpers.
    */
   GskNextDriver *driver;
 };
@@ -70,8 +66,8 @@ gsk_next_renderer_realize (GskRenderer  *renderer,
                            GError      **error)
 {
   GskNextRenderer *self = (GskNextRenderer *)renderer;
-  GskGLCommandQueue *command_queue = NULL;
   GdkGLContext *context = NULL;
+  GdkGLContext *shared_context;
   GskNextDriver *driver = NULL;
   gboolean ret = FALSE;
   gboolean debug_shaders = FALSE;
@@ -79,23 +75,34 @@ gsk_next_renderer_realize (GskRenderer  *renderer,
   g_assert (GSK_IS_NEXT_RENDERER (self));
   g_assert (GDK_IS_SURFACE (surface));
 
+  if (self->context != NULL)
+    return TRUE;
+
+  g_assert (self->driver == NULL);
+  g_assert (self->context == NULL);
+
   if (!(context = gdk_surface_create_gl_context (surface, error)) ||
       !gdk_gl_context_realize (context, error))
     goto failure;
 
-  gdk_gl_context_make_current (context);
-
-  command_queue = gsk_gl_command_queue_new (context);
+  if (!(shared_context = gdk_surface_get_shared_data_gl_context (surface)))
+    {
+      g_set_error (error,
+                   GDK_GL_ERROR,
+                   GDK_GL_ERROR_NOT_AVAILABLE,
+                   "Failed to locate shared GL context for driver");
+      goto failure;
+    }
 
 #ifdef G_ENABLE_DEBUG
   if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), SHADERS))
     debug_shaders = TRUE;
 #endif
 
-  if (!(driver = gsk_next_driver_new (command_queue, debug_shaders, error)))
+  if (!(driver = gsk_next_driver_from_shared_context (shared_context, debug_shaders, error)))
     goto failure;
 
-  self->command_queue = g_steal_pointer (&command_queue);
+  self->context = g_steal_pointer (&context);
   self->driver = g_steal_pointer (&driver);
 
   ret = TRUE;
@@ -103,7 +110,6 @@ gsk_next_renderer_realize (GskRenderer  *renderer,
 failure:
   g_clear_object (&driver);
   g_clear_object (&context);
-  g_clear_object (&command_queue);
 
   return ret;
 }
@@ -116,7 +122,6 @@ gsk_next_renderer_unrealize (GskRenderer *renderer)
   g_assert (GSK_IS_NEXT_RENDERER (renderer));
 
   g_clear_object (&self->driver);
-  g_clear_object (&self->command_queue);
 }
 
 typedef struct _GskGLTextureState
@@ -207,32 +212,35 @@ gsk_gl_renderer_render (GskRenderer          *renderer,
   cairo_region_t *render_region;
   graphene_rect_t viewport;
   GskGLRenderJob *job;
-  GdkGLContext *context;
   GdkSurface *surface;
   float scale_factor;
 
   g_assert (GSK_IS_NEXT_RENDERER (renderer));
   g_assert (root != NULL);
 
-  context = gsk_gl_command_queue_get_context (self->command_queue);
-  surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context));
+  surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self->context));
   scale_factor = gdk_surface_get_scale_factor (surface);
-  render_region = get_render_region (surface, context);
+  render_region = get_render_region (surface, self->context);
 
   viewport.origin.x = 0;
   viewport.origin.y = 0;
   viewport.size.width = gdk_surface_get_width (surface) * scale_factor;
   viewport.size.height = gdk_surface_get_height (surface) * scale_factor;
 
-  gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (context), update_area);
-  job = gsk_gl_render_job_new (self->driver, &viewport, scale_factor, render_region, 0);
+  gdk_gl_context_make_current (self->context);
+  gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (self->context), update_area);
+
+  job = gsk_gl_render_job_new (self->driver, &viewport, scale_factor, render_region, self->context, 0);
+
 #ifdef G_ENABLE_DEBUG
   if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), FALLBACK))
     gsk_gl_render_job_set_debug_fallback (job, TRUE);
 #endif
 
   gsk_gl_render_job_render (job, root);
-  gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (context));
+
+  gdk_gl_context_make_current (self->context);
+  gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (self->context));
 
   gsk_gl_render_job_free (job);
 
@@ -255,7 +263,7 @@ gsk_gl_renderer_render_texture (GskRenderer           *renderer,
   g_assert (GSK_IS_NEXT_RENDERER (renderer));
   g_assert (root != NULL);
 
-  context = gsk_gl_command_queue_get_context (self->command_queue);
+  context = gsk_gl_command_queue_get_context (self->driver->command_queue);
   width = ceilf (viewport->size.width);
   height = ceilf (viewport->size.height);
 
@@ -267,7 +275,7 @@ gsk_gl_renderer_render_texture (GskRenderer           *renderer,
                                              &render_target))
     return NULL;
 
-  job = gsk_gl_render_job_new (self->driver, viewport, 1, NULL, render_target->framebuffer_id);
+  job = gsk_gl_render_job_new (self->driver, viewport, 1, NULL, context, render_target->framebuffer_id);
   gsk_gl_render_job_render_flipped (job, root);
   gsk_gl_render_job_free (job);
 
@@ -282,7 +290,6 @@ gsk_next_renderer_dispose (GObject *object)
 #ifdef G_ENABLE_DEBUG
   GskNextRenderer *self = (GskNextRenderer *)object;
 
-  g_assert (self->command_queue == NULL);
   g_assert (self->driver == NULL);
 #endif
 
diff --git a/gsk/next/gskglrenderjob.c b/gsk/next/gskglrenderjob.c
index c1e6fd615c..a88f411082 100644
--- a/gsk/next/gskglrenderjob.c
+++ b/gsk/next/gskglrenderjob.c
@@ -74,6 +74,12 @@
 
 struct _GskGLRenderJob
 {
+  /* The context containing the framebuffer we are drawing to. Generally this
+   * is the context of the surface but may be a shared context if rendering to
+   * an offscreen texture such as gsk_gl_renderer_render_texture().
+   */
+  GdkGLContext *context;
+
   /* The driver to be used. This is shared among all the renderers on a given
    * GdkDisplay and uses the shared GL context to send commands.
    */
@@ -3578,18 +3584,20 @@ gsk_gl_render_job_render (GskGLRenderJob *job,
   g_return_if_fail (root != NULL);
   g_return_if_fail (GSK_IS_NEXT_DRIVER (job->driver));
 
-  context = gsk_next_driver_get_context (job->driver);
+  context = gsk_gl_command_queue_get_context (job->command_queue);
   scale_factor = MAX (job->scale_x, job->scale_y);
   surface_height = job->viewport.size.height;
 
+  /* Do initial frame setup using shared GL context */
   gsk_next_driver_begin_frame (job->driver);
 
+  /* Build the command queue using the shared GL context for all renderers
+   * on the same display.
+   */
+  gdk_gl_context_push_debug_group (context, "Building command queue");
   if (job->framebuffer != 0)
     gsk_gl_command_queue_bind_framebuffer (job->command_queue, job->framebuffer);
-
   gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
-
-  gdk_gl_context_push_debug_group (context, "Building command queue");
   gsk_gl_render_job_visit_node (job, root);
   gdk_gl_context_pop_debug_group (context);
 
@@ -3600,10 +3608,16 @@ gsk_gl_render_job_render (GskGLRenderJob *job,
   gsk_next_driver_save_atlases_to_png (job->driver, NULL);
 #endif
 
-  gdk_gl_context_push_debug_group (context, "Executing command queue");
+  /* But now for executing the command queue, we want to use the context
+   * that was provided to us when creating the render job as framebuffer 0
+   * is bound to that context.
+   */
+  gdk_gl_context_make_current (job->context);
+  gdk_gl_context_push_debug_group (job->context, "Executing command queue");
   gsk_gl_command_queue_execute (job->command_queue, surface_height, scale_factor, job->region);
-  gdk_gl_context_pop_debug_group (context);
+  gdk_gl_context_pop_debug_group (job->context);
 
+  /* Do frame cleanup using shared GL context */
   gsk_next_driver_end_frame (job->driver);
 }
 
@@ -3621,6 +3635,7 @@ gsk_gl_render_job_new (GskNextDriver         *driver,
                        const graphene_rect_t *viewport,
                        float                  scale_factor,
                        const cairo_region_t  *region,
+                       GdkGLContext          *context,
                        guint                  framebuffer)
 {
   const graphene_rect_t *clip_rect = viewport;
@@ -3628,12 +3643,14 @@ gsk_gl_render_job_new (GskNextDriver         *driver,
   GskGLRenderJob *job;
 
   g_return_val_if_fail (GSK_IS_NEXT_DRIVER (driver), NULL);
+  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
   g_return_val_if_fail (viewport != NULL, NULL);
   g_return_val_if_fail (scale_factor > 0, NULL);
 
   job = g_slice_new0 (GskGLRenderJob);
   job->driver = g_object_ref (driver);
   job->command_queue = driver->command_queue;
+  job->context = g_object_ref (context);
   job->clip = g_array_new (FALSE, FALSE, sizeof (GskGLRenderClip));
   job->modelview = g_array_new (FALSE, FALSE, sizeof (GskGLRenderModelview));
   job->framebuffer = framebuffer;
@@ -3687,6 +3704,7 @@ gsk_gl_render_job_free (GskGLRenderJob *job)
     }
 
   g_clear_object (&job->driver);
+  g_clear_object (&job->context);
   g_clear_pointer (&job->region, cairo_region_destroy);
   g_clear_pointer (&job->modelview, g_array_unref);
   g_clear_pointer (&job->clip, g_array_unref);
diff --git a/gsk/next/gskglrenderjobprivate.h b/gsk/next/gskglrenderjobprivate.h
index 2a8c01fa71..02cfee5737 100644
--- a/gsk/next/gskglrenderjobprivate.h
+++ b/gsk/next/gskglrenderjobprivate.h
@@ -27,6 +27,7 @@ GskGLRenderJob *gsk_gl_render_job_new                 (GskNextDriver         *dr
                                                        const graphene_rect_t *viewport,
                                                        float                  scale_factor,
                                                        const cairo_region_t  *region,
+                                                       GdkGLContext          *context,
                                                        guint                  framebuffer);
 void            gsk_gl_render_job_free                (GskGLRenderJob        *job);
 void            gsk_gl_render_job_render              (GskGLRenderJob        *job,


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