[gtk/wip/matthiasc/opbuffer: 17/28] prototype OpBuffer helper for building op buffer



commit a00d12c62aea1f51a2ffdf3ec970084c670c4ce9
Author: Christian Hergert <chergert redhat com>
Date:   Fri Oct 11 10:23:33 2019 -0400

    prototype OpBuffer helper for building op buffer

 gsk/gl/gskglrenderer.c         | 438 +++++++++++++++++++++--------------------
 gsk/gl/gskglrenderops.c        | 280 ++++++++++----------------
 gsk/gl/gskglrenderopsprivate.h | 140 +------------
 gsk/gl/opbuffer.c              | 134 +++++++++++++
 gsk/gl/opbuffer.h              | 263 +++++++++++++++++++++++++
 gsk/meson.build                |   1 +
 6 files changed, 742 insertions(+), 514 deletions(-)
---
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index 82b5e3018c..07d7938d19 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -8,6 +8,7 @@
 #include "gskglprofilerprivate.h"
 #include "gskprofilerprivate.h"
 #include "gskrendererprivate.h"
+#include "gskrendernodeprivate.h"
 #include "gsktransformprivate.h"
 #include "gskshaderbuilderprivate.h"
 #include "gskglglyphcacheprivate.h"
@@ -328,7 +329,6 @@ struct _GskGLRenderer
   };
 
   RenderOpBuilder op_builder;
-  GArray *render_ops;
 
   GskGLTextureAtlases *atlases;
   GskGLGlyphCache *glyph_cache;
