gsk/gl: support non-standard default framebuffer
authorChristian Hergert <christian@hergert.me>
Fri, 11 Feb 2022 05:51:15 +0000 (21:51 -0800)
committerChristian Hergert <christian@hergert.me>
Tue, 22 Feb 2022 20:01:24 +0000 (12:01 -0800)
There are situations where our "default framebuffer" is not actually
zero, yet we still want to apply a scissor rect.

Generally, 0 is the default framebuffer. But on platforms where we need
to bind a platform-specific feature to a GL_FRAMEBUFFER, we might have a
default that is not 0. For example, on macOS we bind an IOSurfaceRef to
a GL_TEXTURE_RECTANGLE which then is assigned as the backing store for a
framebuffer. This is different than using gsk_gl_renderer_render_texture()
in that we don't want to incur an extra copy to the destination surface
nor do we even have a way to pass a texture_id into render_texture().

gdk/gdkglcontext.c
gdk/gdkglcontextprivate.h
gsk/gl/gskglcommandqueue.c
gsk/gl/gskglcommandqueueprivate.h
gsk/gl/gskglrenderjob.c

index 373f2fefd504e4089db1d2597d9a9ef1a8a5ce20..be514c0ac66a9a2be5165c8e9913882ec24c5ecf 100644 (file)
@@ -648,6 +648,12 @@ gdk_gl_context_surface_resized (GdkDrawContext *draw_context)
   gdk_gl_context_clear_old_updated_area (context);
 }
 
+static guint
+gdk_gl_context_real_get_default_framebuffer (GdkGLContext *self)
+{
+  return 0;
+}
+
 static void
 gdk_gl_context_class_init (GdkGLContextClass *klass)
 {
@@ -659,6 +665,7 @@ gdk_gl_context_class_init (GdkGLContextClass *klass)
   klass->is_shared = gdk_gl_context_real_is_shared;
   klass->make_current = gdk_gl_context_real_make_current;
   klass->clear_current = gdk_gl_context_real_clear_current;
+  klass->get_default_framebuffer = gdk_gl_context_real_get_default_framebuffer;
 
   draw_context_class->begin_frame = gdk_gl_context_real_begin_frame;
   draw_context_class->end_frame = gdk_gl_context_real_end_frame;
index ff11349b3ec6ed740efda5feb4ed4c4e111d41a8..0a3739f090fef8a4dba9a4da51acc958f5de2ad0 100644 (file)
@@ -71,6 +71,8 @@ struct _GdkGLContextClass
 
   gboolean              (* is_shared)                           (GdkGLContext          *self,
                                                                  GdkGLContext          *other);
+
+  guint                 (* get_default_framebuffer)             (GdkGLContext          *self);
 };
 
 typedef struct {
index 3af8c1eb094b32be292f1d5838d8b4450690ff48..b7640edf87107a2deb8ea43e7d98bc58a0c15ef0 100644 (file)
@@ -675,9 +675,9 @@ gsk_gl_command_queue_split_draw (GskGLCommandQueue *self)
 }
 
 void
