summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2023-02-15 20:10:58 -0500
committerMatthias Clasen <mclasen@redhat.com>2023-03-03 11:32:08 -0600
commit3a0152b65fd75f631b76e036a53ed7aca209dfa0 (patch)
tree41b234ee043a6fd063618be031c5faac22365cf8
parent22ba6b1f33dcbcab8d42d133b7f333ca6ff44679 (diff)
downloadgtk+-3a0152b65fd75f631b76e036a53ed7aca209dfa0.tar.gz
gl: Respect clip wehn drawing scale nodes
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.
-rw-r--r--gsk/gl/gskglrenderjob.c196
1 files changed, 91 insertions, 105 deletions
diff --git a/gsk/gl/gskglrenderjob.c b/gsk/gl/gskglrenderjob.c
index 83f928ba5d..1fcd13a1e2 100644
--- a/gsk/gl/gskglrenderjob.c
+++ b/gsk/gl/gskglrenderjob.c
@@ -873,6 +873,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,
GskRoundedRect *out_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