@@ -1047,7 +1047,7 @@ render_linear_gradient_node (GskGLRenderer       *self,
                              RenderOpBuilder     *builder,
                              const GskQuadVertex *vertex_data)
 {
-  RenderOp *op;
+  OpLinearGradient *op;
   int n_color_stops = MIN (8, gsk_linear_gradient_node_get_n_color_stops (node));
   const GskColorStop *stops = gsk_linear_gradient_node_peek_color_stops (node);
   const graphene_point_t *start = gsk_linear_gradient_node_peek_start (node);
@@ -1062,18 +1062,18 @@ render_linear_gradient_node (GskGLRenderer       *self,
     {
       const GskColorStop *stop = stops + i;
 
-      op->linear_gradient.color_stops[(i * 4) + 0] = stop->color.red;
-      op->linear_gradient.color_stops[(i * 4) + 1] = stop->color.green;
-      op->linear_gradient.color_stops[(i * 4) + 2] = stop->color.blue;
-      op->linear_gradient.color_stops[(i * 4) + 3] = stop->color.alpha;
-      op->linear_gradient.color_offsets[i] = stop->offset;
+      op->color_stops[(i * 4) + 0] = stop->color.red;
+      op->color_stops[(i * 4) + 1] = stop->color.green;
+      op->color_stops[(i * 4) + 2] = stop->color.blue;
+      op->color_stops[(i * 4) + 3] = stop->color.alpha;
+      op->color_offsets[i] = stop->offset;
     }
 
-  op->linear_gradient.n_color_stops = n_color_stops;
-  op->linear_gradient.start_point.x = start->x + builder->dx;
-  op->linear_gradient.start_point.y = start->y + builder->dy;
-  op->linear_gradient.end_point.x = end->x + builder->dx;
-  op->linear_gradient.end_point.y = end->y + builder->dy;
+  op->n_color_stops = n_color_stops;
+  op->start_point.x = start->x + builder->dx;
+  op->start_point.y = start->y + builder->dy;
+  op->end_point.x = end->x + builder->dx;
+  op->end_point.y = end->y + builder->dy;
 
   ops_draw (builder, vertex_data);
 }
@@ -1280,7 +1280,7 @@ render_blur_node (GskGLRenderer       *self,
   GskRenderNode *child = gsk_blur_node_get_child (node);
   TextureRegion region;
   gboolean is_offscreen;
-  RenderOp *op;
+  OpBlur *op;
 
   if (blur_radius <= 0)
     {
@@ -1302,8 +1302,8 @@ render_blur_node (GskGLRenderer       *self,
   ops_set_program (builder, &self->blur_program);
 
   op = ops_begin (builder, OP_CHANGE_BLUR);
-  graphene_size_init_from_size (&op->blur.size, &node->bounds.size);
-  op->blur.radius = blur_radius;
+  graphene_size_init_from_size (&op->size, &node->bounds.size);
+  op->radius = blur_radius;
 
   ops_set_texture (builder, region.texture_id);
 
@@ -1334,7 +1334,7 @@ render_inset_shadow_node (GskGLRenderer       *self,
                           const GskQuadVertex *vertex_data)
 {
   const float scale = ops_get_scale (builder);
-  RenderOp *op;
+  OpShadow *op;
 
   /* TODO: Implement blurred inset shadows as well */
   if (gsk_inset_shadow_node_get_blur_radius (node) > 0)
@@ -1346,16 +1346,16 @@ render_inset_shadow_node (GskGLRenderer       *self,
   ops_set_program (builder, &self->inset_shadow_program);
 
   op = ops_begin (builder, OP_CHANGE_INSET_SHADOW);
-  rgba_to_float (gsk_inset_shadow_node_peek_color (node), op->inset_shadow.color);
+  rgba_to_float (gsk_inset_shadow_node_peek_color (node), op->color);
   rounded_rect_to_floats (self, builder,
                           gsk_inset_shadow_node_peek_outline (node),
-                          op->inset_shadow.outline,
-                          op->inset_shadow.corner_widths,
-                          op->inset_shadow.corner_heights);
-  op->inset_shadow.radius = gsk_inset_shadow_node_get_blur_radius (node) * scale;
-  op->inset_shadow.spread = gsk_inset_shadow_node_get_spread (node) * scale;
-  op->inset_shadow.offset[0] = gsk_inset_shadow_node_get_dx (node) * scale;
-  op->inset_shadow.offset[1] = -gsk_inset_shadow_node_get_dy (node) * scale;
+                          op->outline,
+                          op->corner_widths,
+                          op->corner_heights);
+  op->radius = gsk_inset_shadow_node_get_blur_radius (node) * scale;
+  op->spread = gsk_inset_shadow_node_get_spread (node) * scale;
+  op->offset[0] = gsk_inset_shadow_node_get_dx (node) * scale;
+  op->offset[1] = -gsk_inset_shadow_node_get_dy (node) * scale;
 
   ops_draw (builder, vertex_data);
 }
@@ -1371,21 +1371,21 @@ render_unblurred_outset_shadow_node (GskGLRenderer       *self,
   const float spread = gsk_outset_shadow_node_get_spread (node);
   const float dx = gsk_outset_shadow_node_get_dx (node);
   const float dy = gsk_outset_shadow_node_get_dy (node);
-  RenderOp *op;
+  OpShadow *op;
 
   ops_set_program (builder, &self->unblurred_outset_shadow_program);
   op = ops_begin (builder, OP_CHANGE_UNBLURRED_OUTSET_SHADOW);
-  rgba_to_float (gsk_outset_shadow_node_peek_color (node), op->unblurred_outset_shadow.color);
+  rgba_to_float (gsk_outset_shadow_node_peek_color (node), op->color);
 
   rounded_rect_to_floats (self, builder,
                           outline,
-                          op->unblurred_outset_shadow.outline,
-                          op->unblurred_outset_shadow.corner_widths,
-                          op->unblurred_outset_shadow.corner_heights);
+                          op->outline,
+                          op->corner_widths,
+                          op->corner_heights);
 
-  op->unblurred_outset_shadow.spread = spread * scale;
-  op->unblurred_outset_shadow.offset[0] = dx * scale;
-  op->unblurred_outset_shadow.offset[1] = - dy * scale;
+  op->spread = spread * scale;
+  op->offset[0] = dx * scale;
+  op->offset[1] = - dy * scale;
 
   ops_draw (builder, vertex_data);
 }
@@ -1407,7 +1407,8 @@ render_outset_shadow_node (GskGLRenderer       *self,
   const float max_x = min_x + outline->bounds.size.width  + (spread + blur_extra/2.0) * 2;
   const float max_y = min_y + outline->bounds.size.height + (spread + blur_extra/2.0) * 2;
   float texture_width, texture_height;
-  RenderOp *op;
+  OpBlur *op;
+  OpShadow *shadow;
   graphene_matrix_t prev_projection;
   graphene_rect_t prev_viewport;
   graphene_matrix_t item_proj;
@@ -1494,9 +1495,9 @@ render_outset_shadow_node (GskGLRenderer       *self,
       ops_set_program (builder, &self->blur_program);
 
       op = ops_begin (builder, OP_CHANGE_BLUR);
-      op->blur.size.width = texture_width;
-      op->blur.size.height = texture_height;
-      op->blur.radius = blur_radius;
+      op->size.width = texture_width;
+      op->size.height = texture_height;
+      op->radius = blur_radius;
 
       ops_push_clip (builder, &blit_clip);
       ops_set_texture (builder, texture_id);
@@ -1531,12 +1532,12 @@ render_outset_shadow_node (GskGLRenderer       *self,
   ops_set_program (builder, &self->outset_shadow_program);
   ops_set_texture (builder, blurred_texture_id);
 
-  op = ops_begin (builder, OP_CHANGE_OUTSET_SHADOW);
+  shadow = ops_begin (builder, OP_CHANGE_OUTSET_SHADOW);
   rounded_rect_to_floats (self, builder,
                           outline,
-                          op->outset_shadow.outline,
-                          op->outset_shadow.corner_widths,
-                          op->outset_shadow.corner_heights);
+                          shadow->outline,
+                          shadow->corner_widths,
+                          shadow->corner_heights);
 
   /* We use the one outset shadow op from above to draw all 8 sides/corners. */
   {
@@ -1875,7 +1876,7 @@ render_cross_fade_node (GskGLRenderer       *self,
   TextureRegion start_region;
   TextureRegion end_region;
   gboolean is_offscreen1, is_offscreen2;
-  RenderOp *op;
+  OpCrossFade *op;
   const GskQuadVertex vertex_data[GL_N_VERTICES] = {
     { { min_x, min_y }, { 0, 1 }, },
     { { min_x, max_y }, { 0, 0 }, },
@@ -1904,8 +1905,8 @@ render_cross_fade_node (GskGLRenderer       *self,
   ops_set_program (builder, &self->cross_fade_program);
 
   op = ops_begin (builder, OP_CHANGE_CROSS_FADE);
-  op->cross_fade.progress = progress;
-  op->cross_fade.source2 = end_region.texture_id;
+  op->progress = progress;
+  op->source2 = end_region.texture_id;
 
   ops_set_texture (builder, start_region.texture_id);
 
@@ -1926,7 +1927,7 @@ render_blend_node (GskGLRenderer   *self,
   TextureRegion top_region;
   TextureRegion bottom_region;
   gboolean is_offscreen1, is_offscreen2;
-  RenderOp *op;
+  OpBlend *op;
   const GskQuadVertex vertex_data[GL_N_VERTICES] = {
     { { min_x, min_y }, { 0, 1 }, },
     { { min_x, max_y }, { 0, 0 }, },
@@ -1955,8 +1956,8 @@ render_blend_node (GskGLRenderer   *self,
   ops_set_texture (builder, bottom_region.texture_id);
 
   op = ops_begin (builder, OP_CHANGE_BLEND);
-  op->blend.source2 = top_region.texture_id;
-  op->blend.mode = gsk_blend_node_get_blend_mode (node);
+  op->source2 = top_region.texture_id;
+  op->mode = gsk_blend_node_get_blend_mode (node);
 
   ops_draw (builder, vertex_data);
 }
@@ -1974,7 +1975,7 @@ render_repeat_node (GskGLRenderer   *self,
   const graphene_rect_t *child_bounds = gsk_repeat_node_peek_child_bounds (node);
   TextureRegion region;
   gboolean is_offscreen;
-  RenderOp *op;
+  OpRepeat *op;
 
   if (child_bounds != NULL &&
       !graphene_rect_equal (child_bounds, &child->bounds))
@@ -1995,23 +1996,23 @@ render_repeat_node (GskGLRenderer   *self,
   ops_set_texture (builder, region.texture_id);
 
   op = ops_begin (builder, OP_CHANGE_REPEAT);
-  op->repeat.child_bounds[0] = 0; /* Both currently unused */
-  op->repeat.child_bounds[1] = 0;
-  op->repeat.child_bounds[2] = node->bounds.size.width / child_bounds->size.width;
-  op->repeat.child_bounds[3] = node->bounds.size.height / child_bounds->size.height;
+  op->child_bounds[0] = 0; /* Both currently unused */
+  op->child_bounds[1] = 0;
+  op->child_bounds[2] = node->bounds.size.width / child_bounds->size.width;
+  op->child_bounds[3] = node->bounds.size.height / child_bounds->size.height;
 
-  op->repeat.texture_rect[0] = region.x;
-  op->repeat.texture_rect[2] = region.x2;
+  op->texture_rect[0] = region.x;
+  op->texture_rect[2] = region.x2;
 
   if (is_offscreen)
     {
-      op->repeat.texture_rect[1] = region.y2;
-      op->repeat.texture_rect[3] = region.y;
+      op->texture_rect[1] = region.y2;
+      op->texture_rect[3] = region.y;
     }
   else
     {
-      op->repeat.texture_rect[1] = region.y;
-      op->repeat.texture_rect[3] = region.y2;
+      op->texture_rect[1] = region.y;
+      op->texture_rect[3] = region.y2;
     }
 
   if (is_offscreen)
@@ -2045,10 +2046,12 @@ render_repeat_node (GskGLRenderer   *self,
 }
 
 static inline void
-apply_viewport_op (const Program  *program,
-                   const RenderOp *op)
+apply_viewport_op (const Program    *program,
+                   const OpViewport *op)
 {
-  OP_PRINT (" -> New Viewport: %f, %f, %f, %f", op->viewport.origin.x, op->viewport.origin.y, 
op->viewport.size.width, op->viewport.size.height);
+  OP_PRINT (" -> New Viewport: %f, %f, %f, %f",
+            op->viewport.origin.x, op->viewport.origin.y,
+            op->viewport.size.width, op->viewport.size.height);
   glUniform4f (program->viewport_location,
                op->viewport.origin.x, op->viewport.origin.y,
                op->viewport.size.width, op->viewport.size.height);
@@ -2057,38 +2060,38 @@ apply_viewport_op (const Program  *program,
 
 static inline void
 apply_modelview_op (const Program  *program,
-                    const RenderOp *op)
+                    const OpMatrix *op)
 {
   float mat[16];
 
   OP_PRINT (" -> Modelview");
-  graphene_matrix_to_float (&op->modelview, mat);
+  graphene_matrix_to_float (&op->matrix, mat);
   glUniformMatrix4fv (program->modelview_location, 1, GL_FALSE, mat);
 }
 
 static inline void
 apply_projection_op (const Program  *program,
-                     const RenderOp *op)
+                     const OpMatrix *op)
 {
   float mat[16];
 
   OP_PRINT (" -> Projection");
-  graphene_matrix_to_float (&op->projection, mat);
+  graphene_matrix_to_float (&op->matrix, mat);
   glUniformMatrix4fv (program->projection_location, 1, GL_FALSE, mat);
 }
 
 static inline void
 apply_program_op (const Program  *program,
-                  const RenderOp *op)
+                  const OpProgram *op)
 {
   OP_PRINT (" -> Program: %d", op->program->index);
   glUseProgram (op->program->id);
 }
 
 static inline void
-apply_render_target_op (GskGLRenderer  *self,
-                        const Program  *program,
-                        const RenderOp *op)
+apply_render_target_op (GskGLRenderer        *self,
+                        const Program        *program,
+                        const OpRenderTarget *op)
 {
   OP_PRINT (" -> Render Target: %d", op->render_target_id);
 
@@ -2101,29 +2104,26 @@ apply_render_target_op (GskGLRenderer  *self,
 }
 
 static inline void
-apply_color_op (const Program  *program,
-                const RenderOp *op)
+apply_color_op (const Program *program,
+                const OpColor *op)
 {
   OP_PRINT (" -> Color: (%f, %f, %f, %f)",
-            op->color->red, op->color->green, op->color->blue, op->color->alpha);
+            op->rgba.red, op->rgba.green, op->rgba.blue, op->rgba.alpha);
   glUniform4f (program->color.color_location,
-               op->color->red,
-               op->color->green,
-               op->color->blue,
-               op->color->alpha);
+               op->rgba.red, op->rgba.green, op->rgba.blue, op->rgba.alpha);
 }
 
 static inline void
-apply_opacity_op (const Program  *program,
-                  const RenderOp *op)
+apply_opacity_op (const Program   *program,
+                  const OpOpacity *op)
 {
   OP_PRINT (" -> Opacity %f", op->opacity);
   glUniform1f (program->alpha_location, op->opacity);
 }
 
 static inline void
-apply_source_texture_op (const Program  *program,
-                         const RenderOp *op)
+apply_source_texture_op (const Program   *program,
+                         const OpTexture *op)
 {
   g_assert(op->texture_id != 0);
   OP_PRINT (" -> New texture: %d", op->texture_id);
@@ -2134,22 +2134,22 @@ apply_source_texture_op (const Program  *program,
 }
 
 static inline void
-apply_color_matrix_op (const Program  *program,
-                       const RenderOp *op)
+apply_color_matrix_op (const Program       *program,
+                       const OpColorMatrix *op)
 {
   float mat[16];
   float vec[4];
   OP_PRINT (" -> Color Matrix");
-  graphene_matrix_to_float (&op->color_matrix.matrix, mat);
+  graphene_matrix_to_float (&op->matrix, mat);
   glUniformMatrix4fv (program->color_matrix.color_matrix_location, 1, GL_FALSE, mat);
 
-  graphene_vec4_to_float (&op->color_matrix.offset, vec);
+  graphene_vec4_to_float (&op->offset, vec);
   glUniform4fv (program->color_matrix.color_offset_location, 1, vec);
 }
 
 static inline void
-apply_clip_op (const Program  *program,
-               const RenderOp *op)
+apply_clip_op (const Program *program,
+               const OpClip  *op)
 {
   OP_PRINT (" -> Clip (%f, %f, %f, %f) (%f, %f, %f, %f), (%f, %f, %f, %f)",
             op->clip.bounds.origin.x, op->clip.bounds.origin.y,
@@ -2180,85 +2180,85 @@ apply_clip_op (const Program  *program,
 
 static inline void
 apply_inset_shadow_op (const Program  *program,
-                       const RenderOp *op)
+                       const OpShadow *op)
 {
   OP_PRINT (" -> inset shadow. Color: (%f, %f, %f, %f), Offset: (%f, %f), Spread: %f, Outline: (%f, %f, %f, 
%f) Corner widths: (%f, %f, %f, %f), Corner Heights: (%f, %f, %f, %f)",
-            op->inset_shadow.color[0],
-            op->inset_shadow.color[1],
-            op->inset_shadow.color[2],
-            op->inset_shadow.color[3],
-            op->inset_shadow.offset[0],
-            op->inset_shadow.offset[1],
-            op->inset_shadow.spread,
-            op->inset_shadow.outline[0],
-            op->inset_shadow.outline[1],
-            op->inset_shadow.outline[2],
-            op->inset_shadow.outline[3],
-            op->inset_shadow.corner_widths[0],
-            op->inset_shadow.corner_widths[1],
-            op->inset_shadow.corner_widths[2],
-            op->inset_shadow.corner_widths[3],
-            op->inset_shadow.corner_heights[0],
-            op->inset_shadow.corner_heights[1],
-            op->inset_shadow.corner_heights[2],
-            op->inset_shadow.corner_heights[3]);
-  glUniform4fv (program->inset_shadow.color_location, 1, op->inset_shadow.color);
-  glUniform2fv (program->inset_shadow.offset_location, 1, op->inset_shadow.offset);
-  glUniform1f (program->inset_shadow.spread_location, op->inset_shadow.spread);
-  glUniform4fv (program->inset_shadow.outline_location, 1, op->inset_shadow.outline);
-  glUniform4fv (program->inset_shadow.corner_widths_location, 1, op->inset_shadow.corner_widths);
-  glUniform4fv (program->inset_shadow.corner_heights_location, 1, op->inset_shadow.corner_heights);
+            op->color[0],
+            op->color[1],
+            op->color[2],
+            op->color[3],
+            op->offset[0],
+            op->offset[1],
+            op->spread,
+            op->outline[0],
+            op->outline[1],
+            op->outline[2],
+            op->outline[3],
+            op->corner_widths[0],
+            op->corner_widths[1],
+            op->corner_widths[2],
+            op->corner_widths[3],
+            op->corner_heights[0],
+            op->corner_heights[1],
+            op->corner_heights[2],
+            op->corner_heights[3]);
+  glUniform4fv (program->inset_shadow.color_location, 1, op->color);
+  glUniform2fv (program->inset_shadow.offset_location, 1, op->offset);
+  glUniform1f (program->inset_shadow.spread_location, op->spread);
+  glUniform4fv (program->inset_shadow.outline_location, 1, op->outline);
+  glUniform4fv (program->inset_shadow.corner_widths_location, 1, op->corner_widths);
+  glUniform4fv (program->inset_shadow.corner_heights_location, 1, op->corner_heights);
 }
 
 static inline void
 apply_unblurred_outset_shadow_op (const Program  *program,
-                                  const RenderOp *op)
+                                  const OpShadow *op)
 {
   OP_PRINT (" -> unblurred outset shadow");
-  glUniform4fv (program->unblurred_outset_shadow.color_location, 1, op->unblurred_outset_shadow.color);
-  glUniform2fv (program->unblurred_outset_shadow.offset_location, 1, op->unblurred_outset_shadow.offset);
-  glUniform1f (program->unblurred_outset_shadow.spread_location, op->unblurred_outset_shadow.spread);
-  glUniform4fv (program->unblurred_outset_shadow.outline_location, 1, op->unblurred_outset_shadow.outline);
+  glUniform4fv (program->unblurred_outset_shadow.color_location, 1, op->color);
+  glUniform2fv (program->unblurred_outset_shadow.offset_location, 1, op->offset);
+  glUniform1f (program->unblurred_outset_shadow.spread_location, op->spread);
+  glUniform4fv (program->unblurred_outset_shadow.outline_location, 1, op->outline);
   glUniform4fv (program->unblurred_outset_shadow.corner_widths_location, 1,
-                op->unblurred_outset_shadow.corner_widths);
+                op->corner_widths);
   glUniform4fv (program->unblurred_outset_shadow.corner_heights_location, 1,
-                op->unblurred_outset_shadow.corner_heights);
+                op->corner_heights);
 }
 
 static inline void
 apply_outset_shadow_op (const Program  *program,
-                        const RenderOp *op)
+                        const OpShadow *op)
 {
   OP_PRINT (" -> outset shadow");
-  glUniform4fv (program->outset_shadow.outline_location, 1, op->outset_shadow.outline);
-  glUniform4fv (program->outset_shadow.corner_widths_location, 1, op->outset_shadow.corner_widths);
-  glUniform4fv (program->outset_shadow.corner_heights_location, 1, op->outset_shadow.corner_heights);
+  glUniform4fv (program->outset_shadow.outline_location, 1, op->outline);
+  glUniform4fv (program->outset_shadow.corner_widths_location, 1, op->corner_widths);
+  glUniform4fv (program->outset_shadow.corner_heights_location, 1, op->corner_heights);
 }
 
 static inline void
-apply_linear_gradient_op (const Program  *program,
-                          const RenderOp *op)
+apply_linear_gradient_op (const Program          *program,
+                          const OpLinearGradient *op)
 {
   OP_PRINT (" -> Linear gradient");
   glUniform1i (program->linear_gradient.num_color_stops_location,
-               op->linear_gradient.n_color_stops);
+               op->n_color_stops);
   glUniform4fv (program->linear_gradient.color_stops_location,
-                op->linear_gradient.n_color_stops,
-                op->linear_gradient.color_stops);
+                op->n_color_stops,
+                op->color_stops);
   glUniform1fv (program->linear_gradient.color_offsets_location,
-                op->linear_gradient.n_color_stops,
-                op->linear_gradient.color_offsets);
+                op->n_color_stops,
+                op->color_offsets);
   glUniform2f (program->linear_gradient.start_point_location,
-               op->linear_gradient.start_point.x, op->linear_gradient.start_point.y);
+               op->start_point.x, op->start_point.y);
   glUniform2f (program->linear_gradient.end_point_location,
-               op->linear_gradient.end_point.x, op->linear_gradient.end_point.y);
+               op->end_point.x, op->end_point.y);
 }
 
 static inline void
 apply_border_op (const Program  *program,
-                 const RenderOp *op)
+                 const OpBorder *op)
 {
-  const GskRoundedRect *o = &op->border.outline;
+  const GskRoundedRect *o = &op->outline;
   float outline[4];
   float widths[4];
   float heights[4];
@@ -2283,63 +2283,63 @@ apply_border_op (const Program  *program,
 
 static inline void
 apply_border_width_op (const Program  *program,
-                       const RenderOp *op)
+                       const OpBorder *op)
 {
   OP_PRINT (" -> Border width (%f, %f, %f, %f)",
-            op->border.widths[0], op->border.widths[1], op->border.widths[2], op->border.widths[3]);
+            op->widths[0], op->widths[1], op->widths[2], op->widths[3]);
 
-  glUniform4fv (program->border.widths_location, 1, op->border.widths);
+  glUniform4fv (program->border.widths_location, 1, op->widths);
 }
 
 static inline void
 apply_border_color_op (const Program  *program,
-                       const RenderOp *op)
+                       const OpBorder *op)
 {
   OP_PRINT (" -> Border color (%f, %f, %f, %f)",
-            op->border.color[0], op->border.color[1], op->border.color[2], op->border.color[3]);
-  glUniform4fv (program->border.color_location, 1, op->border.color);
+            op->color[0], op->color[1], op->color[2], op->color[3]);
+  glUniform4fv (program->border.color_location, 1, op->color);
 }
 
 static inline void
