[gtk/wip/baedert/for-master: 64/64] gl renderer: Replace modelview matrix with just scale




commit 81df926f57f582b58c226ff3ed5504fa4138c6b8
Author: Timm Bäder <mail baedert org>
Date:   Sun Jan 24 08:38:01 2021 +0100

    gl renderer: Replace modelview matrix with just scale
    
    We don't support non-affine transforms in the general case. If we see a
    non-affine transform node, just use a special shader to render it.

 gsk/gl/gskglrenderer.c                          | 162 +++++++---------
 gsk/gl/gskglrenderops.c                         | 234 +++++++++---------------
 gsk/gl/gskglrenderopsprivate.h                  |  39 ++--
 gsk/gl/opbuffer.c                               |   1 +
 gsk/gl/opbuffer.h                               |   8 +-
 gsk/gskrendernodeimpl.c                         |  37 +++-
 gsk/meson.build                                 |   1 +
 gsk/resources/glsl/blend.glsl                   |   2 +-
 gsk/resources/glsl/blit.glsl                    |   2 +-
 gsk/resources/glsl/blur.glsl                    |   2 +-
 gsk/resources/glsl/border.glsl                  |   6 +-
 gsk/resources/glsl/color.glsl                   |   2 +-
 gsk/resources/glsl/color_matrix.glsl            |   2 +-
 gsk/resources/glsl/coloring.glsl                |   2 +-
 gsk/resources/glsl/conic_gradient.glsl          |   7 +-
 gsk/resources/glsl/cross_fade.glsl              |   2 +-
 gsk/resources/glsl/custom.glsl                  |   3 +-
 gsk/resources/glsl/inset_shadow.glsl            |   6 +-
 gsk/resources/glsl/linear_gradient.glsl         |  10 +-
 gsk/resources/glsl/outset_shadow.glsl           |   4 +-
 gsk/resources/glsl/preamble.fs.glsl             |   2 +-
 gsk/resources/glsl/preamble.vs.glsl             |  23 ++-
 gsk/resources/glsl/radial_gradient.glsl         |   7 +-
 gsk/resources/glsl/repeat.glsl                  |   2 +-
 gsk/resources/glsl/transform.glsl               |  22 +++
 gsk/resources/glsl/unblurred_outset_shadow.glsl |   7 +-
 26 files changed, 288 insertions(+), 307 deletions(-)
---
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index 7f1aa2c6cd..914e3d0b7b 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -68,9 +68,9 @@
               }G_STMT_END
 
 static Program *gsk_gl_renderer_lookup_custom_program (GskGLRenderer  *self,
-                                                       GskGLShader *shader);
+                                                       GskGLShader    *shader);
 static Program *gsk_gl_renderer_create_custom_program (GskGLRenderer  *self,
-                                                       GskGLShader *shader);
+                                                       GskGLShader    *shader);
 
 typedef enum
 {
@@ -328,37 +328,6 @@ gsk_rounded_rect_shrink_to_minimum (GskRoundedRect *self)
                                   self->corner[1].height + self->corner[2].height);
 }
 
