gl: Respect clip wehn drawing scale nodes
authorMatthias Clasen <mclasen@redhat.com>
Thu, 16 Feb 2023 01:10:58 +0000 (20:10 -0500)
committerMatthias Clasen <mclasen@redhat.com>
Fri, 3 Mar 2023 17:32:08 +0000 (11:32 -0600)
Use the same approach and only create an offscreen
that is big enough for the clipped part of the scaled
texture.

If the clipped part is still too large for a single
texture, we give up and just render the texture without
filters (using the regular texture rendering code path
which supports slicing).

The following commit will add the texture-scale-magnify-10000x
test which fails without this fix.

gsk/gl/gskglrenderjob.c

index 83f928ba5d893a4078bd855a62fd92767e5b37b7..1fcd13a1e2803bb4e6b2819ab3d9cdf0c8b99793 100644 (file)
@@ -872,6 +872,21 @@ gsk_gl_render_job_transform_bounds (GskGLRenderJob        *job,
     }
 }
 
+static inline void
+gsk_gl_render_job_untransform_bounds (GskGLRenderJob        *job,
+                                      const graphene_rect_t *rect,
+                                      graphene_rect_t       *out_rect)
+{
+  GskTransform *transform;
+
+  transform = gsk_transform_invert (gsk_transform_ref (job->current_modelview->transform));
+
+  gsk_transform_transform_bounds (transform, rect, out_rect);
+
+  out_rect->origin.x -= job->offset_x;
+  out_rect->origin.y -= job->offset_y;
+}
+
 static inline void
 gsk_gl_render_job_transform_rounded_rect (GskGLRenderJob       *job,
                                           const GskRoundedRect *rect,
@@ -3615,127 +3630,98 @@ gsk_gl_render_job_visit_texture_scale_node (GskGLRenderJob      *job,
   int min_filter = min_filters[scaling_filter];
   int mag_filter = mag_filters[scaling_filter];
   int max_texture_size = job->command_queue->max_texture_size;
+  graphene_rect_t clip_rect;
+  GskGLRenderTarget *render_target;
+  GskGLRenderOffscreen offscreen = {0};
+  graphene_rect_t viewport;
+  graphene_rect_t prev_viewport;
+  graphene_matrix_t prev_projection;
+  float prev_alpha;
+  guint prev_fbo;
+  guint texture_id;
+  float u0, u1, v0, v1;
+
+  gsk_gl_render_job_untransform_bounds (job, &job->current_clip->rect.bounds, &clip_rect);
+  if (!graphene_rect_intersection (bounds, &clip_rect, &clip_rect))
+    return;
 
-  if (scaling_filter == GSK_SCALING_FILTER_LINEAR)
+  if G_UNLIKELY (clip_rect.size.width > max_texture_size ||
+                 clip_rect.size.height > max_texture_size)
     {
       gsk_gl_render_job_visit_texture (job, texture, bounds);
       return;
     }
 
-  if G_LIKELY (texture->width <= max_texture_size &&
-               texture->height <= max_texture_size)
-    {
-      GskGLRenderTarget *render_target;
-      GskGLRenderOffscreen offscreen = {0};
-      graphene_rect_t viewport;
-      graphene_rect_t prev_viewport;
-      graphene_matrix_t prev_projection;
-      float prev_alpha;
-      guint prev_fbo;
-      guint texture_id;
-
-      viewport = GRAPHENE_RECT_INIT (0, 0,
-                                     bounds->size.width,
-                                     bounds->size.height);
-
-      if (!gsk_gl_driver_create_render_target (job->driver,
-                                               (int) ceilf (viewport.size.width),
-                                               (int) ceilf (viewport.size.height),
-                                               get_target_format (job, node),
-                                               GL_LINEAR, GL_LINEAR,
-                                               &render_target))
-        {
-          /* viewport is too big, slice the texture and try again */
-          goto slice;
-        }
-
-      gsk_gl_render_job_upload_texture (job, texture, min_filter, mag_filter, &offscreen);
-
-      g_assert (offscreen.texture_id);
-      g_assert (offscreen.was_offscreen == FALSE);
-
-      gsk_gl_render_job_set_viewport (job, &viewport, &prev_viewport);
-      gsk_gl_render_job_set_projection_from_rect (job, &viewport, &prev_projection);
-      gsk_gl_render_job_set_modelview (job, NULL);
-      prev_alpha = gsk_gl_render_job_set_alpha (job, 1.0f);
-      gsk_gl_render_job_push_clip (job, &GSK_ROUNDED_RECT_INIT_FROM_RECT (viewport));
-
-      prev_fbo = gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id);
-      gsk_gl_command_queue_clear (job->command_queue, 0, &viewport);
-
-      gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit));
-      gsk_gl_program_set_uniform_texture (job->current_program,
-                                          UNIFORM_SHARED_SOURCE, 0,
-                                          GL_TEXTURE_2D,
-                                          GL_TEXTURE0,
-                                          offscreen.texture_id);
-      gsk_gl_render_job_draw_offscreen (job, &viewport, &offscreen);
-      gsk_gl_render_job_end_draw (job);
+  viewport = GRAPHENE_RECT_INIT (0, 0,
+                                 clip_rect.size.width,
+                                 clip_rect.size.height);
 