-apply_blur_op (const Program  *program,
-               const RenderOp *op)
+apply_blur_op (const Program *program,
+               const OpBlur  *op)
 {
   OP_PRINT (" -> Blur");
-  glUniform1f (program->blur.blur_radius_location, op->blur.radius);
-  glUniform2f (program->blur.blur_size_location, op->blur.size.width, op->blur.size.height);
-  /*glUniform2f (program->blur.dir_location, op->blur.dir[0], op->blur.dir[1]);*/
+  glUniform1f (program->blur.blur_radius_location, op->radius);
+  glUniform2f (program->blur.blur_size_location, op->size.width, op->size.height);
+  /*glUniform2f (program->blur.dir_location, op->dir[0], op->dir[1]);*/
 }
 
 static inline void
-apply_cross_fade_op (const Program  *program,
-                     const RenderOp *op)
+apply_cross_fade_op (const Program     *program,
+                     const OpCrossFade *op)
 {
   /* End texture id */
   glUniform1i (program->cross_fade.source2_location, 1);
   glActiveTexture (GL_TEXTURE0 + 1);
-  glBindTexture (GL_TEXTURE_2D, op->cross_fade.source2);
+  glBindTexture (GL_TEXTURE_2D, op->source2);
   /* progress */
-  glUniform1f (program->cross_fade.progress_location, op->cross_fade.progress);
+  glUniform1f (program->cross_fade.progress_location, op->progress);
 }
 
 static inline void
-apply_blend_op (const Program  *program,
-                const RenderOp *op)
+apply_blend_op (const Program *program,
+                const OpBlend *op)
 {
   /* End texture id */
   glUniform1i (program->blend.source2_location, 1);
   glActiveTexture (GL_TEXTURE0 + 1);
-  glBindTexture (GL_TEXTURE_2D, op->blend.source2);
+  glBindTexture (GL_TEXTURE_2D, op->source2);
   /* progress */
-  glUniform1i (program->blend.mode_location, op->blend.mode);
+  glUniform1i (program->blend.mode_location, op->mode);
 }
 
 static inline void
 apply_repeat_op (const Program  *program,
-                 const RenderOp *op)
+                 const OpRepeat *op)
 {
-  glUniform4fv (program->repeat.child_bounds_location, 1, op->repeat.child_bounds);
-  glUniform4fv (program->repeat.texture_rect_location, 1, op->repeat.texture_rect);
+  glUniform4fv (program->repeat.child_bounds_location, 1, op->child_bounds);
+  glUniform4fv (program->repeat.texture_rect_location, 1, op->texture_rect);
 }
 
 static void