-static inline gboolean G_GNUC_PURE
-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:
-      case GSK_CROSS_FADE_NODE:
-      case GSK_LINEAR_GRADIENT_NODE:
-      case GSK_DEBUG_NODE:
-      case GSK_TEXT_NODE:
-        return TRUE;
-
-      case GSK_TRANSFORM_NODE:
-        return node_supports_transform (gsk_transform_node_get_child (node));
-
-      default:
-        return FALSE;
-    }
-  return FALSE;
-}
-
 static inline void
 load_vertex_data_with_region (GskQuadVertex          vertex_data[GL_N_VERTICES],
                               const graphene_rect_t *bounds,
@@ -1150,7 +1119,7 @@ compile_glshader (GskGLRenderer  *self,
   INIT_COMMON_UNIFORM_LOCATION (program, clip_rect);
   INIT_COMMON_UNIFORM_LOCATION (program, viewport);
   INIT_COMMON_UNIFORM_LOCATION (program, projection);
-  INIT_COMMON_UNIFORM_LOCATION (program, modelview);
+  INIT_COMMON_UNIFORM_LOCATION (program, scale);
   program->glshader.size_location = glGetUniformLocation(program->id, "u_size");
   program->glshader.texture_locations[0] = glGetUniformLocation(program->id, "u_texture1");
   program->glshader.texture_locations[1] = glGetUniformLocation(program->id, "u_texture2");
@@ -1328,12 +1297,18 @@ render_transform_node (GskGLRenderer   *self,
 
     case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
       {
-        ops_push_modelview (builder, node_transform);
+        float dx, dy, scale_x, scale_y;
+
+        gsk_transform_to_affine (node_transform, &scale_x, &scale_y, &dx, &dy);
+
+        ops_push_modelview (builder, dx, dy, scale_x, scale_y);
         gsk_gl_renderer_add_render_ops (self, child, builder);
         ops_pop_modelview (builder);
       }
     break;
 
+    /* For non-affine transforms, we draw everything on a texture and then
+     * draw the texture transformed. */
     case GSK_TRANSFORM_CATEGORY_2D:
     case GSK_TRANSFORM_CATEGORY_3D:
     case GSK_TRANSFORM_CATEGORY_ANY:
@@ -1341,42 +1316,34 @@ render_transform_node (GskGLRenderer   *self,
       {
         TextureRegion region;
         gboolean is_offscreen;
+        int filter_flag = 0;
 
-        if (node_supports_transform (child))
-          {
-            ops_push_modelview (builder, node_transform);
-            gsk_gl_renderer_add_render_ops (self, child, builder);
-            ops_pop_modelview (builder);
-          }
-        else
+        if (!result_is_axis_aligned (node_transform, &child->bounds))
+          filter_flag = LINEAR_FILTER;
+
+        if (add_offscreen_ops (self, builder,
+                               &child->bounds,
+                               child,
+                               &region, &is_offscreen,
+                               RESET_CLIP | filter_flag))
           {
-            int filter_flag = 0;
-
-            if (!result_is_axis_aligned (node_transform, &child->bounds))
-              filter_flag = LINEAR_FILTER;
-
-            if (add_offscreen_ops (self, builder,
-                                   &child->bounds,
-                                   child,
-                                   &region, &is_offscreen,
-                                   RESET_CLIP | filter_flag))
-              {
-                /* 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.
-                 */
-                ops_push_modelview (builder, node_transform);
-                ops_set_texture (builder, region.texture_id);
-                ops_set_program (builder, &self->programs->blit_program);
-
-                load_vertex_data_with_region (ops_draw (builder, NULL),
-                                              &child->bounds, builder,
-                                              &region,
-                                              is_offscreen);
-                ops_pop_modelview (builder);
-              }
+            const float dx = builder->dx;
+            const float dy = builder->dy;
+
+            ops_set_program (builder, &self->programs->transform_program);
+            ops_set_transform (builder, node_transform);
+            ops_set_texture (builder, region.texture_id);
+
+            /* Quickly resetting these since ops_set_transform() adds them to the matrix
+             * and we don't want the geometry we're about to add picking the wrong offset */
+            builder->dx = 0;
+            builder->dy = 0;
+            load_vertex_data_with_region (ops_draw (builder, NULL),
+                                          &child->bounds, builder,
+                                          &region,
+                                          is_offscreen);
+            builder->dx = dx;
+            builder->dy = dy;
           }
       }
       break;
@@ -1845,7 +1812,7 @@ blur_texture (GskGLRenderer       *self,
 
   ops_set_program (builder, &self->programs->blur_program);
   prev_projection = ops_set_projection (builder, &item_proj);
-  ops_set_modelview (builder, NULL);
+  ops_set_modelview (builder, 0, 0, 1, 1);
   prev_viewport = ops_set_viewport (builder, &new_clip.bounds);
   ops_push_clip (builder, &new_clip);
 
@@ -2093,7 +2060,7 @@ render_inset_shadow_node (GskGLRenderer   *self,
                               &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height));
 
       prev_projection = ops_set_projection (builder, &item_proj);
-      ops_set_modelview (builder, NULL);
+      ops_set_modelview (builder, 0, 0, 1, 1);
       prev_viewport = ops_set_viewport (builder, &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height));
       ops_push_clip (builder, &GSK_ROUNDED_RECT_INIT (0, 0, texture_width, texture_height));
 
@@ -2284,6 +2251,9 @@ render_outset_shadow_node (GskGLRenderer   *self,
   texture_width  = (int)ceil ((scaled_outline.bounds.size.width  + blur_extra) * scale_x);
   texture_height = (int)ceil ((scaled_outline.bounds.size.height + blur_extra) * scale_y);
 
+  if (texture_width <= 0 || texture_height <= 0)
+    return;
+
   scaled_outline.bounds.origin.x = extra_blur_pixels;
   scaled_outline.bounds.origin.y = extra_blur_pixels;
   scaled_outline.bounds.size.width = texture_width - (extra_blur_pixels * 2);
@@ -2327,7 +2297,7 @@ render_outset_shadow_node (GskGLRenderer   *self,
       prev_render_target = ops_set_render_target (builder, render_target);
       ops_begin (builder, OP_CLEAR);
       prev_projection = ops_set_projection (builder, &item_proj);
-      ops_set_modelview (builder, NULL);
+      ops_set_modelview (builder, 0, 0, 1, 1);
       prev_viewport = ops_set_viewport (builder, &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height));
 
       /* Draw outline */
@@ -2835,18 +2805,26 @@ apply_viewport_op (const Program    *program,
 }
 
 static inline void
-apply_modelview_op (const Program  *program,
+apply_scale_op (const Program *program,
+                const OpScale *op)
+{
+  OP_PRINT (" -> Scale %f, %f", op->scale[0], op->scale[1]);
+  glUniform2f (program->scale_location, op->scale[0], op->scale[1]);
+}
+
+static inline void
+apply_transform_op (const Program  *program,
                     const OpMatrix *op)
 {
   float mat[16];
 
   graphene_matrix_to_float (&op->matrix, mat);
-  OP_PRINT (" -> Modelview { { %f,%f,%f,%f }, { %f,%f,%f,%f }, { %f,%f,%f,%f }, { %f,%f,%f,%f }",
+  OP_PRINT (" -> Transform { { %f,%f,%f,%f }, { %f,%f,%f,%f }, { %f,%f,%f,%f }, { %f,%f,%f,%f }",
             mat[0], mat[1], mat[2], mat[3],
             mat[4], mat[5], mat[6], mat[7],
             mat[8], mat[9], mat[10], mat[11],
             mat[12], mat[13], mat[14], mat[15]);
-  glUniformMatrix4fv (program->modelview_location, 1, GL_FALSE, mat);
+  glUniformMatrix4fv (program->transform.transform_location, 1, GL_FALSE, mat);
 }
 
 static inline void
@@ -2865,7 +2843,7 @@ apply_projection_op (const Program  *program,
 }
 
 static inline void
-apply_program_op (const Program  *program,
+apply_program_op (const Program   *program,
                   const OpProgram *op)
 {
   OP_PRINT (" -> Program: %d", op->program->index);
@@ -3335,6 +3313,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer  *self,
     { "/org/gtk/libgsk/glsl/outset_shadow.glsl",             "outset shadow" },
     { "/org/gtk/libgsk/glsl/repeat.glsl",                    "repeat" },
     { "/org/gtk/libgsk/glsl/unblurred_outset_shadow.glsl",   "unblurred_outset shadow" },
+    { "/org/gtk/libgsk/glsl/transform.glsl",                 "transform" },
   };
 
   gsk_gl_shader_builder_init (&shader_builder,
@@ -3368,7 +3347,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer  *self,
       INIT_COMMON_UNIFORM_LOCATION (prog, clip_rect);
       INIT_COMMON_UNIFORM_LOCATION (prog, viewport);
       INIT_COMMON_UNIFORM_LOCATION (prog, projection);
-      INIT_COMMON_UNIFORM_LOCATION (prog, modelview);
+      INIT_COMMON_UNIFORM_LOCATION (prog, scale);
     }
   /* color */
   INIT_PROGRAM_UNIFORM_LOCATION (color, color);
@@ -3436,6 +3415,8 @@ gsk_gl_renderer_create_programs (GskGLRenderer  *self,
   INIT_PROGRAM_UNIFORM_LOCATION (repeat, child_bounds);
   INIT_PROGRAM_UNIFORM_LOCATION (repeat, texture_rect);
 
+  /* transform */
+  INIT_PROGRAM_UNIFORM_LOCATION (transform, transform);
 
   /* We initialize the alpha uniform here, since the default value is important.
    * We can't do it in the shader like a reasonable person would because that doesn't
@@ -3444,6 +3425,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer  *self,
     {
       glUseProgram(programs->programs[i].id);
       glUniform1f (programs->programs[i].alpha_location, 1.0);
+      glUniform2f (programs->programs[i].scale_location, 1.0, 1.0);
     }
 
 out:
@@ -3941,14 +3923,10 @@ add_offscreen_ops (GskGLRenderer         *self,
   /* Clear since we use this rendertarget for the first time */
   ops_begin (builder, OP_CLEAR);
   prev_projection = ops_set_projection (builder, &item_proj);
-  ops_set_modelview (builder, gsk_transform_scale (NULL, scale_x, scale_y));
   prev_viewport = ops_set_viewport (builder, &viewport);
   if (flags & RESET_CLIP)
     ops_push_clip (builder, &GSK_ROUNDED_RECT_INIT_FROM_RECT (viewport));
 
-  builder->dx = dx;
-  builder->dy = dy;
-
   prev_opacity = ops_set_opacity (builder, 1.0);
 
   gsk_gl_renderer_add_render_ops (self, child_node, builder);
@@ -3968,14 +3946,10 @@ add_offscreen_ops (GskGLRenderer         *self,
 
   ops_set_opacity (builder, prev_opacity);
 
-  builder->dx = dx;
-  builder->dy = dy;
-
   if (flags & RESET_CLIP)
     ops_pop_clip (builder);
 
   ops_set_viewport (builder, &prev_viewport);
-  ops_pop_modelview (builder);
   ops_set_projection (builder, &prev_projection);
   ops_set_render_target (builder, prev_render_target);
 
@@ -4040,12 +4014,16 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self)
 
       switch (kind)
         {
-        case OP_CHANGE_PROJECTION:
-          apply_projection_op (program, ptr);
+        case OP_CHANGE_TRANSFORM:
+          apply_transform_op (program, ptr);
           break;
 
-        case OP_CHANGE_MODELVIEW:
-          apply_modelview_op (program, ptr);
+        case OP_CHANGE_SCALE:
+          apply_scale_op (program, ptr);
+          break;
+
+        case OP_CHANGE_PROJECTION:
+          apply_projection_op (program, ptr);
           break;
 
         case OP_CHANGE_PROGRAM:
@@ -4237,7 +4215,9 @@ gsk_gl_renderer_do_render (GskRenderer           *renderer,
   init_projection_matrix (&projection, viewport);
   ops_set_projection (&self->op_builder, &projection);
   ops_set_viewport (&self->op_builder, viewport);
-  ops_set_modelview (&self->op_builder, gsk_transform_scale (NULL, scale_factor, scale_factor));
+  ops_set_modelview (&self->op_builder, 0, 0, scale_factor, scale_factor);
+  g_assert_cmpfloat (self->op_builder.scale_x, ==, scale_factor);
+  g_assert_cmpfloat (self->op_builder.scale_y, ==, scale_factor);
 
   /* Initial clip is self->render_region! */
   if (self->render_region != NULL)
@@ -4419,7 +4399,7 @@ gsk_gl_renderer_render_texture (GskRenderer           *renderer,
 
     ops_begin (&self->op_builder, OP_CLEAR);
     ops_set_texture (&self->op_builder, texture_id);
-    ops_set_modelview (&self->op_builder, NULL);
+    ops_set_modelview (&self->op_builder, 0, 0, 1, 1);
     ops_set_viewport (&self->op_builder, &GRAPHENE_RECT_INIT (0, 0, width, height));
     init_projection_matrix (&m, &GRAPHENE_RECT_INIT (0, 0, width, height));
     graphene_matrix_scale (&m, 1, -1, 1); /* Undo the scale init_projection_matrix() does again */
diff --git a/gsk/gl/gskglrenderops.c b/gsk/gl/gskglrenderops.c
index 36f7e37e35..ffadef0ad2 100644
--- a/gsk/gl/gskglrenderops.c
+++ b/gsk/gl/gskglrenderops.c
@@ -79,9 +79,8 @@ ops_finish (RenderOpBuilder *builder)
   builder->dy = 0;
   builder->scale_x = 1;
   builder->scale_y = 1;
-  builder->current_modelview = NULL;
   builder->current_clip = NULL;
-  builder->clip_is_rectilinear = TRUE;
+  builder->clip_is_rectilinear = true;
   builder->current_render_target = 0;
   builder->current_texture = 0;
   builder->current_program = NULL;
@@ -121,83 +120,33 @@ ops_pop_debug_group (RenderOpBuilder *builder)
   ops_begin (builder, OP_POP_DEBUG_GROUP);
 }
 
-static void
-extract_matrix_metadata (GskTransform      *transform,
-                         OpsMatrixMetadata *md)
-{
-  float dummy;
-
-  switch (gsk_transform_get_category (transform))
-    {
-    case GSK_TRANSFORM_CATEGORY_IDENTITY:
-    case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
-      md->scale_x = 1;
-      md->scale_y = 1;
-    break;
-
-    case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
-      gsk_transform_to_affine (transform,
-                               &md->scale_x, &md->scale_y,
-                               &dummy, &dummy);
-    break;
-
-    case GSK_TRANSFORM_CATEGORY_UNKNOWN:
-    case GSK_TRANSFORM_CATEGORY_ANY:
-    case GSK_TRANSFORM_CATEGORY_3D:
-    case GSK_TRANSFORM_CATEGORY_2D:
-      {
-        graphene_vec3_t col1;
-        graphene_vec3_t col2;
-        graphene_matrix_t m;
-
-        gsk_transform_to_matrix (transform, &m);
-
-        /* TODO: 90% sure this is incorrect. But we should never hit this code
-         * path anyway. */
-        graphene_vec3_init (&col1,
-                            graphene_matrix_get_value (&m, 0, 0),
-                            graphene_matrix_get_value (&m, 1, 0),
-                            graphene_matrix_get_value (&m, 2, 0));
-
-        graphene_vec3_init (&col2,
-                            graphene_matrix_get_value (&m, 0, 1),
-                            graphene_matrix_get_value (&m, 1, 1),
-                            graphene_matrix_get_value (&m, 2, 1));
-
-        md->scale_x = graphene_vec3_length (&col1);
-        md->scale_y = graphene_vec3_length (&col2);
-      }
-    break;
-    default:
-      {}
-    }
-}
-
 void
-ops_transform_bounds_modelview (const RenderOpBuilder *builder,
+ops_transform_bounds_modelview (const RenderOpBuilder *self,
                                 const graphene_rect_t *src,
                                 graphene_rect_t       *dst)
 {
-  graphene_rect_t r = *src;
-
-  g_assert (builder->mv_stack != NULL);
-  g_assert (builder->mv_stack->len >= 1);
+  g_assert (self->mv_stack != NULL);
+  g_assert (self->mv_stack->len >= 1);
+  g_assert (src);
+  g_assert (dst);
 
-  r.origin.x += builder->dx;
-  r.origin.y += builder->dy;
-
-  gsk_transform_transform_bounds (builder->current_modelview, &r, dst);
+  dst->origin.x = (src->origin.x + self->dx) * self->scale_x;
+  dst->origin.y = (src->origin.y + self->dy) * self->scale_y;
+  dst->size.width = src->size.width * self->scale_x;
+  dst->size.height = src->size.height * self->scale_y;
 }
 
 void
-ops_init (RenderOpBuilder *builder)
+ops_init (RenderOpBuilder *self)
 {
-  memset (builder, 0, sizeof (*builder));
+  memset (self, 0, sizeof (*self));
 
-  builder->current_opacity = 1.0f;
+  self->current_opacity = 1.0f;
+  self->scale_x = 1.0f;
+  self->scale_y = 1.0f;
 
-  op_buffer_init (&builder->render_ops);
-  builder->vertices = g_array_new (FALSE, TRUE, sizeof (GskQuadVertex));
+  op_buffer_init (&self->render_ops);
+  self->vertices = g_array_new (FALSE, TRUE, sizeof (GskQuadVertex));
 }
 
 void
@@ -270,6 +219,22 @@ ops_has_clip (RenderOpBuilder *self)
          self->clip_stack->len > 1;
 }
 
+void
+ops_set_transform (RenderOpBuilder *self,
+                   GskTransform    *transform)
+{
+  OpMatrix *op;
+
+  op = ops_begin (self, OP_CHANGE_TRANSFORM);
+
+  g_assert (gsk_transform_get_category (transform) < GSK_TRANSFORM_CATEGORY_2D_AFFINE);
+
+  gsk_transform_to_matrix (transform, &op->matrix);
+
+  /* Apply our own offset */
+  graphene_matrix_translate (&op->matrix, &(graphene_point3d_t) { self->dx, self->dy, 0 });
+}
+
 /**
  * ops_set_modelview:
  * @builder
@@ -278,103 +243,83 @@ ops_has_clip (RenderOpBuilder *self)
  * This sets the modelview to the given one without looking at the
  * one that's currently set */
 void
-ops_set_modelview (RenderOpBuilder *builder,
-                   GskTransform    *transform)
+ops_set_modelview (RenderOpBuilder *self,
+                   const float      dx,
+                   const float      dy,
+                   const float      scale_x,
+                   const float      scale_y)
+
 {
-  MatrixStackEntry *entry;
+  ModelviewState *entry;
 
-  if (G_UNLIKELY (builder->mv_stack == NULL))
-    builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry));
+  g_assert (self);
 
-  g_assert (builder->mv_stack != NULL);
+  if (G_UNLIKELY (self->mv_stack == NULL))
+    self->mv_stack = g_array_new (false, true, sizeof (ModelviewState));
 
-  g_array_set_size (builder->mv_stack, builder->mv_stack->len + 1);
-  entry = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
+  g_assert (self->mv_stack != NULL);
 
-  entry->transform = transform;
+  g_array_set_size (self->mv_stack, self->mv_stack->len + 1);
+  entry = &g_array_index (self->mv_stack, ModelviewState, self->mv_stack->len - 1);
 
-  entry->metadata.dx_before = builder->dx;
-  entry->metadata.dy_before = builder->dy;
-  extract_matrix_metadata (entry->transform, &entry->metadata);
+  entry->dx_before = self->dx;
+  entry->dy_before = self->dy;
+  entry->scale_x_before = self->scale_x;
+  entry->scale_y_before = self->scale_y;
 
-  builder->dx = 0;
-  builder->dy = 0;
-  builder->current_modelview = entry->transform;
-  builder->scale_x = entry->metadata.scale_x;
-  builder->scale_y = entry->metadata.scale_y;
+  self->dx = dx;
+  self->dy = dy;
+  self->scale_x = scale_x;
+  self->scale_y = scale_y;
 }
 
-/* This sets the given modelview to the one we get when multiplying
- * the given modelview with the current one. */
 void
-ops_push_modelview (RenderOpBuilder *builder,
-                    GskTransform    *transform)
+ops_push_modelview (RenderOpBuilder *self,
+                    const float      dx,
+                    const float      dy,
+                    const float      scale_x,
+                    const float      scale_y)
 {
-  MatrixStackEntry *entry;
-
-  if (G_UNLIKELY (builder->mv_stack == NULL))
-    builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry));
+  ModelviewState *entry;
 
-  g_assert (builder->mv_stack != NULL);
+  g_assert (self);
 
-  g_array_set_size (builder->mv_stack, builder->mv_stack->len + 1);
-  entry = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
-
-  if (G_LIKELY (builder->mv_stack->len >= 2))
-    {
-      const MatrixStackEntry *cur;
-      GskTransform *t = NULL;
+  if (G_UNLIKELY (self->mv_stack == NULL))
+    self->mv_stack = g_array_new (false, true, sizeof (ModelviewState));
 
-      cur = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 2);
-      /* Multiply given matrix with current modelview */
+  g_assert (self->mv_stack != NULL);
 
-      t = gsk_transform_translate (gsk_transform_ref (cur->transform),
-                                   &(graphene_point_t) { builder->dx, builder->dy});
-      t = gsk_transform_transform (t, transform);
-      entry->transform = t;
-    }
-  else
-    {
-      entry->transform = gsk_transform_ref (transform);
-    }
+  g_array_set_size (self->mv_stack, self->mv_stack->len + 1);
+  entry = &g_array_index (self->mv_stack, ModelviewState, self->mv_stack->len - 1);
 
-  entry->metadata.dx_before = builder->dx;
-  entry->metadata.dy_before = builder->dy;
-  extract_matrix_metadata (entry->transform, &entry->metadata);
+  entry->dx_before = self->dx;
+  entry->dy_before = self->dy;
+  entry->scale_x_before = self->scale_x;
+  entry->scale_y_before = self->scale_y;
 
-  builder->dx = 0;
-  builder->dy = 0;
-  builder->scale_x = entry->metadata.scale_x;
-  builder->scale_y = entry->metadata.scale_y;
-  builder->current_modelview = entry->transform;
+  /* This version is additive */
+  self->dx += dx;
+  self->dy += dy;
+  self->scale_x = scale_x * self->scale_x;
+  self->scale_y = scale_y * self->scale_y;
 }
 
 void
 ops_pop_modelview (RenderOpBuilder *builder)
 {
-  const MatrixStackEntry *head;
+  const ModelviewState *head;
 
   g_assert (builder->mv_stack);
   g_assert (builder->mv_stack->len >= 1);
 
-  head = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
-  builder->dx = head->metadata.dx_before;
-  builder->dy = head->metadata.dy_before;
-  gsk_transform_unref (head->transform);
+  head = &g_array_index (builder->mv_stack, ModelviewState, builder->mv_stack->len - 1);
+  builder->dx = head->dx_before;
+  builder->dy = head->dy_before;
+  builder->scale_x = head->scale_x_before;
+  builder->scale_y = head->scale_y_before;
 
   builder->mv_stack->len --;
-  head = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
-
-  if (builder->mv_stack->len >= 1)
-    {
-      builder->scale_x = head->metadata.scale_x;
-      builder->scale_y = head->metadata.scale_y;
-      builder->current_modelview = head->transform;
-    }
-  else
-    {
-      builder->current_modelview = NULL;
-    }
+  head = &g_array_index (builder->mv_stack, ModelviewState, builder->mv_stack->len - 1);
 }
 
 graphene_matrix_t
@@ -635,15 +580,14 @@ ops_draw (RenderOpBuilder     *builder,
       program_state->projection = builder->current_projection;
     }
 
-  if (program_state->modelview == NULL ||
-      !gsk_transform_equal (builder->current_modelview, program_state->modelview))
+  if (program_state->scale_x != builder->scale_x ||
+      program_state->scale_y != builder->scale_y)
     {
-      OpMatrix *opm;
-
-      opm = ops_begin (builder, OP_CHANGE_MODELVIEW);
-      gsk_transform_to_matrix (builder->current_modelview, &opm->matrix);
-      gsk_transform_unref (program_state->modelview);
-      program_state->modelview = gsk_transform_ref (builder->current_modelview);
+      OpScale *op_s = ops_begin (builder, OP_CHANGE_SCALE);
+      op_s->scale[0] = builder->scale_x;
+      op_s->scale[1] = builder->scale_y;
+      program_state->scale_x = builder->scale_x;
+      program_state->scale_y = builder->scale_y;
     }
 
   if (!rect_equal (&builder->current_viewport, &program_state->viewport))
diff --git a/gsk/gl/gskglrenderopsprivate.h b/gsk/gl/gskglrenderopsprivate.h
index b23060014b..acf1f42851 100644
--- a/gsk/gl/gskglrenderopsprivate.h
+++ b/gsk/gl/gskglrenderopsprivate.h
@@ -13,23 +13,17 @@
 #include "opbuffer.h"
 
 #define GL_N_VERTICES 6
-#define GL_N_PROGRAMS 15
+#define GL_N_PROGRAMS 16
 #define GL_MAX_GRADIENT_STOPS 6
 
 typedef struct
 {
-  float scale_x;
-  float scale_y;
+  float scale_x_before;
+  float scale_y_before;
 
   float dx_before;
   float dy_before;
-} OpsMatrixMetadata;
-
-typedef struct
-{
-  GskTransform *transform;
-  OpsMatrixMetadata metadata;
-} MatrixStackEntry;
+} ModelviewState;
 
 typedef struct
 {
@@ -39,6 +33,9 @@ typedef struct
   int source_texture;
   graphene_rect_t viewport;
   float opacity;
+  float scale_x;
+  float scale_y;
+
   /* Per-program state */
   union {
     GdkRGBA color;
@@ -104,6 +101,7 @@ struct _Program
   int projection_location;
   int modelview_location;
   int clip_rect_location;
+  int scale_location;
   union {
     struct {
       int color_location;
@@ -177,6 +175,9 @@ struct _Program
       int texture_locations[4];
       GError *compile_error;
     } glshader;
+    struct {
+      int transform_location;
+    } transform;
   };
   ProgramState state;
 };
@@ -201,6 +202,7 @@ typedef struct {
       Program outset_shadow_program;
       Program repeat_program;
       Program unblurred_outset_shadow_program;
+      Program transform_program;
     };
   };
   GHashTable *custom_programs; /* GskGLShader -> Program* */
@@ -216,6 +218,7 @@ typedef struct
   graphene_matrix_t current_projection;
   graphene_rect_t current_viewport;
   float current_opacity;
+
   float dx, dy;
   float scale_x, scale_y;
 
@@ -226,7 +229,6 @@ typedef struct
 
   /* Stack of modelview matrices */
   GArray *mv_stack;
-  GskTransform *current_modelview;
 
   /* Same thing */
   GArray *clip_stack;
@@ -248,9 +250,17 @@ void              ops_push_debug_group    (RenderOpBuilder         *builder,
 void              ops_pop_debug_group     (RenderOpBuilder         *builder);
 
 void              ops_finish             (RenderOpBuilder         *builder);
-void              ops_push_modelview     (RenderOpBuilder         *builder,
-                                          GskTransform            *transform);
-void              ops_set_modelview      (RenderOpBuilder         *builder,
+void              ops_push_modelview     (RenderOpBuilder         *self,
+                                          const float              dx,
+                                          const float              dy,
+                                          const float              scale_x,
+                                          const float              scale_y);
+void              ops_set_modelview      (RenderOpBuilder         *self,
+                                          const float              dx,
+                                          const float              dy,
+                                          const float              scale_x,
+                                          const float              scale_y);
+void              ops_set_transform      (RenderOpBuilder         *self,
                                           GskTransform            *transform);
 void              ops_pop_modelview      (RenderOpBuilder         *builder);
 void              ops_set_program        (RenderOpBuilder         *builder,
@@ -345,7 +355,6 @@ GskQuadVertex *   ops_draw               (RenderOpBuilder        *builder,
 void              ops_offset             (RenderOpBuilder        *builder,
                                           float                   x,
                                           float                   y);
-
 gpointer          ops_begin              (RenderOpBuilder        *builder,
                                           OpKind                  kind);
 OpBuffer         *ops_get_buffer         (RenderOpBuilder        *builder);
diff --git a/gsk/gl/opbuffer.c b/gsk/gl/opbuffer.c
index 806b8f7ca4..0024ad9246 100644
--- a/gsk/gl/opbuffer.c
+++ b/gsk/gl/opbuffer.c
@@ -34,6 +34,7 @@ static guint op_sizes[OP_LAST] = {
   sizeof (OpGLShader),
   sizeof (OpExtraTexture),
   sizeof (OpConicGradient),
+  sizeof (OpScale),
 };
 
 void
diff --git a/gsk/gl/opbuffer.h b/gsk/gl/opbuffer.h
index ea954249c5..6557876a34 100644
--- a/gsk/gl/opbuffer.h
+++ b/gsk/gl/opbuffer.h
@@ -15,7 +15,7 @@ typedef enum
   OP_CHANGE_OPACITY                    =  1,
   OP_CHANGE_COLOR                      =  2,
   OP_CHANGE_PROJECTION                 =  3,
-  OP_CHANGE_MODELVIEW                  =  4,
+  OP_CHANGE_TRANSFORM                  =  4,
   OP_CHANGE_PROGRAM                    =  5,
   OP_CHANGE_RENDER_TARGET              =  6,
   OP_CHANGE_CLIP                       =  7,
@@ -42,6 +42,7 @@ typedef enum
   OP_CHANGE_GL_SHADER_ARGS             = 28,
   OP_CHANGE_EXTRA_SOURCE_TEXTURE       = 29,
   OP_CHANGE_CONIC_GRADIENT             = 30,
+  OP_CHANGE_SCALE                      = 31,
   OP_LAST
 } OpKind;
 
@@ -97,6 +98,11 @@ typedef struct
   graphene_matrix_t matrix;
 } OpMatrix;
 
+typedef struct
+{
+  float scale[2];
+} OpScale;
+
 typedef struct
 {
   const Program *program;
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index f932c38185..b7bd4bd0c1 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -2772,10 +2772,30 @@ gsk_transform_node_draw (GskRenderNode *node,
                          cairo_t       *cr)
 {
   GskTransformNode *self = (GskTransformNode *) node;
+  GskTransformCategory category;
   float xx, yx, xy, yy, dx, dy;
   cairo_matrix_t ctm;
 
-  if (gsk_transform_get_category (self->transform) < GSK_TRANSFORM_CATEGORY_2D)
+  category = gsk_transform_get_category (self->transform);
+
+  if (category == GSK_TRANSFORM_CATEGORY_IDENTITY)
+    {
+      gsk_render_node_draw (self->child, cr);
+      return;
+    }
+  else if (category == GSK_TRANSFORM_CATEGORY_2D_TRANSLATE)
+    {
+      gsk_transform_to_translate (self->transform, &dx, &dy);
+
+      cairo_translate (cr, dx, dy);
+      gsk_render_node_draw (self->child, cr);
+      cairo_translate (cr, -dx, -dy);
+      return;
+    }
+  else if (category == GSK_TRANSFORM_CATEGORY_2D_AFFINE)
+    {
+    }
+  else if (gsk_transform_get_category (self->transform) < GSK_TRANSFORM_CATEGORY_2D)
     {
       cairo_set_source_rgb (cr, 255 / 255., 105 / 255., 180 / 255.);
       gsk_cairo_rectangle (cr, &node->bounds);
@@ -2783,6 +2803,8 @@ gsk_transform_node_draw (GskRenderNode *node,
       return;
     }
 
+  /*if (category == GSK_TRANSFORM_CATEGORY_2D_AFFINE)*/
+    {
   gsk_transform_to_2d (self->transform, &xx, &yx, &xy, &yy, &dx, &dy);
   cairo_matrix_init (&ctm, xx, yx, xy, yy, dx, dy);
   GSK_NOTE (CAIRO, g_message ("CTM = { .xx = %g, .yx = %g, .xy = %g, .yy = %g, .x0 = %g, .y0 = %g }",
@@ -2791,16 +2813,15 @@ gsk_transform_node_draw (GskRenderNode *node,
                             ctm.x0, ctm.y0));
   if (xx * yy == xy * yx)
     {
-      /* broken matrix here. This can happen during transitions
-       * (like when flipping an axis at the point where scale == 0)
-       * and just means that nothing should be drawn.
-       * But Cairo thows lots of ugly errors instead of silently
-       * going on. So We silently go on.
-       */
+       /*broken matrix here. This can happen during transitions*/
+       /*(like when flipping an axis at the point where scale == 0)*/
+       /*and just means that nothing should be drawn.*/
+       /*But Cairo thows lots of ugly errors instead of silently*/
+       /*going on. So We silently go on.*/
       return;
     }
   cairo_transform (cr, &ctm);
-
+  }
   gsk_render_node_draw (self->child, cr);
 }
 
diff --git a/gsk/meson.build b/gsk/meson.build
index 748f4738a1..b82fb40cf0 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -18,6 +18,7 @@ gsk_private_gl_shaders = [
   'resources/glsl/blend.glsl',
   'resources/glsl/repeat.glsl',
   'resources/glsl/custom.glsl',
+  'resources/glsl/transform.glsl',
 ]
 
 gsk_public_sources = files([
diff --git a/gsk/resources/glsl/blend.glsl b/gsk/resources/glsl/blend.glsl
index 22323402ac..0587db304e 100644
--- a/gsk/resources/glsl/blend.glsl
+++ b/gsk/resources/glsl/blend.glsl
@@ -1,6 +1,6 @@
 // VERTEX_SHADER:
 void main() {
-  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+  gl_Position = gsk_project(aPosition);
 
   vUv = vec2(aUv.x, aUv.y);
 }
diff --git a/gsk/resources/glsl/blit.glsl b/gsk/resources/glsl/blit.glsl
index f01cd238ec..a5017c75fb 100644
--- a/gsk/resources/glsl/blit.glsl
+++ b/gsk/resources/glsl/blit.glsl
@@ -1,6 +1,6 @@
 // VERTEX_SHADER:
 void main() {
-  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+  gl_Position = gsk_project(aPosition);
 
   vUv = vec2(aUv.x, aUv.y);
 }
diff --git a/gsk/resources/glsl/blur.glsl b/gsk/resources/glsl/blur.glsl
index f782ab48cc..00bbd74e9f 100644
--- a/gsk/resources/glsl/blur.glsl
+++ b/gsk/resources/glsl/blur.glsl
@@ -11,7 +11,7 @@ const float PI = 3.14159265;
 const float RADIUS_MULTIPLIER = 2.0;
 
 void main() {
-  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+  gl_Position = gsk_project(aPosition);
 
   vUv = vec2(aUv.x, aUv.y);
 
diff --git a/gsk/resources/glsl/border.glsl b/gsk/resources/glsl/border.glsl
index 677a0df7cd..2fa4f1ad8b 100644
--- a/gsk/resources/glsl/border.glsl
+++ b/gsk/resources/glsl/border.glsl
@@ -8,15 +8,15 @@ _OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
 _OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
 
 void main() {
-  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+  gl_Position = gsk_project(aPosition);
 
   final_color = gsk_premultiply(u_color) * u_alpha;
 
   GskRoundedRect outside = gsk_create_rect(u_outline_rect);
   GskRoundedRect inside = gsk_rounded_rect_shrink (outside, u_widths);
 
-  gsk_rounded_rect_transform(outside, u_modelview);
-  gsk_rounded_rect_transform(inside, u_modelview);
+  gsk_rounded_rect_transform(outside);
+  gsk_rounded_rect_transform(inside);
 
   gsk_rounded_rect_encode(outside, transformed_outside_outline);
   gsk_rounded_rect_encode(inside, transformed_inside_outline);
diff --git a/gsk/resources/glsl/color.glsl b/gsk/resources/glsl/color.glsl
index 636456ce0d..e6f2a84b2b 100644
--- a/gsk/resources/glsl/color.glsl
+++ b/gsk/resources/glsl/color.glsl
@@ -4,7 +4,7 @@ uniform vec4 u_color;
 _OUT_ vec4 final_color;
 
 void main() {
-  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+  gl_Position = gsk_project(aPosition);
 
   final_color = gsk_premultiply(u_color) * u_alpha;
 }
diff --git a/gsk/resources/glsl/color_matrix.glsl b/gsk/resources/glsl/color_matrix.glsl
index 79cb36434e..1d34607455 100644
--- a/gsk/resources/glsl/color_matrix.glsl
+++ b/gsk/resources/glsl/color_matrix.glsl
@@ -1,6 +1,6 @@
 // VERTEX_SHADER:
 void main() {
-  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+  gl_Position = gsk_project(aPosition);
 
   vUv = vec2(aUv.x, aUv.y);
 }
diff --git a/gsk/resources/glsl/coloring.glsl b/gsk/resources/glsl/coloring.glsl
index a675493030..b399ff3db0 100644
--- a/gsk/resources/glsl/coloring.glsl
+++ b/gsk/resources/glsl/coloring.glsl
@@ -4,7 +4,7 @@ uniform vec4 u_color;
 _OUT_ vec4 final_color;
 
 void main() {
-  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+  gl_Position = gsk_project(aPosition);
 
   vUv = vec2(aUv.x, aUv.y);
 
diff --git a/gsk/resources/glsl/conic_gradient.glsl b/gsk/resources/glsl/conic_gradient.glsl
index 630a42c5e6..7f5dbd8db4 100644
--- a/gsk/resources/glsl/conic_gradient.glsl
+++ b/gsk/resources/glsl/conic_gradient.glsl
@@ -4,14 +4,11 @@ uniform vec4 u_geometry;
 _NOPERSPECTIVE_ _OUT_ vec2 coord;
 
 void main() {
-  gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
+  gl_Position = gsk_project(aPosition);
 
-  vec2 mv0 = u_modelview[0].xy;
-  vec2 mv1 = u_modelview[1].xy;
   vec2 offset = aPosition - u_geometry.xy;
 
-  coord = vec2(dot(mv0, offset),
-               dot(mv1, offset));
+  coord = u_scale * offset;
 }
 
 // FRAGMENT_SHADER:
diff --git a/gsk/resources/glsl/cross_fade.glsl b/gsk/resources/glsl/cross_fade.glsl
index f824430f9d..5a73e532fb 100644
--- a/gsk/resources/glsl/cross_fade.glsl
+++ b/gsk/resources/glsl/cross_fade.glsl
@@ -1,6 +1,6 @@
 // VERTEX_SHADER:
 void main() {
-  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+  gl_Position = gsk_project(aPosition);
 
   vUv = vec2(aUv.x, aUv.y);
 }
diff --git a/gsk/resources/glsl/custom.glsl b/gsk/resources/glsl/custom.glsl
index d2aed97fc8..c3b22d9add 100644
--- a/gsk/resources/glsl/custom.glsl
+++ b/gsk/resources/glsl/custom.glsl
@@ -1,6 +1,7 @@
 // VERTEX_SHADER:
 void main() {
-  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+  gl_Position = gsk_project(aPosition);
+
   vUv = vec2(aUv.x, aUv.y);
 }
 
diff --git a/gsk/resources/glsl/inset_shadow.glsl b/gsk/resources/glsl/inset_shadow.glsl
index 9a21cdef09..0b8fa43d4e 100644
--- a/gsk/resources/glsl/inset_shadow.glsl
+++ b/gsk/resources/glsl/inset_shadow.glsl
@@ -9,7 +9,7 @@ _OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
 _OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
 
 void main() {
-  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+  gl_Position = gsk_project(aPosition);
 
   final_color = gsk_premultiply(u_color) * u_alpha;
 
@@ -18,8 +18,8 @@ void main() {
 
   gsk_rounded_rect_offset(inside, u_offset);
 
-  gsk_rounded_rect_transform(outside, u_modelview);
-  gsk_rounded_rect_transform(inside, u_modelview);
+  gsk_rounded_rect_transform(outside);
+  gsk_rounded_rect_transform(inside);
 
   gsk_rounded_rect_encode(outside, transformed_outside_outline);
   gsk_rounded_rect_encode(inside, transformed_inside_outline);
diff --git a/gsk/resources/glsl/linear_gradient.glsl b/gsk/resources/glsl/linear_gradient.glsl
index cc90392c06..7530778e38 100644
--- a/gsk/resources/glsl/linear_gradient.glsl
+++ b/gsk/resources/glsl/linear_gradient.glsl
@@ -4,13 +4,10 @@ uniform vec4 u_points;
 _NOPERSPECTIVE_ _OUT_ vec4 info;
 
 void main() {
-  gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
+  gl_Position = gsk_project(aPosition);
 
-  vec2 mv0 = u_modelview[0].xy;
-  vec2 mv1 = u_modelview[1].xy;
   vec2 offset = aPosition - u_points.xy;
-  vec2 coord = vec2(dot(mv0, offset),
-                    dot(mv1, offset));
+  vec2 coord = u_scale * offset;
 
   // Original equation:
   // VS | maxDist = length(end - start);
@@ -31,8 +28,7 @@ void main() {
   // 4. We can avoid the FS division by passing a scaled pos from the VS:
   // offset = dot(gnorm, pos) / gradientLength = dot(gnorm, pos / gradientLength)
   // 5. 1.0 / length(gradient) is inversesqrt(dot(gradient, gradient)) in GLSL
-  vec2 gradient = vec2(dot(mv0, u_points.zw),
-                       dot(mv1, u_points.zw));
+  vec2 gradient = u_scale * u_points.zw;
   float rcp_gradient_length = inversesqrt(dot(gradient, gradient));
 
   info = rcp_gradient_length * vec4(coord, gradient);
diff --git a/gsk/resources/glsl/outset_shadow.glsl b/gsk/resources/glsl/outset_shadow.glsl
index 373c650179..db4139f939 100644
--- a/gsk/resources/glsl/outset_shadow.glsl
+++ b/gsk/resources/glsl/outset_shadow.glsl
@@ -6,14 +6,14 @@ _OUT_ vec4 final_color;
 _OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outline;
 
 void main() {
-  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+  gl_Position = gsk_project(aPosition);
 
   vUv = vec2(aUv.x, aUv.y);
 
   final_color = gsk_premultiply(u_color) * u_alpha;
 
   GskRoundedRect outline = gsk_create_rect(u_outline_rect);
-  gsk_rounded_rect_transform(outline, u_modelview);
+  gsk_rounded_rect_transform(outline);
   gsk_rounded_rect_encode(outline, transformed_outline);
 }
 
diff --git a/gsk/resources/glsl/preamble.fs.glsl b/gsk/resources/glsl/preamble.fs.glsl
index c2cd1cdc29..6d7bdee072 100644
--- a/gsk/resources/glsl/preamble.fs.glsl
+++ b/gsk/resources/glsl/preamble.fs.glsl
@@ -1,6 +1,6 @@
 uniform sampler2D u_source;
 uniform mat4 u_projection;
-uniform mat4 u_modelview;
+uniform vec2 u_scale;
 uniform float u_alpha;// = 1.0;
 uniform vec4 u_viewport;
 uniform vec4[3] u_clip_rect;
diff --git a/gsk/resources/glsl/preamble.vs.glsl b/gsk/resources/glsl/preamble.vs.glsl
index 89ee6f74e0..2b44c8fe03 100644
--- a/gsk/resources/glsl/preamble.vs.glsl
+++ b/gsk/resources/glsl/preamble.vs.glsl
@@ -1,6 +1,6 @@
 uniform mat4 u_projection;
-uniform mat4 u_modelview;
 uniform float u_alpha;
+uniform vec2 u_scale;
 
 #if defined(GSK_GLES) || defined(GSK_LEGACY)
 attribute vec2 aPosition;
@@ -12,6 +12,12 @@ _IN_ vec2 aUv;
 _OUT_ vec2 vUv;
 #endif
 
+vec4
+gsk_project(vec2 pos)
+{
+  return u_projection * vec4(u_scale * pos, 0.0, 1.0);
+}
+
 // amount is: top, right, bottom, left
 GskRoundedRect
 gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
@@ -39,16 +45,17 @@ gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
   r.corner_points2.zw += offset;
 }
 
-void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+void
+gsk_rounded_rect_transform(inout GskRoundedRect r)
 {
-  r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
-  r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+  r.bounds.xy = u_scale * r.bounds.xy;
+  r.bounds.zw = u_scale * r.bounds.zw;
 
-  r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
-  r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+  r.corner_points1.xy = (u_scale * r.corner_points1.xy);
+  r.corner_points1.zw = (u_scale * r.corner_points1.zw);
 
-  r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
-  r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+  r.corner_points2.xy = (u_scale * r.corner_points2.xy);
+  r.corner_points2.zw = (u_scale * r.corner_points2.zw);
 }
 
 #if defined(GSK_LEGACY)
diff --git a/gsk/resources/glsl/radial_gradient.glsl b/gsk/resources/glsl/radial_gradient.glsl
index 0ab3fdf07a..23a01ae4bd 100644
--- a/gsk/resources/glsl/radial_gradient.glsl
+++ b/gsk/resources/glsl/radial_gradient.glsl
@@ -4,13 +4,10 @@ uniform vec4 u_geometry;
 _NOPERSPECTIVE_ _OUT_ vec2 coord;
 
 void main() {
-  gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
+  gl_Position = gsk_project(aPosition);
 
-  vec2 mv0 = u_modelview[0].xy;
-  vec2 mv1 = u_modelview[1].xy;
   vec2 offset = aPosition - u_geometry.xy;
-  vec2 dir = vec2(dot(mv0, offset),
-                  dot(mv1, offset));
+  vec2 dir = u_scale * offset;
 
   coord = dir * u_geometry.zw;
 }
diff --git a/gsk/resources/glsl/repeat.glsl b/gsk/resources/glsl/repeat.glsl
index a9ebcc5e10..f5a756b69b 100644
--- a/gsk/resources/glsl/repeat.glsl
+++ b/gsk/resources/glsl/repeat.glsl
@@ -1,6 +1,6 @@
 // VERTEX_SHADER:
 void main() {
-  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+  gl_Position = gsk_project(aPosition);
 
   vUv = vec2(aUv.x, aUv.y);
 }
diff --git a/gsk/resources/glsl/transform.glsl b/gsk/resources/glsl/transform.glsl
new file mode 100644
index 0000000000..a79d4a4b1f
--- /dev/null
+++ b/gsk/resources/glsl/transform.glsl
@@ -0,0 +1,22 @@
+// VERTEX_SHADER:
+uniform mat4 u_transform;
+
+void main() {
+  // We are not using gsk_project() here of course. That's the entire
+  // point of this shader.
+  mat4 scale_matrix = mat4(u_scale.x, 0.0,       0.0, 0.0,
+                           0.0,       u_scale.y, 0.0, 0.0,
+                           0.0,       0.0,       1.0, 0.0,
+                           0.0,       0.0,       0.0, 1.0);
+  mat4 transform = scale_matrix * u_transform;
+  gl_Position = u_projection * transform * vec4(aPosition, 0.0, 1.0)  ;
+
+  vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+void main() {
+  vec4 diffuse = GskTexture(u_source, vUv);
+
+  gskSetOutputColor(diffuse * u_alpha);
+}
diff --git a/gsk/resources/glsl/unblurred_outset_shadow.glsl b/gsk/resources/glsl/unblurred_outset_shadow.glsl
index f110370412..573bbf6153 100644
--- a/gsk/resources/glsl/unblurred_outset_shadow.glsl
+++ b/gsk/resources/glsl/unblurred_outset_shadow.glsl
@@ -9,8 +9,7 @@ _OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
 _OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
 
 void main() {
-  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
-
+  gl_Position = gsk_project(aPosition);
   final_color = gsk_premultiply(u_color) * u_alpha;
 
   GskRoundedRect inside = gsk_create_rect(u_outline_rect);
@@ -18,8 +17,8 @@ void main() {
 
   gsk_rounded_rect_offset(outside, u_offset);
 
-  gsk_rounded_rect_transform(outside, u_modelview);
-  gsk_rounded_rect_transform(inside, u_modelview);
+  gsk_rounded_rect_transform(outside);
+  gsk_rounded_rect_transform(inside);
 
   gsk_rounded_rect_encode(outside, transformed_outside_outline);
   gsk_rounded_rect_encode(inside, transformed_inside_outline);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]