gl renderer: Render non-trivial transforms to a texture

This way we can e.g. render rotated clips, borders, etc.
This commit is contained in:
Timm Bäder 2018-11-29 08:09:02 +01:00
parent 7c020bfaaa
commit 5907ff694f
3 changed files with 76 additions and 1 deletions

View File

@ -219,6 +219,31 @@ gsk_rounded_rect_shrink_to_minimum (GskRoundedRect *self)
MAX (self->corner[2].height, self->corner[3].height)) * 2);
}
static inline gboolean
node_supports_transform (GskRenderNode *node)
{
/* Some nodes can't handle non-trivial transforms without being
* rendered to a texture (e.g. rotated clips, etc.). Some however
* work just fine, mostly because they already draw their child
* to a texture and just render the texture manipulated in some
* way, think opacity or color matrix. */
const guint node_type = gsk_render_node_get_node_type (node);
switch (node_type)
{
case GSK_COLOR_NODE:
case GSK_OPACITY_NODE:
case GSK_COLOR_MATRIX_NODE:
case GSK_TEXTURE_NODE:
return TRUE;
default:
return FALSE;
}
return FALSE;
}
static void gsk_gl_renderer_setup_render_mode (GskGLRenderer *self);
static void add_offscreen_ops (GskGLRenderer *self,
RenderOpBuilder *builder,
@ -736,7 +761,43 @@ render_transform_node (GskGLRenderer *self,
graphene_matrix_multiply (&transform, builder->current_modelview, &transformed_mv);
ops_push_modelview (builder, &transformed_mv);
gsk_gl_renderer_add_render_ops (self, child, builder);
if (ops_modelview_is_simple (builder) ||
node_supports_transform (child))
{
gsk_gl_renderer_add_render_ops (self, child, builder);
}
else
{
const float min_x = builder->dx + child->bounds.origin.x;
const float min_y = builder->dy + child->bounds.origin.y;
const float max_x = min_x + child->bounds.size.width;
const float max_y = min_y + child->bounds.size.height;
const GskQuadVertex vertex_data[GL_N_VERTICES] = {
{ { min_x, min_y }, { 0, 1 }, },
{ { min_x, max_y }, { 0, 0 }, },
{ { max_x, min_y }, { 1, 1 }, },
{ { max_x, max_y }, { 1, 0 }, },
{ { min_x, max_y }, { 0, 0 }, },
{ { max_x, min_y }, { 1, 1 }, },
};
int texture_id;
gboolean is_offscreen;
/* For non-trivial transforms, we draw everything on a texture and then
* draw the texture transformed. */
/* TODO: We should compute a modelview containing only the "non-trivial"
* part (e.g. the rotation) and use that. We want to keep the scale
* for the texture.
*/
add_offscreen_ops (self, builder,
min_x, max_x, min_y, max_y,
child,
&texture_id, &is_offscreen,
FALSE, TRUE);
ops_set_texture (builder, texture_id);
ops_set_program (builder, &self->blit_program);
ops_draw (builder, vertex_data);
}
ops_pop_modelview (builder);
}

View File

@ -116,6 +116,19 @@ ops_transform_bounds_modelview (const RenderOpBuilder *builder,
graphene_rect_offset (dst, builder->dx, builder->dy);
}
gboolean
ops_modelview_is_simple (const RenderOpBuilder *builder)
{
const MatrixStackEntry *head;
g_assert (builder->mv_stack != NULL);
g_assert (builder->mv_stack->len >= 1);
head = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
return head->metadata.simple;
}
void
ops_set_program (RenderOpBuilder *builder,
const Program *program)

View File

@ -263,6 +263,7 @@ void ops_finish (RenderOpBuilder *builder);
void ops_push_modelview (RenderOpBuilder *builder,
const graphene_matrix_t *mv);
void ops_pop_modelview (RenderOpBuilder *builder);
gboolean ops_modelview_is_simple (const RenderOpBuilder *builder);
float ops_get_scale (const RenderOpBuilder *builder);
void ops_set_program (RenderOpBuilder *builder,