@@ -2347,7 +2347,6 @@ gsk_gl_renderer_dispose (GObject *gobject)
 {
   GskGLRenderer *self = GSK_GL_RENDERER (gobject);
 
-  g_clear_pointer (&self->render_ops, g_array_unref);
   ops_free (&self->op_builder);
 
   G_OBJECT_CLASS (gsk_gl_renderer_parent_class)->dispose (gobject);
@@ -2635,7 +2634,7 @@ gsk_gl_renderer_unrealize (GskRenderer *renderer)
   /* We don't need to iterate to destroy the associated GL resources,
    * as they will be dropped when we finalize the GskGLDriver
    */
-  g_array_set_size (self->render_ops, 0);
+  ops_reset (&self->op_builder);
 
   for (i = 0; i < GL_N_PROGRAMS; i ++)
     glDeleteProgram (self->programs[i].id);
@@ -2664,7 +2663,7 @@ gsk_gl_renderer_clear_tree (GskGLRenderer *self)
 
   gdk_gl_context_make_current (self->gl_context);
 
-  g_array_remove_range (self->render_ops, 0, self->render_ops->len);
+  ops_reset (&self->op_builder);
   removed_textures = gsk_gl_driver_collect_textures (self->gl_driver);
 
   GSK_RENDERER_NOTE (GSK_RENDERER (self), OPENGL, g_message ("Collected: %d textures", removed_textures));
@@ -2976,11 +2975,13 @@ static void
 gsk_gl_renderer_render_ops (GskGLRenderer *self,
                             gsize          vertex_data_size)
 {
-  guint i;
-  guint n_ops = self->render_ops->len;
   const Program *program = NULL;
   gsize buffer_index = 0;
   float *vertex_data = g_malloc (vertex_data_size);
+  OpBuffer *buffer;
+  OpBufferIter iter;
+  OpKind kind;
+  gpointer ptr;
 
   /*g_message ("%s: Buffer size: %ld", __FUNCTION__, vertex_data_size);*/
 
@@ -2992,15 +2993,16 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self,
   glGenBuffers (1, &buffer_id);
   glBindBuffer (GL_ARRAY_BUFFER, buffer_id);
 
-
   // Fill buffer data
-  for (i = 0; i < n_ops; i ++)
+  buffer = ops_get_buffer (&self->op_builder);
+  op_buffer_iter_init (&iter, buffer);
+  while ((ptr = op_buffer_iter_next (&iter, &kind)))
     {
-      const RenderOp *op = &g_array_index (self->render_ops, RenderOp, i);
-
-      if (op->op == OP_CHANGE_VAO)
+      if (kind == OP_CHANGE_VAO)
         {
-          memcpy (vertex_data + buffer_index, &op->vertex_data, sizeof (GskQuadVertex) * GL_N_VERTICES);
+          const OpVao *vao = ptr;
+
+          memcpy (vertex_data + buffer_index, &vao->vertex_data, sizeof (GskQuadVertex) * GL_N_VERTICES);
           buffer_index += sizeof (GskQuadVertex) * GL_N_VERTICES / sizeof (float);
         }
     }
@@ -3021,41 +3023,42 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self,
                          sizeof (GskQuadVertex),
                          (void *) G_STRUCT_OFFSET (GskQuadVertex, uv));
 
-  for (i = 0; i < n_ops; i ++)
+  op_buffer_iter_init (&iter, ops_get_buffer (&self->op_builder));
+  while ((ptr = op_buffer_iter_next (&iter, &kind)))
     {
-      const RenderOp *op = &g_array_index (self->render_ops, RenderOp, i);
-
-      if (op->op == OP_NONE ||
-          op->op == OP_CHANGE_VAO)
+      if (kind == OP_NONE || kind == OP_CHANGE_VAO)
         continue;
 
       if (program == NULL &&
-          op->op != OP_PUSH_DEBUG_GROUP &&
-          op->op != OP_POP_DEBUG_GROUP &&
-          op->op != OP_CHANGE_PROGRAM &&
-          op->op != OP_CHANGE_RENDER_TARGET &&
-          op->op != OP_CLEAR)
+          kind != OP_PUSH_DEBUG_GROUP &&
+          kind != OP_POP_DEBUG_GROUP &&
+          kind != OP_CHANGE_PROGRAM &&
+          kind != OP_CHANGE_RENDER_TARGET &&
+          kind != OP_CLEAR)
         continue;
 
-      OP_PRINT ("Op %u: %u", i, op->op);
+      OP_PRINT ("Op %u: %u", iter.pos - 2, kind);
 
-      switch (op->op)
+      switch (kind)
         {
         case OP_CHANGE_PROJECTION:
-          apply_projection_op (program, op);
+          apply_projection_op (program, ptr);
           break;
 
         case OP_CHANGE_MODELVIEW:
-          apply_modelview_op (program, op);
+          apply_modelview_op (program, ptr);
           break;
 
         case OP_CHANGE_PROGRAM:
-          apply_program_op (program, op);
-          program = op->program;
-          break;
+          {
+            const OpProgram *op = ptr;
+            apply_program_op (program, op);
+            program = op->program;
+            break;
+          }
 
         case OP_CHANGE_RENDER_TARGET:
-          apply_render_target_op (self, program, op);
+          apply_render_target_op (self, program, ptr);
           break;
 
         case OP_CLEAR:
@@ -3064,95 +3067,109 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self,
           break;
 
         case OP_CHANGE_VIEWPORT:
-          apply_viewport_op (program, op);
+          apply_viewport_op (program, ptr);
           break;
 
         case OP_CHANGE_OPACITY:
-          apply_opacity_op (program, op);
+          apply_opacity_op (program, ptr);
           break;
 
         case OP_CHANGE_COLOR_MATRIX:
-          apply_color_matrix_op (program, op);
+          apply_color_matrix_op (program, ptr);
           break;
 
         case OP_CHANGE_COLOR:
           /*g_assert (program == &self->color_program || program == &self->coloring_program ||*/
                     /*program == &self->shadow_program);*/
-          apply_color_op (program, op);
+          apply_color_op (program, ptr);
           break;
 
         case OP_CHANGE_BORDER_COLOR:
-          apply_border_color_op (program, op);
+          apply_border_color_op (program, ptr);
           break;
 
         case OP_CHANGE_CLIP:
-          apply_clip_op (program, op);
+          apply_clip_op (program, ptr);
           break;
 
         case OP_CHANGE_SOURCE_TEXTURE:
-          apply_source_texture_op (program, op);
+          apply_source_texture_op (program, ptr);
           break;
 
         case OP_CHANGE_CROSS_FADE:
           g_assert (program == &self->cross_fade_program);
-          apply_cross_fade_op (program, op);
+          apply_cross_fade_op (program, ptr);
           break;
 
         case OP_CHANGE_BLEND:
           g_assert (program == &self->blend_program);
-          apply_blend_op (program, op);
+          apply_blend_op (program, ptr);
           break;
 
         case OP_CHANGE_LINEAR_GRADIENT:
-          apply_linear_gradient_op (program, op);
+          apply_linear_gradient_op (program, ptr);
           break;
 
         case OP_CHANGE_BLUR:
-          apply_blur_op (program, op);
+          apply_blur_op (program, ptr);
           break;
 
         case OP_CHANGE_INSET_SHADOW:
-          apply_inset_shadow_op (program, op);
+          apply_inset_shadow_op (program, ptr);
           break;
 
         case OP_CHANGE_OUTSET_SHADOW:
-          apply_outset_shadow_op (program, op);
+          apply_outset_shadow_op (program, ptr);
           break;
 
         case OP_CHANGE_BORDER:
-          apply_border_op (program, op);
+          apply_border_op (program, ptr);
           break;
 
         case OP_CHANGE_BORDER_WIDTH:
-          apply_border_width_op (program, op);
+          apply_border_width_op (program, ptr);
           break;
 
         case OP_CHANGE_UNBLURRED_OUTSET_SHADOW:
-          apply_unblurred_outset_shadow_op (program, op);
+          apply_unblurred_outset_shadow_op (program, ptr);
           break;
 
         case OP_CHANGE_REPEAT:
-          apply_repeat_op (program, op);
+          apply_repeat_op (program, ptr);
           break;
 
         case OP_DRAW:
-          OP_PRINT (" -> draw %ld, size %ld and program %d\n",
-                    op->draw.vao_offset, op->draw.vao_size, program->index);
-          glDrawArrays (GL_TRIANGLES, op->draw.vao_offset, op->draw.vao_size);
-          break;
+          {
+            const OpDraw *op = ptr;
+
+            OP_PRINT (" -> draw %ld, size %ld and program %d\n",
+                      op->vao_offset, op->vao_size, program->index);
+            glDrawArrays (GL_TRIANGLES, op->vao_offset, op->vao_size);
+            break;
+          }
 
         case OP_DUMP_FRAMEBUFFER:
-          dump_framebuffer (op->dump.filename, op->dump.width, op->dump.height);
-          break;
+          {
+            const OpDumpFrameBuffer *op = ptr;
+
+            dump_framebuffer (op->filename, op->width, op->height);
+            break;
+          }
 
         case OP_PUSH_DEBUG_GROUP:
-          gdk_gl_context_push_debug_group (self->gl_context, op->debug_group.text);
-          break;
+          {
+            const OpDebugGroup *op = ptr;
+            gdk_gl_context_push_debug_group (self->gl_context, op->text);
+            break;
+          }
 
         case OP_POP_DEBUG_GROUP:
           gdk_gl_context_pop_debug_group (self->gl_context);
           break;
 
+        case OP_CHANGE_VAO:
+        case OP_NONE:
+        case OP_LAST:
         default:
           g_warn_if_reached ();
         }
@@ -3451,11 +3468,8 @@ gsk_gl_renderer_init (GskGLRenderer *self)
 {
   gsk_ensure_resources ();
 
-  self->render_ops = g_array_new (FALSE, FALSE, sizeof (RenderOp));
-
   ops_init (&self->op_builder);
   self->op_builder.renderer = self;
-  self->op_builder.render_ops = self->render_ops;
 
 #ifdef G_ENABLE_DEBUG
   {
diff --git a/gsk/gl/gskglrenderops.c b/gsk/gl/gskglrenderops.c
index 98736bcaca..663403aa2b 100644
--- a/gsk/gl/gskglrenderops.c
+++ b/gsk/gl/gskglrenderops.c
@@ -57,23 +57,23 @@ ops_dump_framebuffer (RenderOpBuilder *builder,
                       int              width,
                       int              height)
 {
-  RenderOp *op;
+  OpDumpFrameBuffer *op;
 
   op = ops_begin (builder, OP_DUMP_FRAMEBUFFER);
-  op->dump.filename = g_strdup (filename);
-  op->dump.width = width;
-  op->dump.height = height;
+  op->filename = g_strdup (filename);
+  op->width = width;
+  op->height = height;
 }
 
 void
 ops_push_debug_group (RenderOpBuilder *builder,
                       const char      *text)
 {
-  RenderOp *op;
+  OpDebugGroup *op;
 
   op = ops_begin (builder, OP_PUSH_DEBUG_GROUP);
-  strncpy (op->debug_group.text, text, sizeof(op->debug_group.text) - 1);
-  op->debug_group.text[sizeof(op->debug_group.text) - 1] = 0; /* Ensure zero terminated */
+  strncpy (op->text, text, sizeof(op->text) - 1);
+  op->text[sizeof(op->text) - 1] = 0; /* Ensure zero terminated */
 }
 
 void
@@ -182,6 +182,8 @@ ops_init (RenderOpBuilder *builder)
 
   builder->current_opacity = 1.0f;
 
+  op_buffer_init (&builder->render_ops);
+
   for (i = 0; i < GL_N_PROGRAMS; i ++)
     {
       builder->program_state[i].opacity = 1.0f;
@@ -197,6 +199,8 @@ ops_free (RenderOpBuilder *builder)
     {
       gsk_transform_unref (builder->program_state[i].modelview);
     }
+
+  op_buffer_destroy (&builder->render_ops);
 }
 
 void
@@ -208,7 +212,7 @@ ops_set_program (RenderOpBuilder *builder,
   static const GskRoundedRect empty_clip;
   static const graphene_matrix_t empty_matrix;
   static const graphene_rect_t empty_rect;
-  RenderOp *op;
+  OpProgram *op;
   ProgramState *program_state;
 
   if (builder->current_program == program)
@@ -225,16 +229,20 @@ ops_set_program (RenderOpBuilder *builder,
   if (memcmp (&empty_matrix, &program_state->projection, sizeof (graphene_matrix_t)) == 0 ||
       memcmp (&builder->current_projection, &program_state->projection, sizeof (graphene_matrix_t)) != 0)
     {
-      op = ops_begin (builder, OP_CHANGE_PROJECTION);
-      op->projection = builder->current_projection;
+      OpMatrix *opm;
+
+      opm = ops_begin (builder, OP_CHANGE_PROJECTION);
+      opm->matrix = builder->current_projection;
       program_state->projection = builder->current_projection;
     }
 
   if (program_state->modelview == NULL ||
       !gsk_transform_equal (builder->current_modelview, program_state->modelview))
     {
-      op = ops_begin (builder, OP_CHANGE_MODELVIEW);
-      gsk_transform_to_matrix (builder->current_modelview, &op->modelview);
+      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);
     }
@@ -242,23 +250,29 @@ ops_set_program (RenderOpBuilder *builder,
   if (rect_equal (&empty_rect, &program_state->viewport) ||
       !rect_equal (&builder->current_viewport, &program_state->viewport))
     {
-      op = ops_begin (builder, OP_CHANGE_VIEWPORT);
-      op->viewport = builder->current_viewport;
+      OpViewport *opv;
+
+      opv = ops_begin (builder, OP_CHANGE_VIEWPORT);
+      opv->viewport = builder->current_viewport;
       program_state->viewport = builder->current_viewport;
     }
 
   if (memcmp (&empty_clip, &program_state->clip, sizeof (GskRoundedRect)) == 0 ||
       memcmp (&builder->current_clip, &program_state->clip, sizeof (GskRoundedRect)) != 0)
     {
-      op = ops_begin (builder, OP_CHANGE_CLIP);
-      op->clip = *builder->current_clip;
+      OpClip *opc;
+
+      opc = ops_begin (builder, OP_CHANGE_CLIP);
+      opc->clip = *builder->current_clip;
       program_state->clip = *builder->current_clip;
     }
 
   if (program_state->opacity != builder->current_opacity)
     {
-      op = ops_begin (builder, OP_CHANGE_OPACITY);
-      op->opacity = builder->current_opacity;
+      OpOpacity *opo;
+
+      opo = ops_begin (builder, OP_CHANGE_OPACITY);
+      opo->opacity = builder->current_opacity;
       program_state->opacity = builder->current_opacity;
     }
 }
@@ -267,29 +281,17 @@ static void
 ops_set_clip (RenderOpBuilder      *builder,
               const GskRoundedRect *clip)
 {
-  RenderOp *last_op;
   ProgramState *current_program_state = get_current_program_state (builder);
+  OpClip *op;
 
   if (current_program_state &&
       memcmp (&current_program_state->clip, clip,sizeof (GskRoundedRect)) == 0)
     return;
 
-  if (builder->render_ops->len > 0)
-    {
-      last_op = &g_array_index (builder->render_ops, RenderOp, builder->render_ops->len - 1);
-
-      if (last_op->op == OP_CHANGE_CLIP)
-        {
-          last_op->clip = *clip;
-        }
-      else
-        {
-          RenderOp *op;
-
-          op = ops_begin (builder, OP_CHANGE_CLIP);
-          op->clip = *clip;
-        }
-    }
+  if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_CLIP)))
+    op = op_buffer_add (&builder->render_ops, OP_CHANGE_CLIP);
+
+  op->clip = *clip;
 
   if (builder->current_program != NULL)
     current_program_state->clip = *clip;
@@ -343,8 +345,8 @@ ops_set_modelview_internal (RenderOpBuilder *builder,
                             GskTransform    *transform)
 {
   ProgramState *current_program_state = get_current_program_state (builder);
-  RenderOp *op;
   graphene_matrix_t matrix;
+  OpMatrix *op;
 
 #if 0
   XXX This is not possible if we want pop() to work.
@@ -355,24 +357,10 @@ ops_set_modelview_internal (RenderOpBuilder *builder,
 
   gsk_transform_to_matrix (transform, &matrix);
 
-  if (builder->render_ops->len > 0)
-    {
-      RenderOp *last_op = &g_array_index (builder->render_ops, RenderOp, builder->render_ops->len - 1);
-      if (last_op->op == OP_CHANGE_MODELVIEW)
-        {
-          last_op->modelview = matrix;
-        }
-      else
-        {
-          op = ops_begin (builder, OP_CHANGE_MODELVIEW);
-          op->modelview = matrix;
-        }
-    }
-  else
-    {
-      op = ops_begin (builder, OP_CHANGE_MODELVIEW);
-      op->modelview = matrix;
-    }
+  if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_MODELVIEW)))
+    op = op_buffer_add (&builder->render_ops, OP_CHANGE_MODELVIEW);
+
+  op->matrix = matrix;
 
   if (builder->current_program != NULL)
     {
@@ -490,27 +478,13 @@ ops_set_projection (RenderOpBuilder         *builder,
                     const graphene_matrix_t *projection)
 {
   ProgramState *current_program_state = get_current_program_state (builder);
-  RenderOp *op;
   graphene_matrix_t prev_mv;
+  OpMatrix *op;
 
-  if (builder->render_ops->len > 0)
-    {
-      RenderOp *last_op = &g_array_index (builder->render_ops, RenderOp, builder->render_ops->len - 1);
-      if (last_op->op == OP_CHANGE_PROJECTION)
-        {
-          last_op->projection = *projection;
-        }
-      else
-        {
-          op = ops_begin (builder, OP_CHANGE_PROJECTION);
-          op->projection = *projection;
-        }
-    }
-  else
-    {
-      op = ops_begin (builder, OP_CHANGE_PROJECTION);
-      op->projection = *projection;
-    }
+  if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_PROJECTION)))
+    op = op_buffer_add (&builder->render_ops, OP_CHANGE_PROJECTION);
+
+  op->matrix = *projection;
 
   if (builder->current_program != NULL)
     current_program_state->projection = *projection;
