gl renderer: Add a clip stack

So we can check that the currently set clip is the first one and now
intersect with it. This first clip is always the entire viewport or the
entire render_area and we don't want to end up drawing things to a
texture because of it.
This commit is contained in:
Timm Bäder 2018-12-30 15:25:01 +01:00
parent b4f2a3416e
commit 1d3aa9207c
3 changed files with 142 additions and 45 deletions

View File

@ -895,7 +895,6 @@ render_clip_node (GskGLRenderer *self,
GskRenderNode *node,
RenderOpBuilder *builder)
{
GskRoundedRect prev_clip;
GskRenderNode *child = gsk_clip_node_get_child (node);
graphene_rect_t transformed_clip;
graphene_rect_t intersection;
@ -905,16 +904,41 @@ render_clip_node (GskGLRenderer *self,
ops_transform_bounds_modelview (builder, &transformed_clip, &transformed_clip);
graphene_rect_intersection (&transformed_clip,
&builder->current_clip.bounds,
&builder->current_clip->bounds,
&intersection);
gsk_rounded_rect_init_from_rect (&child_clip, &intersection, 0.0f);
prev_clip = ops_set_clip (builder, &child_clip);
ops_push_clip (builder, &child_clip);
gsk_gl_renderer_add_render_ops (self, child, builder);
ops_set_clip (builder, &prev_clip);
ops_pop_clip (builder);
}
static gboolean
gsk_rounded_rect_intersection (const GskRoundedRect *self,
const GskRoundedRect *other,
GskRoundedRect *out_intersection)
{
const graphene_rect_t *self_bounds = &self->bounds;
const graphene_rect_t *other_bounds = &other->bounds;
if (graphene_rect_contains_rect (self_bounds, other_bounds))
{
*out_intersection = *other;
return TRUE;
}
/* TODO: There are a few cases here that we can express using a single
* rounded rectangle, which are even interesting in every day usage.
* For example, a partially scrolled-away rounded rectangle
* might just work.
*/
return FALSE;
}
static inline void
render_rounded_clip_node (GskGLRenderer *self,
GskRenderNode *node,
@ -923,31 +947,41 @@ render_rounded_clip_node (GskGLRenderer *self,
const float scale = ops_get_scale (builder);
GskRoundedRect child_clip = *gsk_rounded_clip_node_peek_clip (node);
GskRoundedRect transformed_clip;
GskRoundedRect prev_clip;
GskRenderNode *child = gsk_rounded_clip_node_get_child (node);
GskRoundedRect intersection;
gboolean need_offscreen;
int i;
transformed_clip = child_clip;
ops_transform_bounds_modelview (builder, &child_clip.bounds, &transformed_clip.bounds);
if (graphene_rect_contains_rect (&builder->current_clip.bounds,
&transformed_clip.bounds))
if (!ops_has_clip (builder))
{
intersection = transformed_clip;
need_offscreen = FALSE;
}
else
{
need_offscreen = !gsk_rounded_rect_intersection (builder->current_clip,
&transformed_clip,
&intersection);
}
if (!need_offscreen)
{
/* If they don't intersect at all, we can simply set
* the new clip and add the render ops */
for (i = 0; i < 4; i ++)
{
transformed_clip.corner[i].width *= scale;
transformed_clip.corner[i].height *= scale;
intersection.corner[i].width *= scale;
intersection.corner[i].height *= scale;
}
prev_clip = ops_set_clip (builder, &transformed_clip);
ops_push_clip (builder, &intersection);
gsk_gl_renderer_add_render_ops (self, child, builder);
ops_set_clip (builder, &prev_clip);
ops_pop_clip (builder);
}
else if (graphene_rect_intersection (&builder->current_clip.bounds,
&transformed_clip.bounds, NULL))
else
{
const float min_x = builder->dx + node->bounds.origin.x;
const float min_y = builder->dy + node->bounds.origin.y;
@ -973,12 +1007,12 @@ render_rounded_clip_node (GskGLRenderer *self,
child_clip.corner[i].height *= scale;
}
prev_clip = ops_set_clip (builder, &child_clip);
ops_push_clip (builder, &child_clip);
add_offscreen_ops (self, builder, &node->bounds,
child,
&texture_id, &is_offscreen, TRUE, FALSE);
ops_pop_clip (builder);
ops_set_clip (builder, &prev_clip);
ops_set_program (builder, &self->blit_program);
ops_set_texture (builder, texture_id);
@ -1205,7 +1239,7 @@ render_outset_shadow_node (GskGLRenderer *self,
int texture_id, render_target;
int blurred_render_target;
int prev_render_target;
GskRoundedRect prev_clip, blit_clip;
GskRoundedRect blit_clip;
texture_id = gsk_gl_driver_create_texture (self->gl_driver, texture_width, texture_height);
gsk_gl_driver_bind_source_texture (self->gl_driver, texture_id);
@ -1228,7 +1262,7 @@ render_outset_shadow_node (GskGLRenderer *self,
/* Draw outline */
ops_set_program (builder, &self->color_program);
prev_clip = ops_set_clip (builder, &offset_outline);
ops_push_clip (builder, &offset_outline);
ops_set_color (builder, gsk_outset_shadow_node_peek_color (node));
ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
{ { 0, }, { 0, 1 }, },
@ -1246,6 +1280,7 @@ render_outset_shadow_node (GskGLRenderer *self,
blurred_render_target = gsk_gl_driver_create_render_target (self->gl_driver, blurred_texture_id, TRUE, TRUE);
ops_set_render_target (builder, blurred_render_target);
ops_pop_clip (builder);
op.op = OP_CLEAR;
ops_add (builder, &op);
@ -1259,7 +1294,7 @@ render_outset_shadow_node (GskGLRenderer *self,
op.blur.radius = blur_radius;
ops_add (builder, &op);
ops_set_clip (builder, &blit_clip);
ops_push_clip (builder, &blit_clip);
ops_set_texture (builder, texture_id);
ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
{ { 0, 0 }, { 0, 1 }, },
@ -1272,7 +1307,7 @@ render_outset_shadow_node (GskGLRenderer *self,
});
ops_set_clip (builder, &prev_clip);
ops_pop_clip (builder);
ops_set_viewport (builder, &prev_viewport);
ops_pop_modelview (builder);
ops_set_projection (builder, &prev_projection);
@ -2254,7 +2289,7 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
&node->bounds,
&transformed_node_bounds);
if (!graphene_rect_intersection (&builder->current_clip.bounds,
if (!graphene_rect_intersection (&builder->current_clip->bounds,
&transformed_node_bounds, NULL))
return;
}
@ -2384,7 +2419,6 @@ add_offscreen_ops (GskGLRenderer *self,
graphene_matrix_t prev_projection;
graphene_rect_t prev_viewport;
graphene_matrix_t item_proj;
GskRoundedRect prev_clip;
float prev_opacity;
int texture_id = 0;
@ -2444,10 +2478,10 @@ add_offscreen_ops (GskGLRenderer *self,
bounds->origin.y * scale,
width, height));
if (reset_clip)
prev_clip = ops_set_clip (builder,
&GSK_ROUNDED_RECT_INIT (bounds->origin.x * scale,
bounds->origin.y * scale,
width, height));
ops_push_clip (builder,
&GSK_ROUNDED_RECT_INIT (bounds->origin.x * scale,
bounds->origin.y * scale,
width, height));
builder->dx = 0;
builder->dy = 0;
@ -2460,7 +2494,7 @@ add_offscreen_ops (GskGLRenderer *self,
builder->dy = dy;
if (reset_clip)
ops_set_clip (builder, &prev_clip);
ops_pop_clip (builder);
ops_set_viewport (builder, &prev_viewport);
ops_pop_modelview (builder);
@ -2696,25 +2730,34 @@ gsk_gl_renderer_do_render (GskRenderer *renderer,
render_op_builder.current_opacity = 1.0f;
render_op_builder.render_ops = self->render_ops;
ops_push_modelview (&render_op_builder, &modelview);
cairo_rectangle_int_t render_extents;
/*cairo_region_get_extents (self->render_region, &render_extents);*/
/* Initial clip is self->render_region! */
if (self->render_region != NULL)
{
cairo_rectangle_int_t render_extents;
GskRoundedRect transformed_render_region = { 0, };
/*cairo_rectangle_int_t render_extents;*/
cairo_region_get_extents (self->render_region, &render_extents);
render_op_builder.current_clip = GSK_ROUNDED_RECT_INIT (render_extents.x,
render_extents.y,
render_extents.width,
render_extents.height);
ops_transform_bounds_modelview (&render_op_builder,
&render_op_builder.current_clip.bounds,
&render_op_builder.current_clip.bounds);
&GRAPHENE_RECT_INIT (render_extents.x,
render_extents.y,
render_extents.width,
render_extents.height),
&transformed_render_region.bounds);
ops_push_clip (&render_op_builder, &transformed_render_region);
}
else
{
gsk_rounded_rect_init_from_rect (&render_op_builder.current_clip, viewport, 0.0f);
ops_push_clip (&render_op_builder,
&GSK_ROUNDED_RECT_INIT (viewport->origin.x,
viewport->origin.y,
viewport->size.width,
viewport->size.height));
}
/*gsk_rounded_rect_init_from_rect (&render_op_builder.current_clip, viewport, 0.0f);*/
if (fbo_id != 0)
@ -2722,9 +2765,18 @@ gsk_gl_renderer_do_render (GskRenderer *renderer,
gsk_gl_renderer_add_render_ops (self, root, &render_op_builder);
/*if (self->render_region)*/
/*add_rect_outline_ops (self, &render_op_builder,*/
/*&GRAPHENE_RECT_INIT (*/
/*render_extents.x,*/
/*render_extents.y,*/
/*render_extents.width,*/
/*render_extents.height));*/
/* We correctly reset the state everywhere */
g_assert_cmpint (render_op_builder.current_render_target, ==, fbo_id);
ops_pop_modelview (&render_op_builder);
ops_pop_clip (&render_op_builder);
ops_finish (&render_op_builder);
/*g_message ("Ops: %u", self->render_ops->len);*/

View File

@ -12,6 +12,9 @@ ops_finish (RenderOpBuilder *builder)
{
if (builder->mv_stack)
g_array_free (builder->mv_stack, TRUE);
if (builder->clip_stack)
g_array_free (builder->clip_stack, TRUE);
}
static inline void
@ -208,9 +211,9 @@ ops_set_program (RenderOpBuilder *builder,
memcmp (&builder->current_clip, &program_state->clip, sizeof (GskRoundedRect)) != 0)
{
op.op = OP_CHANGE_CLIP;
op.clip = builder->current_clip;
op.clip = *builder->current_clip;
g_array_append_val (builder->render_ops, op);
program_state->clip = builder->current_clip;
program_state->clip = *builder->current_clip;
}
if (program_state->opacity != builder->current_opacity)
@ -224,12 +227,11 @@ ops_set_program (RenderOpBuilder *builder,
builder->current_program_state = &builder->program_state[program->index];
}
GskRoundedRect
static void
ops_set_clip (RenderOpBuilder *builder,
const GskRoundedRect *clip)
{
RenderOp *last_op;
GskRoundedRect prev_clip;
if (builder->render_ops->len > 0)
{
@ -251,11 +253,49 @@ ops_set_clip (RenderOpBuilder *builder,
if (builder->current_program != NULL)
builder->current_program_state->clip = *clip;
}
prev_clip = builder->current_clip;
builder->current_clip = *clip;
void
ops_push_clip (RenderOpBuilder *self,
const GskRoundedRect *clip)
{
if (G_UNLIKELY (self->clip_stack == NULL))
self->clip_stack = g_array_new (FALSE, TRUE, sizeof (GskRoundedRect));
return prev_clip;
g_assert (self->clip_stack != NULL);
g_array_append_val (self->clip_stack, *clip);
self->current_clip = &g_array_index (self->clip_stack, GskRoundedRect, self->clip_stack->len - 1);
ops_set_clip (self, clip);
}
void
ops_pop_clip (RenderOpBuilder *self)
{
const GskRoundedRect *head;
g_assert (self->clip_stack);
g_assert (self->clip_stack->len >= 1);
self->clip_stack->len --;
head = &g_array_index (self->clip_stack, GskRoundedRect, self->clip_stack->len - 1);
if (self->clip_stack->len >= 1)
{
self->current_clip = head;
ops_set_clip (self, head);
}
else
{
self->current_clip = NULL;
}
}
gboolean
ops_has_clip (RenderOpBuilder *self)
{
return self->clip_stack != NULL &&
self->clip_stack->len > 1;
}
static void
@ -301,7 +341,7 @@ ops_push_modelview (RenderOpBuilder *builder,
MatrixStackEntry *entry;
if (G_UNLIKELY (builder->mv_stack == NULL))
builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry));
builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry));
g_assert (builder->mv_stack != NULL);

View File

@ -248,7 +248,6 @@ typedef struct
const Program *current_program;
int current_render_target;
int current_texture;
GskRoundedRect current_clip;
graphene_matrix_t current_projection;
graphene_rect_t current_viewport;
@ -264,6 +263,10 @@ typedef struct
GArray *mv_stack;
/* Pointer into mv_stack */
const graphene_matrix_t *current_modelview;
/* Same thing */
GArray *clip_stack;
const GskRoundedRect *current_clip;
} RenderOpBuilder;
@ -282,8 +285,10 @@ float ops_get_scale (const RenderOpBuilder *builder);
void ops_set_program (RenderOpBuilder *builder,
const Program *program);
GskRoundedRect ops_set_clip (RenderOpBuilder *builder,
void ops_push_clip (RenderOpBuilder *builder,
const GskRoundedRect *clip);
void ops_pop_clip (RenderOpBuilder *builder);
gboolean ops_has_clip (RenderOpBuilder *builder);
void ops_transform_bounds_modelview (const RenderOpBuilder *builder,
const graphene_rect_t *src,