-      gsk_gl_render_job_pop_clip (job);
-      gsk_gl_render_job_pop_modelview (job);
-      gsk_gl_render_job_set_viewport (job, &prev_viewport, NULL);
-      gsk_gl_render_job_set_projection (job, &prev_projection);
-      gsk_gl_render_job_set_alpha (job, prev_alpha);
-      gsk_gl_command_queue_bind_framebuffer (job->command_queue, prev_fbo);
-
-      texture_id = gsk_gl_driver_release_render_target (job->driver, render_target, FALSE);
-
-      gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit));
-      gsk_gl_program_set_uniform_texture (job->current_program,
-                                          UNIFORM_SHARED_SOURCE, 0,
-                                          GL_TEXTURE_2D,
-                                          GL_TEXTURE0,
-                                          texture_id);
-      gsk_gl_render_job_draw_offscreen_rect (job, bounds);
-      gsk_gl_render_job_end_draw (job);
-    }
-  else
-slice:
+  if (!gsk_gl_driver_create_render_target (job->driver,
+                                           (int) ceilf (clip_rect.size.width),
+                                           (int) ceilf (clip_rect.size.height),
+                                           get_target_format (job, node),
+                                           GL_LINEAR, GL_LINEAR,
+                                           &render_target))
     {
-      float min_x = bounds->origin.x;
-      float min_y = bounds->origin.y;
-      float max_x = min_x + bounds->size.width;
-      float max_y = min_y + bounds->size.height;
-      float scale_x = (max_x - min_x) / texture->width;
-      float scale_y = (max_y - min_y) / texture->height;
-      GskGLTextureSlice *slices = NULL;
-      guint n_slices = 0;
-      GdkGLContext *context = gsk_gl_driver_get_context (job->driver);
-      guint rows, cols;
+      gsk_gl_render_job_visit_texture (job, texture, bounds);
+      return;
+    }
 
-      /* Slice enough that neither the original texture nor the scaled texture
-       * exceed the texture size limit
-       */
-      cols = (int)(MAX (bounds->size.width, texture->width) / (max_texture_size / 4)) + 1;
-      rows = (int)(MAX (bounds->size.height, texture->height) / (max_texture_size / 4)) + 1;
+  gsk_gl_render_job_upload_texture (job, texture, min_filter, mag_filter, &offscreen);
 
-      gsk_gl_driver_slice_texture (job->driver, texture, GL_NEAREST, GL_NEAREST, cols, rows, &slices, &n_slices);
+  g_assert (offscreen.texture_id);
+  g_assert (offscreen.was_offscreen == FALSE);
 