@@ -526,7 +500,7 @@ ops_set_viewport (RenderOpBuilder       *builder,
                   const graphene_rect_t *viewport)
 {
   ProgramState *current_program_state = get_current_program_state (builder);
-  RenderOp *op;
+  OpViewport *op;
   graphene_rect_t prev_viewport;
 
   if (current_program_state != NULL &&
@@ -549,7 +523,7 @@ void
 ops_set_texture (RenderOpBuilder *builder,
                  int              texture_id)
 {
-  RenderOp *op;
+  OpTexture *op;
 
   if (builder->current_texture == texture_id)
     return;
@@ -563,7 +537,7 @@ int
 ops_set_render_target (RenderOpBuilder *builder,
                        int              render_target_id)
 {
-  RenderOp *op;
+  OpRenderTarget *op;
   int prev_render_target;
 
   if (builder->current_render_target == render_target_id)
@@ -571,24 +545,10 @@ ops_set_render_target (RenderOpBuilder *builder,
 
   prev_render_target = builder->current_render_target;
 
-  if (builder->render_ops->len > 0)
-    {
-      RenderOp *last_op = &g_array_index (builder->render_ops, RenderOp, builder->render_ops->len - 1);
-      if (last_op->op == OP_CHANGE_RENDER_TARGET)
-        {
-          last_op->render_target_id = render_target_id;
-        }
-      else
-        {
-          op = ops_begin (builder, OP_CHANGE_RENDER_TARGET);
-          op->render_target_id = render_target_id;
-        }
-    }
-  else
-    {
-      op = ops_begin (builder, OP_CHANGE_RENDER_TARGET);
-      op->render_target_id = render_target_id;
-    }
+  if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_RENDER_TARGET)))
+    op = op_buffer_add (&builder->render_ops, OP_CHANGE_RENDER_TARGET);
+
+  op->render_target_id = render_target_id;
 
   builder->current_render_target = render_target_id;
 
@@ -600,32 +560,16 @@ ops_set_opacity (RenderOpBuilder *builder,
                  float            opacity)
 {
   ProgramState *current_program_state = get_current_program_state (builder);
-  RenderOp *op;
+  OpOpacity *op;
   float prev_opacity;
-  RenderOp *last_op;
 
   if (builder->current_opacity == opacity)
     return opacity;
 
-  if (builder->render_ops->len > 0)
-    {
-      last_op = &g_array_index (builder->render_ops, RenderOp, builder->render_ops->len - 1);
-
-      if (last_op->op == OP_CHANGE_OPACITY)
-        {
-          last_op->opacity = opacity;
-        }
-      else
-        {
-          op = ops_begin (builder, OP_CHANGE_OPACITY);
-          op->opacity = opacity;
-        }
-    }
-  else
-    {
-      op = ops_begin (builder, OP_CHANGE_OPACITY);
-      op->opacity = opacity;
-    }
+  if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_OPACITY)))
+    op = op_buffer_add (&builder->render_ops, OP_CHANGE_OPACITY);
+
+  op->opacity = opacity;
 
   prev_opacity = builder->current_opacity;
   builder->current_opacity = opacity;