-gsk_gl_command_queue_clear (GskGLCommandQueue    *self,
-                             guint                  clear_bits,
-                             const graphene_rect_t *viewport)
+gsk_gl_command_queue_clear (GskGLCommandQueue     *self,
+                            guint                  clear_bits,
+                            const graphene_rect_t *viewport)
 {
   GskGLCommandBatch *batch;
 
@@ -750,11 +750,12 @@ static inline void
 apply_scissor (gboolean              *state,
                guint                  framebuffer,
                const graphene_rect_t *scissor,
-               gboolean               has_scissor)
+               gboolean               has_scissor,
+               guint                  default_framebuffer)
 {
   g_assert (framebuffer != (guint)-1);
 
-  if (framebuffer != 0 || !has_scissor)
+  if (framebuffer != default_framebuffer || !has_scissor)
     {
       if (*state != FALSE)
         {
@@ -935,15 +936,24 @@ gsk_gl_command_queue_sort_batches (GskGLCommandQueue *self)
  * @self: a `GskGLCommandQueue`
  * @surface_height: the height of the backing surface
  * @scale_factor: the scale factor of the backing surface
- * #scissor: (nullable): the scissor clip if any
+ * @scissor: (nullable): the scissor clip if any
+ * @default_framebuffer: the default framebuffer id if not zero
  *
  * Executes all of the batches in the command queue.
+ *
+ * Typically, the scissor rect is only applied when rendering to the default
+ * framebuffer (zero in most cases). However, if @default_framebuffer is not
+ * zero, it will be checked to see if the rendering target matches so that
+ * the scissor rect is applied. This should be used in cases where rendering
+ * to the backbuffer for display is not the default GL framebuffer of zero.
+ * Currently, this happens when rendering on macOS using IOSurface.
  */
 void
 gsk_gl_command_queue_execute (GskGLCommandQueue    *self,
                               guint                 surface_height,
                               guint                 scale_factor,
-                              const cairo_region_t *scissor)
+                              const cairo_region_t *scissor,
+                              guint                 default_framebuffer)
 {
   G_GNUC_UNUSED guint count = 0;
   graphene_rect_t scissor_test;
@@ -1049,7 +1059,7 @@ gsk_gl_command_queue_execute (GskGLCommandQueue    *self,
         case GSK_GL_COMMAND_KIND_CLEAR:
           if (apply_framebuffer (&framebuffer, batch->clear.framebuffer))
             {
-              apply_scissor (&scissor_state, framebuffer, &scissor_test, has_scissor);
+              apply_scissor (&scissor_state, framebuffer, &scissor_test, has_scissor, default_framebuffer);
               n_fbos++;
             }
 
@@ -1073,7 +1083,7 @@ gsk_gl_command_queue_execute (GskGLCommandQueue    *self,
 
           if (apply_framebuffer (&framebuffer, batch->draw.framebuffer))
             {
-              apply_scissor (&scissor_state, framebuffer, &scissor_test, has_scissor);
+              apply_scissor (&scissor_state, framebuffer, &scissor_test, has_scissor, default_framebuffer);
               n_fbos++;
             }
 
index 4147283e6d3a450161f7d8c5c6a71484b086601f..48911305a8ba8c3f5936c28a24238fecd2c67c22 100644 (file)
@@ -278,7 +278,8 @@ void                gsk_gl_command_queue_end_frame            (GskGLCommandQueue
 void                gsk_gl_command_queue_execute              (GskGLCommandQueue    *self,
                                                                guint                 surface_height,
                                                                guint                 scale_factor,
-                                                               const cairo_region_t *scissor);
+                                                               const cairo_region_t *scissor,
+                                                               guint                 default_framebuffer);
 int                 gsk_gl_command_queue_upload_texture       (GskGLCommandQueue    *self,
                                                                GdkTexture           *texture,
                                                                int                   min_filter,
index 65b5adf84dedb577012791693542a1d632e04eaf..afefe7a018300208c6daf238b24e5a9612e7b1b7 100644 (file)
@@ -123,6 +123,7 @@ struct _GskGLRenderJob
    * GL context.
    */
   guint framebuffer;
+  guint default_framebuffer;
 
   /* The viewport we are using. This state is updated as we process render
    * nodes in the specific visitor callbacks.
@@ -4058,7 +4059,7 @@ gsk_gl_render_job_render_flipped (GskGLRenderJob *job,
   gsk_gl_render_job_end_draw (job);
 
   gdk_gl_context_push_debug_group (job->command_queue->context, "Executing command queue");
-  gsk_gl_command_queue_execute (job->command_queue, surface_height, 1, NULL);
+  gsk_gl_command_queue_execute (job->command_queue, surface_height, 1, NULL, job->default_framebuffer);
   gdk_gl_context_pop_debug_group (job->command_queue->context);
 
   glDeleteFramebuffers (1, &framebuffer_id);
@@ -4108,7 +4109,7 @@ gsk_gl_render_job_render (GskGLRenderJob *job,
   start_time = GDK_PROFILER_CURRENT_TIME;
   gsk_gl_command_queue_make_current (job->command_queue);
   gdk_gl_context_push_debug_group (job->command_queue->context, "Executing command queue");
-  gsk_gl_command_queue_execute (job->command_queue, surface_height, scale_factor, job->region);
+  gsk_gl_command_queue_execute (job->command_queue, surface_height, scale_factor, job->region, job->default_framebuffer);
   gdk_gl_context_pop_debug_group (job->command_queue->context);
   gdk_profiler_add_mark (start_time, GDK_PROFILER_CURRENT_TIME-start_time, "Execute GL command queue", "");
 }
@@ -4157,11 +4158,23 @@ gsk_gl_render_job_new (GskGLDriver           *driver,
   const graphene_rect_t *clip_rect = viewport;
   graphene_rect_t transformed_extents;
   GskGLRenderJob *job;
+  GdkGLContext *context;
+  GLint default_framebuffer = 0;
 
   g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), NULL);
   g_return_val_if_fail (viewport != NULL, NULL);
   g_return_val_if_fail (scale_factor > 0, NULL);
 
+  /* Check for non-standard framebuffer binding as we might not be using
+   * the default framebuffer on systems like macOS where we've bound an
+   * IOSurface to a GL_TEXTURE_RECTANGLE. Otherwise, no scissor clip will
+   * be applied in the command queue causing overdrawing.
+   */
+  context = driver->command_queue->context;
+  default_framebuffer = GDK_GL_CONTEXT_GET_CLASS (context)->get_default_framebuffer (context);
+  if (framebuffer == 0 && default_framebuffer != 0)
+    framebuffer = default_framebuffer;
+
   job = g_slice_new0 (GskGLRenderJob);
   job->driver = g_object_ref (driver);
   job->command_queue = job->driver->command_queue;
@@ -4169,6 +4182,7 @@ gsk_gl_render_job_new (GskGLDriver           *driver,
   job->modelview = g_array_sized_new (FALSE, FALSE, sizeof (GskGLRenderModelview), 16);
   job->framebuffer = framebuffer;
   job->clear_framebuffer = !!clear_framebuffer;
+  job->default_framebuffer = default_framebuffer;
   job->offset_x = 0;
   job->offset_y = 0;
   job->scale_x = scale_factor;