-      g_assert (slices != NULL);
-      g_assert (n_slices > 0);
+  u0 = (clip_rect.origin.x - bounds->origin.x) / bounds->size.width;
+  v0 = (clip_rect.origin.y - bounds->origin.y) / bounds->size.height;
+  u1 = (clip_rect.origin.x + clip_rect.size.width - bounds->origin.x) / bounds->size.width;
+  v1 = (clip_rect.origin.y + clip_rect.size.height - bounds->origin.y) / bounds->size.height;
 
-      for (guint i = 0; i < n_slices; i ++)
-        {
-          const GskGLTextureSlice *slice = &slices[i];
-          float x1, x2, y1, y2;
-          GdkTexture *sub_texture;
-          GskRenderNode *sub_node;
+  gsk_gl_render_job_set_viewport (job, &viewport, &prev_viewport);
+  gsk_gl_render_job_set_projection_from_rect (job, &viewport, &prev_projection);
+  gsk_gl_render_job_set_modelview (job, NULL);
+  prev_alpha = gsk_gl_render_job_set_alpha (job, 1.0f);
+  gsk_gl_render_job_push_clip (job, &GSK_ROUNDED_RECT_INIT_FROM_RECT (viewport));
 
-          x1 = min_x + (scale_x * slice->rect.x);
-          x2 = x1 + (slice->rect.width * scale_x);
-          y1 = min_y + (scale_y * slice->rect.y);
-          y2 = y1 + (slice->rect.height * scale_y);
+  prev_fbo = gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id);
+  gsk_gl_command_queue_clear (job->command_queue, 0, &viewport);
 
-          sub_texture = gdk_gl_texture_new (context, slice->texture_id, slice->rect.width, slice->rect.height, NULL, NULL);
+  gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit));
+  gsk_gl_program_set_uniform_texture (job->current_program,
+                                      UNIFORM_SHARED_SOURCE, 0,
+                                      GL_TEXTURE_2D,
+                                      GL_TEXTURE0,
+                                      offscreen.texture_id);
+  gsk_gl_render_job_draw_coords (job,
+                                 0, 0, clip_rect.size.width, clip_rect.size.height,
+                                 u0, v0, u1, v1,
+                                 (guint16[]) { FP16_ZERO, FP16_ZERO, FP16_ZERO, FP16_ZERO });
+  gsk_gl_render_job_end_draw (job);
+
+  gsk_gl_render_job_pop_clip (job);
+  gsk_gl_render_job_pop_modelview (job);
+  gsk_gl_render_job_set_viewport (job, &prev_viewport, NULL);
+  gsk_gl_render_job_set_projection (job, &prev_projection);
+  gsk_gl_render_job_set_alpha (job, prev_alpha);
+  gsk_gl_command_queue_bind_framebuffer (job->command_queue, prev_fbo);
 
-          sub_node = gsk_texture_scale_node_new (sub_texture, &GRAPHENE_RECT_INIT (x1, y1, x2 - x1, y2 - y1), scaling_filter);
+  texture_id = gsk_gl_driver_release_render_target (job->driver, render_target, FALSE);
 
-          gsk_gl_render_job_visit_node (job, sub_node);
-          gsk_render_node_unref (sub_node);
-          g_object_unref (sub_texture);
-        }
-    }
+  gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit));
+  gsk_gl_program_set_uniform_texture (job->current_program,
+                                      UNIFORM_SHARED_SOURCE, 0,
+                                      GL_TEXTURE_2D,
+                                      GL_TEXTURE0,
+                                      texture_id);
+  gsk_gl_render_job_draw_coords (job,
+                                 job->offset_x + clip_rect.origin.x,
+                                 job->offset_y + clip_rect.origin.y,
+                                 job->offset_x + clip_rect.origin.x + clip_rect.size.width,
+                                 job->offset_y + clip_rect.origin.y + clip_rect.size.height,
+                                 0, clip_rect.size.width / ceilf (clip_rect.size.width),
+                                 clip_rect.size.height / ceilf (clip_rect.size.height), 0,
+                                 (guint16[]){ FP16_ZERO, FP16_ZERO, FP16_ZERO, FP16_ZERO } );
+  gsk_gl_render_job_end_draw (job);
 }
 
 static inline void