@@ -641,7 +585,7 @@ ops_set_color (RenderOpBuilder *builder,
                const GdkRGBA   *color)
 {
   ProgramState *current_program_state = get_current_program_state (builder);
-  RenderOp *op;
+  OpColor *op;
 
   if (gdk_rgba_equal (color, &current_program_state->color))
     return;
@@ -649,7 +593,7 @@ ops_set_color (RenderOpBuilder *builder,
   current_program_state->color = *color;
 
   op = ops_begin (builder, OP_CHANGE_COLOR);
-  op->color = color;
+  op->rgba = *color;
 }
 
 void
@@ -658,7 +602,7 @@ ops_set_color_matrix (RenderOpBuilder         *builder,
                       const graphene_vec4_t   *offset)
 {
   ProgramState *current_program_state = get_current_program_state (builder);
-  RenderOp *op;
+  OpColorMatrix *op;
 
   if (memcmp (matrix,
               &current_program_state->color_matrix.matrix,
@@ -672,8 +616,8 @@ ops_set_color_matrix (RenderOpBuilder         *builder,
   current_program_state->color_matrix.offset = *offset;
 
   op = ops_begin (builder, OP_CHANGE_COLOR_MATRIX);
-  op->color_matrix.matrix = *matrix;
-  op->color_matrix.offset = *offset;
+  op->matrix = *matrix;
+  op->offset = *offset;
 }
 
 void
@@ -681,7 +625,7 @@ ops_set_border (RenderOpBuilder      *builder,
                 const GskRoundedRect *outline)
 {
   ProgramState *current_program_state = get_current_program_state (builder);
-  RenderOp *op;
+  OpBorder *op;
 
   if (memcmp (&current_program_state->border.outline,
               outline, sizeof (GskRoundedRect)) == 0)
@@ -690,7 +634,7 @@ ops_set_border (RenderOpBuilder      *builder,
   current_program_state->border.outline = *outline;
 
   op = ops_begin (builder, OP_CHANGE_BORDER);
-  op->border.outline = *outline;
+  op->outline = *outline;
 }
 
 void
@@ -698,7 +642,7 @@ ops_set_border_width (RenderOpBuilder *builder,
                       const float     *widths)
 {
   ProgramState *current_program_state = get_current_program_state (builder);
-  RenderOp *op;
+  OpBorder *op;
 
   if (memcmp (current_program_state->border.widths,
               widths, sizeof (float) * 4) == 0)
@@ -708,10 +652,10 @@ ops_set_border_width (RenderOpBuilder *builder,
           widths, sizeof (float) * 4);
 
   op = ops_begin (builder, OP_CHANGE_BORDER_WIDTH);
-  op->border.widths[0] = widths[0];
-  op->border.widths[1] = widths[1];
-  op->border.widths[2] = widths[2];
-  op->border.widths[3] = widths[3];
+  op->widths[0] = widths[0];
+  op->widths[1] = widths[1];
+  op->widths[2] = widths[2];
+  op->widths[3] = widths[3];
 }
 
 void
@@ -719,64 +663,50 @@ ops_set_border_color (RenderOpBuilder *builder,
                       const GdkRGBA   *color)
 {
   ProgramState *current_program_state = get_current_program_state (builder);
-  RenderOp op;
-  op.op = OP_CHANGE_BORDER_COLOR;
-  rgba_to_float (color, op.border.color);
+  OpBorder *op;
+  float fcolor[4];
 
-  if (memcmp (&op.border.color, &current_program_state->border.color,
-              sizeof (float) * 4) == 0)
-    return;
+  rgba_to_float (color, fcolor);
 
-  rgba_to_float (color, current_program_state->border.color);
+  if (memcmp (fcolor, &current_program_state->border.color, sizeof fcolor) == 0)
+    return;
 
-  g_array_append_val (builder->render_ops, op);
+  op = op_buffer_add (&builder->render_ops, OP_CHANGE_BORDER_COLOR);
+  memcpy (op->color, fcolor, sizeof (float[4]));
+  memcpy (current_program_state->border.color, fcolor, sizeof (float[4]));
 }
 
 void
 ops_draw (RenderOpBuilder     *builder,
           const GskQuadVertex  vertex_data[GL_N_VERTICES])
 {
-  RenderOp *last_op;
+  OpDraw *op;
+  OpVao *vao;
 
-  last_op = &g_array_index (builder->render_ops, RenderOp, builder->render_ops->len - 1);
-  /* If the previous op was a DRAW as well, we didn't change anything between the two calls,
-   * so these are just 2 subsequent draw calls. Same VAO, same program etc.
-   * And the offsets into the vao are in order as well, so make it one draw call. */
-  if (last_op->op == OP_DRAW)
+  if ((op = op_buffer_peek_tail_checked (&builder->render_ops, OP_DRAW)))
     {
-      /* We allow ourselves a little trick here. We still have to add a CHANGE_VAO op for
-       * this draw call so we can add our vertex data there, but we want it to be placed before
-       * the last draw call, so we reorder those. */
-      RenderOp *new_draw;
+      gsize old_vao_offset = op->vao_offset;
+      gsize old_vao_size = op->vao_size;
 
-      new_draw = ops_begin (builder, OP_DRAW);
+      op_buffer_pop_tail (&builder->render_ops);
 
-      /* last_op may have moved in memory */
-      last_op = &g_array_index (builder->render_ops, RenderOp, builder->render_ops->len - 2);
+      vao = op_buffer_add (&builder->render_ops, OP_CHANGE_VAO);
+      memcpy (vao->vertex_data, vertex_data, sizeof vao->vertex_data);
 
-      new_draw->draw.vao_offset = last_op->draw.vao_offset;
-      new_draw->draw.vao_size = last_op->draw.vao_size + GL_N_VERTICES;
-
-      last_op->op = OP_CHANGE_VAO;
-      memcpy (&last_op->vertex_data, vertex_data, sizeof(GskQuadVertex) * GL_N_VERTICES);
+      op = op_buffer_add (&builder->render_ops, OP_DRAW);
+      op->vao_offset = old_vao_offset;
+      op->vao_size = old_vao_size + GL_N_VERTICES;
     }
   else
     {
-      const gsize n_ops = builder->render_ops->len;
-      RenderOp *op;
       gsize offset = builder->buffer_size / sizeof (GskQuadVertex);
 
-      /* We will add two render ops here. */
-      g_array_set_size (builder->render_ops, n_ops + 2);
-
-      op = &g_array_index (builder->render_ops, RenderOp, n_ops);
-      op->op = OP_CHANGE_VAO;
-      memcpy (&op->vertex_data, vertex_data, sizeof(GskQuadVertex) * GL_N_VERTICES);
+      vao = op_buffer_add (&builder->render_ops, OP_CHANGE_VAO);
+      memcpy (vao->vertex_data, vertex_data, sizeof vao->vertex_data);
 
-      op = &g_array_index (builder->render_ops, RenderOp, n_ops + 1);
-      op->op = OP_DRAW;
-      op->draw.vao_offset = offset;
-      op->draw.vao_size = GL_N_VERTICES;
+      op = op_buffer_add (&builder->render_ops, OP_DRAW);
+      op->vao_offset = offset;
+      op->vao_size = GL_N_VERTICES;
     }
 
   /* We added new vertex data in both cases so increase the buffer size */
@@ -795,15 +725,21 @@ ops_offset (RenderOpBuilder *builder,
   builder->dy += y;
 }
 
-RenderOp *
+gpointer
 ops_begin (RenderOpBuilder *builder,
-           guint            kind)
+           OpKind           kind)
 {
-  RenderOp *op;
+  return op_buffer_add (&builder->render_ops, kind);
+}
 
-  g_array_set_size (builder->render_ops, builder->render_ops->len + 1);
-  op = &g_array_index (builder->render_ops, RenderOp, builder->render_ops->len - 1);
-  op->op = kind;
+void
+ops_reset (RenderOpBuilder *builder)
+{
+  op_buffer_clear (&builder->render_ops);
+}
 
-  return op;
+OpBuffer *
+ops_get_buffer (RenderOpBuilder *builder)
+{
+  return &builder->render_ops;
 }
diff --git a/gsk/gl/gskglrenderopsprivate.h b/gsk/gl/gskglrenderopsprivate.h
index 5f43eace61..85765ad44f 100644
--- a/gsk/gl/gskglrenderopsprivate.h
+++ b/gsk/gl/gskglrenderopsprivate.h
@@ -10,6 +10,8 @@
 #include "gskglrenderer.h"
 #include "gskrendernodeprivate.h"
 
+#include "opbuffer.h"
+
 #define GL_N_VERTICES 6
 #define GL_N_PROGRAMS 13
 
@@ -32,38 +34,7 @@ typedef struct
   OpsMatrixMetadata metadata;
 } MatrixStackEntry;
 
-enum {
-  OP_NONE,
-  OP_CHANGE_OPACITY         =  1,
-  OP_CHANGE_COLOR           =  2,
-  OP_CHANGE_PROJECTION      =  3,
-  OP_CHANGE_MODELVIEW       =  4,
-  OP_CHANGE_PROGRAM         =  5,
-  OP_CHANGE_RENDER_TARGET   =  6,
-  OP_CHANGE_CLIP            =  7,
-  OP_CHANGE_VIEWPORT        =  8,
-  OP_CHANGE_SOURCE_TEXTURE  =  9,
-  OP_CHANGE_VAO             =  10,
-  OP_CHANGE_LINEAR_GRADIENT =  11,
-  OP_CHANGE_COLOR_MATRIX    =  12,
-  OP_CHANGE_BLUR            =  13,
-  OP_CHANGE_INSET_SHADOW    =  14,
-  OP_CHANGE_OUTSET_SHADOW   =  15,
-  OP_CHANGE_BORDER          =  16,
-  OP_CHANGE_BORDER_COLOR    =  17,
-  OP_CHANGE_BORDER_WIDTH    =  18,
-  OP_CHANGE_CROSS_FADE      =  19,
-  OP_CHANGE_UNBLURRED_OUTSET_SHADOW = 20,
-  OP_CLEAR                  =  21,
-  OP_DRAW                   =  22,
-  OP_DUMP_FRAMEBUFFER       =  23,
-  OP_PUSH_DEBUG_GROUP       =  24,
-  OP_POP_DEBUG_GROUP        =  25,
-  OP_CHANGE_BLEND           =  26,
-  OP_CHANGE_REPEAT          =  27,
-};
-
-typedef struct
+struct _Program
 {
   int index;        /* Into the renderer's program array */
 
@@ -145,101 +116,7 @@ typedef struct
       int texture_rect_location;
     } repeat;
   };
-
-} Program;
-
-typedef struct
-{
-  guint op;
-
-  union {
-    float opacity;
-    graphene_matrix_t modelview;
-    graphene_matrix_t projection;
-    const Program *program;
-    int texture_id;
-    int render_target_id;
-    const GdkRGBA *color;
-    GskQuadVertex vertex_data[6];
-    GskRoundedRect clip;
-    graphene_rect_t viewport;
-    struct {
-      int n_color_stops;
-      float color_offsets[8];
-      float color_stops[4 * 8];
-      graphene_point_t start_point;
-      graphene_point_t end_point;
-    } linear_gradient;
-    struct {
-      gsize vao_offset;
-      gsize vao_size;
-    } draw;
-    struct {
-      graphene_matrix_t matrix;
-      graphene_vec4_t offset;
-    } color_matrix;
-    struct {
-      float radius;
-      graphene_size_t size;
-      float dir[2];
-    } blur;
-    struct {
-      float outline[4];
-      float corner_widths[4];
-      float corner_heights[4];
-      float radius;
-      float spread;
-      float offset[2];
-      float color[4];
-    } inset_shadow;
-    struct {
-      float outline[4];
-      float corner_widths[4];
-      float corner_heights[4];
-      float radius;
-      float spread;
-      float offset[2];
-      float color[4];
-    } outset_shadow;
-    struct {
-      float outline[4];
-      float corner_widths[4];
-      float corner_heights[4];
-      float radius;
-      float spread;
-      float offset[2];
-      float color[4];
-    } unblurred_outset_shadow;
-    struct {
-      float color[4];
-    } shadow;
-    struct {
-      float widths[4];
-      float color[4];
-      GskRoundedRect outline;
-    } border;
-    struct {
-      float progress;
-      int source2;
-    } cross_fade;
-    struct {
-      int source2;
-      int mode;
-    } blend;
-    struct {
-      float child_bounds[4];
-      float texture_rect[4];
-    } repeat;
-    struct {
-      char *filename;
-      int width;
-      int height;
-    } dump;
-    struct {
-      char text[180]; /* Size of linear_gradient, so 'should be enough' without growing RenderOp */
-    } debug_group;
-  };
-} RenderOp;
+};
 
 typedef struct
 {
@@ -278,7 +155,8 @@ typedef struct
 
   gsize buffer_size;
 
-  GArray *render_ops;
+  OpBuffer render_ops;
+
   GskGLRenderer *renderer;
 
   /* Stack of modelview matrices */
@@ -298,6 +176,7 @@ void              ops_dump_framebuffer   (RenderOpBuilder         *builder,
                                           int                      height);
 void              ops_init               (RenderOpBuilder         *builder);
 void              ops_free               (RenderOpBuilder         *builder);
+void              ops_reset              (RenderOpBuilder         *builder);
 void              ops_push_debug_group    (RenderOpBuilder         *builder,
                                            const char              *text);
 void              ops_pop_debug_group     (RenderOpBuilder         *builder);
@@ -358,7 +237,8 @@ void              ops_offset             (RenderOpBuilder        *builder,
                                           float                   x,
                                           float                   y);
 
-RenderOp         *ops_begin              (RenderOpBuilder        *builder,
-                                          guint                   kind);
+gpointer          ops_begin              (RenderOpBuilder        *builder,
+                                          OpKind                  kind);
+OpBuffer         *ops_get_buffer         (RenderOpBuilder        *builder);
 
 #endif
diff --git a/gsk/gl/opbuffer.c b/gsk/gl/opbuffer.c
new file mode 100644
index 0000000000..2d3b16ffb6
--- /dev/null
+++ b/gsk/gl/opbuffer.c
@@ -0,0 +1,134 @@
+#include "opbuffer.h"
+
+#include <string.h>
+
+static guint op_sizes[OP_LAST] = {
+  0,
+  sizeof (OpOpacity),
+  sizeof (OpColor),
+  sizeof (OpMatrix),
+  sizeof (OpMatrix),
+  sizeof (OpProgram),
+  sizeof (OpRenderTarget),
+  sizeof (OpClip),
+  sizeof (OpViewport),
+  sizeof (OpTexture),
+  sizeof (OpVao),
+  sizeof (OpLinearGradient),
+  sizeof (OpColorMatrix),
+  sizeof (OpBlur),
+  sizeof (OpShadow),
+  sizeof (OpShadow),
+  sizeof (OpBorder),
+  sizeof (OpBorder),
+  sizeof (OpBorder),
+  sizeof (OpCrossFade),
+  sizeof (OpShadow),
+  0,
+  sizeof (OpDraw),
+  sizeof (OpDumpFrameBuffer),
+  sizeof (OpDebugGroup),
+  0,
+  sizeof (OpBlend),
+  sizeof (OpRepeat),
+};
+
+void
+op_buffer_init (OpBuffer *buffer)
+{
+  static gsize initialized = FALSE;
+
+  if (g_once_init_enter (&initialized))
+    {
+      guint i;
+
+      for (i = 0; i < G_N_ELEMENTS (op_sizes); i++)
+        {
+          guint size = op_sizes[i];
+
+          if (size > 0)
+            {
+              /* Round all op entry sizes to the nearest 16 to ensure
+               * that we guarantee proper alignments for all op entries.
+               * This is only done once on first use.
+               */
+#define CHECK_SIZE(s) else if (size < (s)) { size = s; }
+              if (0) {}
+              CHECK_SIZE (16)
+              CHECK_SIZE (32)
+              CHECK_SIZE (48)
+              CHECK_SIZE (64)
+              CHECK_SIZE (80)
+              CHECK_SIZE (96)
+              CHECK_SIZE (112)
+              CHECK_SIZE (128)
+              CHECK_SIZE (144)
+              CHECK_SIZE (160)
+              CHECK_SIZE (176)
+              CHECK_SIZE (192)
+              else g_assert_not_reached ();
+#undef CHECK_SIZE
+
+              op_sizes[i] = size;
+            }
+        }
+
+      g_once_init_leave (&initialized, TRUE);
+    }
+
+  memset (buffer, 0, sizeof *buffer);
+
+  buffer->buflen = 4096;
+  buffer->bufpos = 0;
+  buffer->buf = g_malloc (buffer->buflen);
+  buffer->index = g_array_new (FALSE, FALSE, sizeof (OpBufferEntry));
+
+  /* Add dummy entry to guarantee non-empty index */
+  op_buffer_add (buffer, OP_NONE);
+}
+
+void
+op_buffer_destroy (OpBuffer *buffer)
+{
+  g_free (buffer->buf);
+  g_array_unref (buffer->index);
+}
+
+void
+op_buffer_clear (OpBuffer *buffer)
+{
+  if (buffer->index->len > 1)
+    g_array_remove_range (buffer->index, 1, buffer->index->len - 1);
+  buffer->bufpos = 0;
+}
+
+static inline void
+ensure_buffer_space_for (OpBuffer *buffer,
+                         guint     size)
+{
+  if G_UNLIKELY (buffer->bufpos + size >= buffer->buflen)
+    {
+      buffer->buflen *= 2;
+      buffer->buf = g_realloc (buffer->buf, buffer->buflen);
+    }
+}
+
+gpointer
+op_buffer_add (OpBuffer *buffer,
+               OpKind    kind)
+{
+  guint size = op_sizes[kind];
+  OpBufferEntry entry;
+
+  entry.pos = buffer->bufpos;
+  entry.kind = kind;
+
+  if (size > 0)
+    ensure_buffer_space_for (buffer, size);
+
+  g_array_append_val (buffer->index, entry);
+
+  buffer->bufpos += size;
+
+  return &buffer->buf[entry.pos];
+}
diff --git a/gsk/gl/opbuffer.h b/gsk/gl/opbuffer.h
new file mode 100644
index 0000000000..d777e2ae41
--- /dev/null
+++ b/gsk/gl/opbuffer.h
@@ -0,0 +1,263 @@
+#ifndef __OP_BUFFER_H__
+#define __OP_BUFFER_H__
+
+#include <gdk/gdk.h>
+#include <gsk/gsk.h>
+#include <graphene.h>
+
+#include "gskgldriverprivate.h"
+
+typedef struct _Program Program;
+
+typedef enum
+{
+  OP_NONE                              =  0,
+  OP_CHANGE_OPACITY                    =  1,
+  OP_CHANGE_COLOR                      =  2,
+  OP_CHANGE_PROJECTION                 =  3,
+  OP_CHANGE_MODELVIEW                  =  4,
+  OP_CHANGE_PROGRAM                    =  5,
+  OP_CHANGE_RENDER_TARGET              =  6,
+  OP_CHANGE_CLIP                       =  7,
+  OP_CHANGE_VIEWPORT                   =  8,
+  OP_CHANGE_SOURCE_TEXTURE             =  9,
+  OP_CHANGE_VAO                        = 10,
+  OP_CHANGE_LINEAR_GRADIENT            = 11,
+  OP_CHANGE_COLOR_MATRIX               = 12,
+  OP_CHANGE_BLUR                       = 13,
+  OP_CHANGE_INSET_SHADOW               = 14,
+  OP_CHANGE_OUTSET_SHADOW              = 15,
+  OP_CHANGE_BORDER                     = 16,
+  OP_CHANGE_BORDER_COLOR               = 17,
+  OP_CHANGE_BORDER_WIDTH               = 18,
+  OP_CHANGE_CROSS_FADE                 = 19,
+  OP_CHANGE_UNBLURRED_OUTSET_SHADOW    = 20,
+  OP_CLEAR                             = 21,
+  OP_DRAW                              = 22,
+  OP_DUMP_FRAMEBUFFER                  = 23,
+  OP_PUSH_DEBUG_GROUP                  = 24,
+  OP_POP_DEBUG_GROUP                   = 25,
+  OP_CHANGE_BLEND                      = 26,
+  OP_CHANGE_REPEAT                     = 27,
+  OP_LAST
+} OpKind;
+
+/* OpNode are allocated within OpBuffer.pos, but we keep
+ * a secondary index into the locations of that buffer
+ * from OpBuffer.index. This allows peeking at the kind
+ * and quickly replacing existing entries when necessary.
+ */
+typedef struct
+{
+  guint  pos;
+  OpKind kind;
+} OpBufferEntry;
+
+typedef struct
+{
+  guint8  *buf;
+  gsize    buflen;
+  gsize    bufpos;
+  GArray  *index;
+} OpBuffer;
+
+typedef struct
+{
+  float opacity;
+} OpOpacity;
+
+typedef struct
+{
+  graphene_matrix_t matrix;
+} OpMatrix;
+
+typedef struct
+{
+  const Program *program;
+} OpProgram;
+
+typedef struct
+{
+  GdkRGBA rgba;
+} OpColor;
+
+typedef struct
+{
+  int render_target_id;
+} OpRenderTarget;
+
+typedef struct
+{
+  GskRoundedRect clip;
+} OpClip;
+
+typedef struct
+{
+  graphene_rect_t viewport;
+} OpViewport;
+
+typedef struct
+{
+  int texture_id;
+} OpTexture;
+
+typedef struct
+{
+  gsize vao_offset;
+  gsize vao_size;
+} OpDraw;
+
+typedef struct
+{
+  GskQuadVertex vertex_data[6];
+} OpVao;
+
+typedef struct
+{
+  float color_offsets[8];
+  float color_stops[4 * 8];
+  graphene_point_t start_point;
+  graphene_point_t end_point;
+  int n_color_stops;
+} OpLinearGradient;
+
+typedef struct
+{
+  graphene_matrix_t matrix;
+  graphene_vec4_t offset;
+} OpColorMatrix;
+
+typedef struct
+{
+  float radius;
+  graphene_size_t size;
+  float dir[2];
+} OpBlur;
+
+typedef struct
+{
+  float outline[4];
+  float corner_widths[4];
+  float corner_heights[4];
+  float radius;
+  float spread;
+  float offset[2];
+  float color[4];
+} OpShadow;
+
+typedef struct
+{
+  float widths[4];
+  float color[4];
+  GskRoundedRect outline;
+} OpBorder;
+
+typedef struct
+{
+  float progress;
+  int source2;
+} OpCrossFade;
+
+typedef struct
+{
+  char *filename;
+  int width;
+  int height;
+} OpDumpFrameBuffer;
+
+typedef struct
+{
+  char text[64];
+} OpDebugGroup;
+
+typedef struct
+{
+  int source2;
+  int mode;
+} OpBlend;
+
+typedef struct
+{
+  float child_bounds[4];
+  float texture_rect[4];
+} OpRepeat;
+
+void     op_buffer_init            (OpBuffer *buffer);
+void     op_buffer_destroy         (OpBuffer *buffer);
+void     op_buffer_clear           (OpBuffer *buffer);
+gpointer op_buffer_add             (OpBuffer *buffer,
+                                    OpKind    kind);
+
+typedef struct
+{
+  GArray   *index;
+  OpBuffer *buffer;
+  guint     pos;
+} OpBufferIter;
+
+static inline void
+op_buffer_iter_init (OpBufferIter *iter,
+                     OpBuffer     *buffer)
+{
+  iter->index = buffer->index;
+  iter->buffer = buffer;
+  iter->pos = 1; /* Skip first OP_NONE */
+}
+
+static inline gpointer
+op_buffer_iter_next (OpBufferIter *iter,
+                     OpKind       *kind)
+{
+  const OpBufferEntry *entry;
+
+  if (iter->pos == iter->index->len)
+    return NULL;
+
+  entry = &g_array_index (iter->index, OpBufferEntry, iter->pos);
+
+  iter->pos++;
+
+  *kind = entry->kind;
+  return &iter->buffer->buf[entry->pos];
+}
+
+static inline void
+op_buffer_pop_tail (OpBuffer *buffer)
+{
+  /* Never truncate the first OP_NONE */
+  if G_LIKELY (buffer->index->len > 0)
+    buffer->index->len--;
+}
+
+static inline gpointer
+op_buffer_peek_tail (OpBuffer *buffer,
+                     OpKind   *kind)
+{
+  const OpBufferEntry *entry;
+
+  entry = &g_array_index (buffer->index, OpBufferEntry, buffer->index->len - 1);
+  *kind = entry->kind;
+  return &buffer->buf[entry->pos];
+}
+
+static inline gpointer
+op_buffer_peek_tail_checked (OpBuffer *buffer,
+                             OpKind    kind)
+{
+  const OpBufferEntry *entry;
+
+  entry = &g_array_index (buffer->index, OpBufferEntry, buffer->index->len - 1);
+
+  if (entry->kind == kind)
+    return &buffer->buf[entry->pos];
+
+  return NULL;
+}
+
+static inline guint
+op_buffer_n_ops (OpBuffer *buffer)
+{
+  return buffer->index->len - 1;
+}
+
+#endif /* __OP_BUFFER_H__ */
diff --git a/gsk/meson.build b/gsk/meson.build
index 6819ac5426..ed621284bb 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -48,6 +48,7 @@ gsk_private_sources = files([
   'gl/gskglnodesample.c',
   'gl/gskgltextureatlas.c',
   'gl/gskgliconcache.c',
+  'gl/opbuffer.c',
   'gl/stb_rect_pack.c',
 ])
 


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