[gtk/wip/chergert/glproto] do less work



commit 5418cc7fe01ef9317b0b836f4a0c12f26d08cd9b
Author: Christian Hergert <chergert redhat com>
Date:   Tue Feb 2 20:55:19 2021 -0800

    do less work
    
     * simplify adding vertices, remove branch
     * inline adding vertices in cmdqueue and buffer
     * avoid comparing shared state when we know its the same (only about 25% of time)

 gsk/next/gskglbufferprivate.h       |   4 +-
 gsk/next/gskglcommandqueue.c        | 194 +++----------------------
 gsk/next/gskglcommandqueueprivate.h | 144 ++++++++++++++++++-
 gsk/next/gskglprogramprivate.h      |   3 +
 gsk/next/gskglrenderjob.c           | 280 +++++++++++++-----------------------
 5 files changed, 266 insertions(+), 359 deletions(-)
---
diff --git a/gsk/next/gskglbufferprivate.h b/gsk/next/gskglbufferprivate.h
index 6c7e15924f..01fda316cd 100644
--- a/gsk/next/gskglbufferprivate.h
+++ b/gsk/next/gskglbufferprivate.h
@@ -43,8 +43,7 @@ GLuint       gsk_gl_buffer_submit (GskGLBuffer *buffer);
 
 static inline gpointer
 gsk_gl_buffer_advance (GskGLBuffer *buffer,
-                       guint        count,
-                       guint       *offset)
+                       guint        count)
 {
   gpointer ret;
   gsize to_alloc = count * buffer->element_size;
@@ -55,7 +54,6 @@ gsk_gl_buffer_advance (GskGLBuffer *buffer,
       buffer->buffer = g_realloc (buffer->buffer, buffer->buffer_len);
     }
 
-  *offset = buffer->count;
   ret = buffer->buffer + buffer->buffer_pos;
 
   buffer->buffer_pos += to_alloc;
diff --git a/gsk/next/gskglcommandqueue.c b/gsk/next/gskglcommandqueue.c
index d89da4e33d..50c5d02d5c 100644
--- a/gsk/next/gskglcommandqueue.c
+++ b/gsk/next/gskglcommandqueue.c
@@ -37,135 +37,6 @@
 #include "gskglcommandqueueprivate.h"
 #include "gskgluniformstateprivate.h"
 
-typedef enum _GskGLCommandKind
-{
-  /* The batch will perform a glClear() */
-  GSK_GL_COMMAND_KIND_CLEAR,
-
-  /* THe batch represents a new debug group */
-  GSK_GL_COMMAND_KIND_PUSH_DEBUG_GROUP,
-
-  /* The batch represents the end of a debug group */
-  GSK_GL_COMMAND_KIND_POP_DEBUG_GROUP,
-
-  /* The batch will perform a glDrawArrays() */
-  GSK_GL_COMMAND_KIND_DRAW,
-} GskGLCommandKind;
-
-typedef struct _GskGLCommandBind
-{
-  /* @texture is the value passed to glActiveTexture(), the "slot" the
-   * texture will be placed into. We always use GL_TEXTURE_2D so we don't
-   * waste any bits here to indicate that.
-   */
-  guint texture : 5;
-
-  /* The identifier for the texture created with glGenTextures(). */
-  guint id : 27;
-} GskGLCommandBind;
-
-G_STATIC_ASSERT (sizeof (GskGLCommandBind) == 4);
-
-typedef struct _GskGLCommandBatchAny
-{
-  /* A GskGLCommandKind indicating what the batch will do */
-  guint kind : 8;
-
-  /* The program's identifier to use for determining if we can merge two
-   * batches together into a single set of draw operations. We put this
-   * here instead of the GskGLCommandDraw so that we can use the extra
-   * bits here without making the structure larger.
-   */
-  guint program : 24;
-
-  /* The index of the next batch following this one. This is used
-   * as a sort of integer-based linked list to simplify out-of-order
-   * batching without moving memory around. -1 indicates last batch.
-   */
-  int next_batch_index;
-
-  /* The viewport size of the batch. We check this as we process
-   * batches to determine if we need to resize the viewport.
-   */
-  struct {
-    guint16 width;
-    guint16 height;
-  } viewport;
-} GskGLCommandBatchAny;
-
-G_STATIC_ASSERT (sizeof (GskGLCommandBatchAny) == 12);
-
-typedef struct _GskGLCommandDraw
-{
-  GskGLCommandBatchAny head;
-
-  /* There doesn't seem to be a limit on the framebuffer identifier that
-   * can be returned, so we have to use a whole unsigned for the framebuffer
-   * we are drawing to. When processing batches, we check to see if this
-   * changes and adjust the render target accordingly. Some sorting is
-   * performed to reduce the amount we change framebuffers.
-   */
-  guint framebuffer;
-
-  /* The number of uniforms to change. This must be less than or equal to
-   * GL_MAX_UNIFORM_LOCATIONS but only guaranteed up to 1024 by any OpenGL
-   * implementation to be conformant.
-   */
-  guint uniform_count : 11;
-
-  /* The number of textures to bind, which is only guaranteed up to 16
-   * by the OpenGL specification to be conformant.
-   */
-  guint bind_count : 5;
-
-  /* GL_MAX_ELEMENTS_VERTICES specifies 33000 for this which requires 16-bit
-   * to address all possible counts <= GL_MAX_ELEMENTS_VERTICES.
-   */
-  guint vbo_count : 16;
-
-  /* The offset within the VBO containing @vbo_count vertices to send with
-   * glDrawArrays().
-   */
-  guint vbo_offset;
-
-  /* The offset within the array of uniform changes to be made containing
-   * @uniform_count #GskGLCommandUniform elements to apply.
-   */
-  guint uniform_offset;
-
-  /* The offset within the array of bind changes to be made containing
-   * @bind_count #GskGLCommandBind elements to apply.
-   */
-  guint bind_offset;
-} GskGLCommandDraw;
-
-G_STATIC_ASSERT (sizeof (GskGLCommandDraw) == 32);
-
-typedef struct _GskGLCommandUniform
-{
-  GskGLUniformInfo info;
-  guint            location;
-} GskGLCommandUniform;
-
-G_STATIC_ASSERT (sizeof (GskGLCommandUniform) == 8);
-
-typedef union _GskGLCommandBatch
-{
-  GskGLCommandBatchAny    any;
-  GskGLCommandDraw        draw;
-  struct {
-    GskGLCommandBatchAny  any;
-    const char           *debug_group;
-  } debug_group;
-  struct {
-    GskGLCommandBatchAny  any;
-    guint                 bits;
-    guint                 framebuffer;
-  } clear;
-} GskGLCommandBatch;
-
-G_STATIC_ASSERT (sizeof (GskGLCommandBatch) == 32);
-
 G_DEFINE_TYPE (GskGLCommandQueue, gsk_gl_command_queue, G_TYPE_OBJECT)
 
 static inline void
@@ -665,31 +536,6 @@ gsk_gl_command_queue_split_draw (GskGLCommandQueue *self)
   gsk_gl_command_queue_begin_draw (self, program, &viewport);
 }
 
-GskGLDrawVertex *
-gsk_gl_command_queue_add_vertices (GskGLCommandQueue     *self,
-                                   const GskGLDrawVertex  vertices[GSK_GL_N_VERTICES])
-{
-  GskGLCommandBatch *batch;
-  GskGLDrawVertex *dest;
-  guint offset;
-
-  g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self), NULL);
-  g_return_val_if_fail (self->in_draw == TRUE, NULL);
-
-  batch = &g_array_index (self->batches, GskGLCommandBatch, self->batches->len - 1);
-  batch->draw.vbo_count += GSK_GL_N_VERTICES;
-
-  dest = gsk_gl_buffer_advance (self->vertices, GSK_GL_N_VERTICES, &offset);
-
-  if (vertices != NULL)
-    {
-      memcpy (dest, vertices, sizeof (GskGLDrawVertex) * GSK_GL_N_VERTICES);
-      return NULL;
-    }
-
-  return dest;
-}
-
 void
 gsk_gl_command_queue_clear (GskGLCommandQueue     *self,
                             guint                  clear_bits,
@@ -697,9 +543,9 @@ gsk_gl_command_queue_clear (GskGLCommandQueue     *self,
 {
   GskGLCommandBatch *batch;
 
-  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
-  g_return_if_fail (self->in_draw == FALSE);
-  g_return_if_fail (self->batches->len < G_MAXINT);
+  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_assert (self->in_draw == FALSE);
+  g_assert (self->batches->len < G_MAXINT);
 
   if (clear_bits == 0)
     clear_bits = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
@@ -724,9 +570,9 @@ gsk_gl_command_queue_push_debug_group (GskGLCommandQueue *self,
 {
   GskGLCommandBatch *batch;
 
-  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
-  g_return_if_fail (self->in_draw == FALSE);
-  g_return_if_fail (self->batches->len < G_MAXINT);
+  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_assert (self->in_draw == FALSE);
+  g_assert (self->batches->len < G_MAXINT);
 
   batch = begin_next_batch (self);
   batch->any.kind = GSK_GL_COMMAND_KIND_PUSH_DEBUG_GROUP;
@@ -742,9 +588,9 @@ gsk_gl_command_queue_pop_debug_group (GskGLCommandQueue *self)
 {
   GskGLCommandBatch *batch;
 
-  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
-  g_return_if_fail (self->in_draw == FALSE);
-  g_return_if_fail (self->batches->len < G_MAXINT);
+  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_assert (self->in_draw == FALSE);
+  g_assert (self->batches->len < G_MAXINT);
 
   batch = begin_next_batch (self);
   batch->any.kind = GSK_GL_COMMAND_KIND_POP_DEBUG_GROUP;
@@ -766,8 +612,8 @@ gsk_gl_command_queue_get_context (GskGLCommandQueue *self)
 void
 gsk_gl_command_queue_make_current (GskGLCommandQueue *self)
 {
-  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
-  g_return_if_fail (GDK_IS_GL_CONTEXT (self->context));
+  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_assert (GDK_IS_GL_CONTEXT (self->context));
 
   gdk_gl_context_make_current (self->context);
 }
@@ -776,7 +622,7 @@ void
 gsk_gl_command_queue_delete_program (GskGLCommandQueue *self,
                                      guint              program)
 {
-  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
 
   gsk_gl_command_queue_make_current (self);
   glDeleteProgram (program);
@@ -938,8 +784,8 @@ gsk_gl_command_queue_execute (GskGLCommandQueue    *self,
   guint n_uniforms = 0;
   guint vbo_id;
 
-  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
-  g_return_if_fail (self->in_draw == FALSE);
+  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_assert (self->in_draw == FALSE);
 
   if (self->batches->len == 0)
     return;
@@ -1136,8 +982,8 @@ gsk_gl_command_queue_execute (GskGLCommandQueue    *self,
 void
 gsk_gl_command_queue_begin_frame (GskGLCommandQueue *self)
 {
-  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
-  g_return_if_fail (self->batches->len == 0);
+  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_assert (self->batches->len == 0);
 
   self->tail_batch_index = -1;
   self->in_frame = TRUE;
@@ -1158,8 +1004,8 @@ gsk_gl_command_queue_begin_frame (GskGLCommandQueue *self)
 void
 gsk_gl_command_queue_end_frame (GskGLCommandQueue *self)
 {
-  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
-  g_return_if_fail (self->saved_state->len == 0);
+  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_assert (self->saved_state->len == 0);
 
   gsk_gl_command_queue_make_current (self);
 
@@ -1378,8 +1224,8 @@ gsk_gl_command_queue_set_profiler (GskGLCommandQueue *self,
                                    GskProfiler       *profiler)
 {
 #ifdef G_ENABLE_DEBUG
-  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
-  g_return_if_fail (GSK_IS_PROFILER (profiler));
+  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_assert (GSK_IS_PROFILER (profiler));
 
   if (g_set_object (&self->profiler, profiler))
     {
diff --git a/gsk/next/gskglcommandqueueprivate.h b/gsk/next/gskglcommandqueueprivate.h
index 0ca22c10af..d43962fcbb 100644
--- a/gsk/next/gskglcommandqueueprivate.h
+++ b/gsk/next/gskglcommandqueueprivate.h
@@ -24,6 +24,7 @@
 #include <gsk/gskprofilerprivate.h>
 
 #include "gskgltypesprivate.h"
+#include "gskglbufferprivate.h"
 #include "gskglattachmentstateprivate.h"
 #include "gskgluniformstateprivate.h"
 
@@ -35,6 +36,135 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GskGLCommandQueue, gsk_gl_command_queue, GSK, GL_COMMAND_QUEUE, GObject)
 
+typedef enum _GskGLCommandKind
+{
+  /* The batch will perform a glClear() */
+  GSK_GL_COMMAND_KIND_CLEAR,
+
+  /* THe batch represents a new debug group */
+  GSK_GL_COMMAND_KIND_PUSH_DEBUG_GROUP,
+
+  /* The batch represents the end of a debug group */
+  GSK_GL_COMMAND_KIND_POP_DEBUG_GROUP,
+
+  /* The batch will perform a glDrawArrays() */
+  GSK_GL_COMMAND_KIND_DRAW,
+} GskGLCommandKind;
+
+typedef struct _GskGLCommandBind
+{
+  /* @texture is the value passed to glActiveTexture(), the "slot" the
+   * texture will be placed into. We always use GL_TEXTURE_2D so we don't
+   * waste any bits here to indicate that.
+   */
+  guint texture : 5;
+
+  /* The identifier for the texture created with glGenTextures(). */
+  guint id : 27;
+} GskGLCommandBind;
+
+G_STATIC_ASSERT (sizeof (GskGLCommandBind) == 4);
+
+typedef struct _GskGLCommandBatchAny
+{
+  /* A GskGLCommandKind indicating what the batch will do */
+  guint kind : 8;
+
+  /* The program's identifier to use for determining if we can merge two
+   * batches together into a single set of draw operations. We put this
+   * here instead of the GskGLCommandDraw so that we can use the extra
+   * bits here without making the structure larger.
+   */
+  guint program : 24;
+
+  /* The index of the next batch following this one. This is used
+   * as a sort of integer-based linked list to simplify out-of-order
+   * batching without moving memory around. -1 indicates last batch.
+   */
+  int next_batch_index;
+
+  /* The viewport size of the batch. We check this as we process
+   * batches to determine if we need to resize the viewport.
+   */
+  struct {
+    guint16 width;
+    guint16 height;
+  } viewport;
+} GskGLCommandBatchAny;
+
+G_STATIC_ASSERT (sizeof (GskGLCommandBatchAny) == 12);
+
+typedef struct _GskGLCommandDraw
+{
+  GskGLCommandBatchAny head;
+
+  /* There doesn't seem to be a limit on the framebuffer identifier that
+   * can be returned, so we have to use a whole unsigned for the framebuffer
+   * we are drawing to. When processing batches, we check to see if this
+   * changes and adjust the render target accordingly. Some sorting is
+   * performed to reduce the amount we change framebuffers.
+   */
+  guint framebuffer;
+
+  /* The number of uniforms to change. This must be less than or equal to
+   * GL_MAX_UNIFORM_LOCATIONS but only guaranteed up to 1024 by any OpenGL
+   * implementation to be conformant.
+   */
+  guint uniform_count : 11;
+
+  /* The number of textures to bind, which is only guaranteed up to 16
+   * by the OpenGL specification to be conformant.
+   */
+  guint bind_count : 5;
+
+  /* GL_MAX_ELEMENTS_VERTICES specifies 33000 for this which requires 16-bit
+   * to address all possible counts <= GL_MAX_ELEMENTS_VERTICES.
+   */
+  guint vbo_count : 16;
+
+  /* The offset within the VBO containing @vbo_count vertices to send with
+   * glDrawArrays().
+   */
+  guint vbo_offset;
+
+  /* The offset within the array of uniform changes to be made containing
+   * @uniform_count #GskGLCommandUniform elements to apply.
+   */
+  guint uniform_offset;
+
+  /* The offset within the array of bind changes to be made containing
+   * @bind_count #GskGLCommandBind elements to apply.
+   */
+  guint bind_offset;
+} GskGLCommandDraw;
+
+G_STATIC_ASSERT (sizeof (GskGLCommandDraw) == 32);
+
+typedef struct _GskGLCommandUniform
+{
+  GskGLUniformInfo info;
+  guint            location;
+} GskGLCommandUniform;
+
+G_STATIC_ASSERT (sizeof (GskGLCommandUniform) == 8);
+
+typedef union _GskGLCommandBatch
+{
+  GskGLCommandBatchAny    any;
+  GskGLCommandDraw        draw;
+  struct {
+    GskGLCommandBatchAny  any;
+    const char           *debug_group;
+  } debug_group;
+  struct {
+    GskGLCommandBatchAny  any;
+    guint                 bits;
+    guint                 framebuffer;
+  } clear;
+} GskGLCommandBatch;
+
+G_STATIC_ASSERT (sizeof (GskGLCommandBatch) == 32);
+
 struct _GskGLCommandQueue
 {
   GObject parent_instance;
@@ -176,8 +306,18 @@ void               gsk_gl_command_queue_begin_draw           (GskGLCommandQueue
                                                               const graphene_rect_t    *viewport);
 void               gsk_gl_command_queue_end_draw             (GskGLCommandQueue        *self);
 void               gsk_gl_command_queue_split_draw           (GskGLCommandQueue        *self);
-GskGLDrawVertex   *gsk_gl_command_queue_add_vertices         (GskGLCommandQueue        *self,
-                                                              const GskGLDrawVertex     
vertices[GSK_GL_N_VERTICES]);
+
+static inline GskGLDrawVertex *
+gsk_gl_command_queue_add_vertices (GskGLCommandQueue *self)
+{
+  GskGLCommandBatch *batch = &g_array_index (self->batches, GskGLCommandBatch, self->batches->len - 1);
+  GskGLDrawVertex *dest = gsk_gl_buffer_advance (self->vertices, GSK_GL_N_VERTICES);
+
+  batch->draw.vbo_count += GSK_GL_N_VERTICES;
+
+  return dest;
+}
+
 
 static inline void
 gsk_gl_command_queue_bind_framebuffer (GskGLCommandQueue *self,
diff --git a/gsk/next/gskglprogramprivate.h b/gsk/next/gskglprogramprivate.h
index 6c69f1ad9a..b958abf1e6 100644
--- a/gsk/next/gskglprogramprivate.h
+++ b/gsk/next/gskglprogramprivate.h
@@ -52,6 +52,9 @@ struct _GskGLProgram
   int texture_locations[4];
   int args_locations[8];
   int size_location;
+
+  /* Used to avoid comparing shared state */
+  guint last_shared_state;
 };
 
 GskGLProgram *gsk_gl_program_new         (GskNextDriver           *driver,
diff --git a/gsk/next/gskglrenderjob.c b/gsk/next/gskglrenderjob.c
index 2b0bea594e..9bf6b9e8b6 100644
--- a/gsk/next/gskglrenderjob.c
+++ b/gsk/next/gskglrenderjob.c
@@ -130,6 +130,9 @@ struct _GskGLRenderJob
   float scale_x;
   float scale_y;
 
+  /* Counter whenever we change shared state */
+  guint last_shared_state;
+
   /* If we should be rendering red zones over fallback nodes */
   guint debug_fallback : 1;
 };
@@ -179,6 +182,8 @@ typedef struct _GskGLRenderOffscreen
   guint was_offscreen : 1;
 } GskGLRenderOffscreen;
 
+static void     gsk_gl_render_job_begin_draw                (GskGLRenderJob       *job,
+                                                             GskGLProgram         *program);
 static void     gsk_gl_render_job_visit_node                (GskGLRenderJob       *job,
                                                              GskRenderNode        *node);
 static gboolean gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob       *job,
@@ -467,6 +472,8 @@ gsk_gl_render_job_set_modelview (GskGLRenderJob *job,
   g_assert (job != NULL);
   g_assert (job->modelview != NULL);
 
+  job->last_shared_state++;
+
   g_array_set_size (job->modelview, job->modelview->len + 1);
 
   modelview = &g_array_index (job->modelview,
@@ -496,6 +503,8 @@ gsk_gl_render_job_push_modelview (GskGLRenderJob *job,
   g_assert (job->modelview != NULL);
   g_assert (transform != NULL);
 
+  job->last_shared_state++;
+
   g_array_set_size (job->modelview, job->modelview->len + 1);
 
   modelview = &g_array_index (job->modelview,
@@ -545,6 +554,8 @@ gsk_gl_render_job_pop_modelview (GskGLRenderJob *job)
   g_assert (job->modelview);
   g_assert (job->modelview->len > 0);
 
+  job->last_shared_state++;
+
   head = gsk_gl_render_job_get_modelview (job);
 
   job->offset_x = head->offset_x_before;
@@ -596,6 +607,8 @@ gsk_gl_render_job_push_clip (GskGLRenderJob       *job,
   g_assert (job->clip != NULL);
   g_assert (rect != NULL);
 
+  job->last_shared_state++;
+
   g_array_set_size (job->clip, job->clip->len + 1);
 
   clip = &g_array_index (job->clip, GskGLRenderClip, job->clip->len - 1);
@@ -610,6 +623,8 @@ gsk_gl_render_job_pop_clip (GskGLRenderJob *job)
   g_assert (job->clip != NULL);
   g_assert (job->clip->len > 0);
 
+  job->last_shared_state++;
+
   job->clip->len--;
 }
 
@@ -618,8 +633,13 @@ gsk_gl_render_job_offset (GskGLRenderJob *job,
                           float           offset_x,
                           float           offset_y)
 {
-  job->offset_x += offset_x;
-  job->offset_y += offset_y;
+  if (offset_x || offset_y)
+    {
+      job->last_shared_state++;
+
+      job->offset_x += offset_x;
+      job->offset_y += offset_y;
+    }
 }
 
 static void
@@ -629,6 +649,8 @@ gsk_gl_render_state_save (GskGLRenderState *state,
   g_assert (state != NULL);
   g_assert (job != NULL);
 
+  job->last_shared_state++;
+
   memcpy (&state->viewport, &job->viewport, sizeof state->viewport);
   memcpy (&state->projection, &job->projection, sizeof state->projection);
 
@@ -647,6 +669,8 @@ gsk_gl_render_state_restore (GskGLRenderState *state,
   g_assert (state != NULL);
   g_assert (job != NULL);
 
+  job->last_shared_state++;
+
   memcpy (&job->viewport, &state->viewport, sizeof state->viewport);
   memcpy (&job->projection, &state->projection, sizeof state->projection);
 
@@ -719,7 +743,7 @@ gsk_gl_render_job_load_vertices_from_offscreen (GskGLRenderJob             *job,
                                                 const graphene_rect_t      *bounds,
                                                 const GskGLRenderOffscreen *offscreen)
 {
-  GskGLDrawVertex *vertices = gsk_gl_command_queue_add_vertices (job->command_queue, NULL);
+  GskGLDrawVertex *vertices = gsk_gl_command_queue_add_vertices (job->command_queue);
   float min_x = job->offset_x + bounds->origin.x;
   float min_y = job->offset_y + bounds->origin.y;
   float max_x = min_x + bounds->size.width;
@@ -766,7 +790,7 @@ gsk_gl_render_job_draw (GskGLRenderJob *job,
                         float           width,
                         float           height)
 {
-  GskGLDrawVertex *vertices = gsk_gl_command_queue_add_vertices (job->command_queue, NULL);
+  GskGLDrawVertex *vertices = gsk_gl_command_queue_add_vertices (job->command_queue);
   float min_x = job->offset_x + x;
   float min_y = job->offset_y + y;
   float max_x = min_x + width;
@@ -823,7 +847,7 @@ gsk_gl_render_job_draw_coords (GskGLRenderJob *job,
                                float           max_x,
                                float           max_y)
 {
-  GskGLDrawVertex *vertices = gsk_gl_command_queue_add_vertices (job->command_queue, NULL);
+  GskGLDrawVertex *vertices = gsk_gl_command_queue_add_vertices (job->command_queue);
 
   vertices[0].position[0] = min_x;
   vertices[0].position[1] = min_y;
@@ -898,12 +922,7 @@ gsk_gl_render_job_visit_as_fallback (GskGLRenderJob *job,
 
   if (cached_id != 0)
     {
-      gsk_gl_program_begin_draw (job->driver->blit,
-                                 &job->viewport,
-                                 &job->projection,
-                                 gsk_gl_render_job_get_modelview_matrix (job),
-                                 gsk_gl_render_job_get_clip (job),
-                                 job->alpha);
+      gsk_gl_render_job_begin_draw (job, job->driver->blit);
       gsk_gl_program_set_uniform_texture (job->driver->blit,
                                           UNIFORM_SHARED_SOURCE,
                                           GL_TEXTURE_2D, GL_TEXTURE0, cached_id);
@@ -981,12 +1000,7 @@ gsk_gl_render_job_visit_as_fallback (GskGLRenderJob *job,
 
   gsk_next_driver_cache_texture (job->driver, &key, texture_id);
 
-  gsk_gl_program_begin_draw (job->driver->blit,
-                             &job->viewport,
-                             &job->projection,
-                             gsk_gl_render_job_get_modelview_matrix (job),
-                             gsk_gl_render_job_get_clip (job),
-                             job->alpha);
+  gsk_gl_render_job_begin_draw (job, job->driver->blit);
   gsk_gl_program_set_uniform_texture (job->driver->blit,
                                       UNIFORM_SHARED_SOURCE,
                                       GL_TEXTURE_2D,
@@ -1046,12 +1060,7 @@ blur_offscreen (GskGLRenderJob       *job,
   /* Begin drawing the first horizontal pass, using offscreen as the
    * source texture for the program.
    */
-  gsk_gl_program_begin_draw (job->driver->blur,
-                             &job->viewport,
-                             &job->projection,
-                             gsk_gl_render_job_get_modelview_matrix (job),
-                             gsk_gl_render_job_get_clip (job),
-                             job->alpha);
+  gsk_gl_render_job_begin_draw (job, job->driver->blur);
   gsk_gl_program_set_uniform_texture (job->driver->blur,
                                       UNIFORM_SHARED_SOURCE,
                                       GL_TEXTURE_2D,
@@ -1075,12 +1084,7 @@ blur_offscreen (GskGLRenderJob       *job,
   gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
 
   /* Draw using blur program with first pass as source texture */
-  gsk_gl_program_begin_draw (job->driver->blur,
-                             &job->viewport,
-                             &job->projection,
-                             gsk_gl_render_job_get_modelview_matrix (job),
-                             gsk_gl_render_job_get_clip (job),
-                             job->alpha);
+  gsk_gl_render_job_begin_draw (job, job->driver->blur);
   gsk_gl_program_set_uniform_texture (job->driver->blur,
                                       UNIFORM_SHARED_SOURCE,
                                       GL_TEXTURE_2D,
@@ -1168,12 +1172,7 @@ static void
 gsk_gl_render_job_visit_color_node (GskGLRenderJob *job,
                                     GskRenderNode  *node)
 {
-  gsk_gl_program_begin_draw (job->driver->color,
-                             &job->viewport,
-                             &job->projection,
-                             gsk_gl_render_job_get_modelview_matrix (job),
-                             gsk_gl_render_job_get_clip (job),
-                             job->alpha);
+  gsk_gl_render_job_begin_draw (job, job->driver->color);
   gsk_gl_program_set_uniform_color (job->driver->color,
                                     UNIFORM_COLOR_COLOR,
                                     gsk_color_node_get_color (node));
@@ -1197,12 +1196,7 @@ gsk_gl_render_job_visit_linear_gradient_node (GskGLRenderJob *job,
 
   g_assert (n_color_stops < MAX_GRADIENT_STOPS);
 
-  gsk_gl_program_begin_draw (job->driver->linear_gradient,
-                             &job->viewport,
-                             &job->projection,
-                             gsk_gl_render_job_get_modelview_matrix (job),
-                             gsk_gl_render_job_get_clip (job),
-                             job->alpha);
+  gsk_gl_render_job_begin_draw (job, job->driver->linear_gradient);
   gsk_gl_program_set_uniform1i (job->driver->linear_gradient,
                                 UNIFORM_LINEAR_GRADIENT_NUM_COLOR_STOPS,
                                 n_color_stops);
@@ -1234,12 +1228,7 @@ gsk_gl_render_job_visit_conic_gradient_node (GskGLRenderJob *job,
 
   g_assert (n_color_stops < MAX_GRADIENT_STOPS);
 
-  gsk_gl_program_begin_draw (job->driver->conic_gradient,
-                             &job->viewport,
-                             &job->projection,
-                             gsk_gl_render_job_get_modelview_matrix (job),
-                             gsk_gl_render_job_get_clip (job),
-                             job->alpha);
+  gsk_gl_render_job_begin_draw (job, job->driver->conic_gradient);
   gsk_gl_program_set_uniform1i (job->driver->conic_gradient,
                                 UNIFORM_CONIC_GRADIENT_NUM_COLOR_STOPS,
                                 n_color_stops);
@@ -1274,12 +1263,7 @@ gsk_gl_render_job_visit_radial_gradient_node (GskGLRenderJob *job,
 
   g_assert (n_color_stops < MAX_GRADIENT_STOPS);
 
-  gsk_gl_program_begin_draw (job->driver->radial_gradient,
-                             &job->viewport,
-                             &job->projection,
-                             gsk_gl_render_job_get_modelview_matrix (job),
-                             gsk_gl_render_job_get_clip (job),
-                             job->alpha);
+  gsk_gl_render_job_begin_draw (job, job->driver->radial_gradient);
   gsk_gl_program_set_uniform1i (job->driver->radial_gradient,
                                 UNIFORM_RADIAL_GRADIENT_NUM_COLOR_STOPS,
                                 n_color_stops);
@@ -1353,12 +1337,7 @@ gsk_gl_render_job_visit_clipped_child (GskGLRenderJob        *job,
 
       g_assert (offscreen.texture_id);
 
-      gsk_gl_program_begin_draw (job->driver->blit,
-                                 &job->viewport,
-                                 &job->projection,
-                                 gsk_gl_render_job_get_modelview_matrix (job),
-                                 gsk_gl_render_job_get_clip (job),
-                                 job->alpha);
+      gsk_gl_render_job_begin_draw (job, job->driver->blit);
       gsk_gl_program_set_uniform_texture (job->driver->blit,
                                           UNIFORM_SHARED_SOURCE,
                                           GL_TEXTURE_2D,
@@ -1478,12 +1457,7 @@ gsk_gl_render_job_visit_rounded_clip_node (GskGLRenderJob *job,
 
       g_assert (offscreen.texture_id);
 
-      gsk_gl_program_begin_draw (job->driver->blit,
-                                 &job->viewport,
-                                 &job->projection,
-                                 gsk_gl_render_job_get_modelview_matrix (job),
-                                 gsk_gl_render_job_get_clip (job),
-                                 job->alpha);
+      gsk_gl_render_job_begin_draw (job, job->driver->blit);
       gsk_gl_program_set_uniform_texture (job->driver->blit,
                                           UNIFORM_SHARED_SOURCE,
                                           GL_TEXTURE_2D,
@@ -1536,12 +1510,7 @@ gsk_gl_render_job_visit_uniform_border_node (GskGLRenderJob *job,
 
   gsk_gl_render_job_transform_rounded_rect (job, rounded_outline, &outline);
 
-  gsk_gl_program_begin_draw (job->driver->inset_shadow,
-                             &job->viewport,
-                             &job->projection,
-                             gsk_gl_render_job_get_modelview_matrix (job),
-                             gsk_gl_render_job_get_clip (job),
-                             job->alpha);
+  gsk_gl_render_job_begin_draw (job, job->driver->inset_shadow);
   gsk_gl_program_set_uniform_rounded_rect (job->driver->inset_shadow,
                                            UNIFORM_INSET_SHADOW_OUTLINE_RECT,
                                            &outline);
@@ -1681,20 +1650,18 @@ gsk_gl_render_job_visit_border_node (GskGLRenderJob *job,
 
     for (guint i = 0; i < 4; i++)
       {
+        GskGLDrawVertex *vertices;
+
         if (widths[indices[i]] <= 0)
           continue;
 
-        gsk_gl_program_begin_draw (job->driver->border,
-                                   &job->viewport,
-                                   &job->projection,
-                                   gsk_gl_render_job_get_modelview_matrix (job),
-                                   gsk_gl_render_job_get_clip (job),
-                                   job->alpha);
+        gsk_gl_render_job_begin_draw (job, job->driver->border);
         gsk_gl_program_set_uniform4fv (job->driver->border,
                                        UNIFORM_BORDER_COLOR,
                                        1,
                                        (const float *)&colors[indices[i]]);
-        gsk_gl_command_queue_add_vertices (job->command_queue, side_data[indices[i]]);
+        vertices = gsk_gl_command_queue_add_vertices (job->command_queue);
+        memcpy (vertices, side_data[indices[i]], sizeof (GskGLDrawVertex) * GSK_GL_N_VERTICES);
         gsk_gl_program_end_draw (job->driver->border);
       }
   }
@@ -1794,12 +1761,7 @@ gsk_gl_render_job_visit_transform_node (GskGLRenderJob *job,
                */
               gsk_gl_render_job_push_modelview (job, transform);
 
-              gsk_gl_program_begin_draw (job->driver->blit,
-                                         &job->viewport,
-                                         &job->projection,
-                                         gsk_gl_render_job_get_modelview_matrix (job),
-                                         gsk_gl_render_job_get_clip (job),
-                                         job->alpha);
+              gsk_gl_render_job_begin_draw (job, job->driver->blit);
               gsk_gl_program_set_uniform_texture (job->driver->blit,
                                                   UNIFORM_SHARED_SOURCE,
                                                   GL_TEXTURE_2D,
@@ -1827,12 +1789,7 @@ gsk_gl_render_job_visit_unblurred_inset_shadow_node (GskGLRenderJob *job,
 
   gsk_gl_render_job_transform_rounded_rect (job, outline, &transformed_outline);
 
-  gsk_gl_program_begin_draw (job->driver->inset_shadow,
-                             &job->viewport,
-                             &job->projection,
-                             gsk_gl_render_job_get_modelview_matrix (job),
-                             gsk_gl_render_job_get_clip (job),
-                             job->alpha);
+  gsk_gl_render_job_begin_draw (job, job->driver->inset_shadow);
   gsk_gl_program_set_uniform_rounded_rect (job->driver->inset_shadow,
                                            UNIFORM_INSET_SHADOW_OUTLINE_RECT,
                                            &transformed_outline);
@@ -1940,12 +1897,7 @@ gsk_gl_render_job_visit_blurred_inset_shadow_node (GskGLRenderJob *job,
       gsk_gl_render_job_transform_rounded_rect (job, &outline_to_blur, &transformed_outline);
 
       /* Actual inset shadow outline drawing */
-      gsk_gl_program_begin_draw (job->driver->inset_shadow,
-                                 &job->viewport,
-                                 &job->projection,
-                                 gsk_gl_render_job_get_modelview_matrix (job),
-                                 gsk_gl_render_job_get_clip (job),
-                                 job->alpha);
+      gsk_gl_render_job_begin_draw (job, job->driver->inset_shadow);
       gsk_gl_program_set_uniform_rounded_rect (job->driver->inset_shadow,
                                                UNIFORM_INSET_SHADOW_OUTLINE_RECT,
                                                &transformed_outline);
@@ -2014,12 +1966,7 @@ gsk_gl_render_job_visit_blurred_inset_shadow_node (GskGLRenderJob *job,
     offscreen.area.x2 = tx2;
     offscreen.area.y2 = ty2;
 
-    gsk_gl_program_begin_draw (job->driver->blit,
-                               &job->viewport,
-                               &job->projection,
-                               gsk_gl_render_job_get_modelview_matrix (job),
-                               gsk_gl_render_job_get_clip (job),
-                               job->alpha);
+    gsk_gl_render_job_begin_draw (job, job->driver->blit);
     gsk_gl_program_set_uniform_texture (job->driver->blit,
                                         UNIFORM_SHARED_SOURCE,
                                         GL_TEXTURE_2D,
@@ -2058,12 +2005,7 @@ gsk_gl_render_job_visit_unblurred_outset_shadow_node (GskGLRenderJob *job,
 
   gsk_gl_render_job_transform_rounded_rect (job, outline, &transformed_outline);
 
-  gsk_gl_program_begin_draw (job->driver->unblurred_outset_shadow,
-                             &job->viewport,
-                             &job->projection,
-                             gsk_gl_render_job_get_modelview_matrix (job),
-                             gsk_gl_render_job_get_clip (job),
-                             job->alpha);
+  gsk_gl_render_job_begin_draw (job, job->driver->unblurred_outset_shadow);
   gsk_gl_program_set_uniform_rounded_rect (job->driver->unblurred_outset_shadow,
                                            UNIFORM_UNBLURRED_OUTSET_SHADOW_OUTLINE_RECT,
                                            &transformed_outline);
@@ -2220,12 +2162,7 @@ gsk_gl_render_job_visit_blurred_outset_shadow_node (GskGLRenderJob *job,
       gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
 
       /* Draw the outline using color program */
-      gsk_gl_program_begin_draw (job->driver->color,
-                                 &job->viewport,
-                                 &job->projection,
-                                 gsk_gl_render_job_get_modelview_matrix (job),
-                                 gsk_gl_render_job_get_clip (job),
-                                 job->alpha);
+      gsk_gl_render_job_begin_draw (job, job->driver->color);
       gsk_gl_program_set_uniform_color (job->driver->color,
                                         UNIFORM_COLOR_COLOR,
                                         &white);
@@ -2268,12 +2205,7 @@ gsk_gl_render_job_visit_blurred_outset_shadow_node (GskGLRenderJob *job,
       offscreen.texture_id = blurred_texture_id;
       init_full_texture_region (&offscreen);
 
-      gsk_gl_program_begin_draw (job->driver->outset_shadow,
-                                 &job->viewport,
-                                 &job->projection,
-                                 gsk_gl_render_job_get_modelview_matrix (job),
-                                 gsk_gl_render_job_get_clip (job),
-                                 job->alpha);
+      gsk_gl_render_job_begin_draw (job, job->driver->outset_shadow);
       gsk_gl_program_set_uniform_color (job->driver->outset_shadow,
                                         UNIFORM_OUTSET_SHADOW_COLOR,
                                         color);
@@ -2296,12 +2228,7 @@ gsk_gl_render_job_visit_blurred_outset_shadow_node (GskGLRenderJob *job,
       return;
     }
 
-  gsk_gl_program_begin_draw (job->driver->outset_shadow,
-                             &job->viewport,
-                             &job->projection,
-                             gsk_gl_render_job_get_modelview_matrix (job),
-                             gsk_gl_render_job_get_clip (job),
-                             job->alpha);
+  gsk_gl_render_job_begin_draw (job, job->driver->outset_shadow);
   gsk_gl_program_set_uniform_color (job->driver->outset_shadow,
                                     UNIFORM_OUTSET_SHADOW_COLOR,
                                     color);
@@ -2511,12 +2438,7 @@ gsk_gl_render_job_visit_cross_fade_node (GskGLRenderJob *job,
 
   g_assert (offscreen_end.texture_id);
 
-  gsk_gl_program_begin_draw (job->driver->cross_fade,
-                             &job->viewport,
-                             &job->projection,
-                             gsk_gl_render_job_get_modelview_matrix (job),
-                             gsk_gl_render_job_get_clip (job),
-                             job->alpha);
+  gsk_gl_render_job_begin_draw (job, job->driver->cross_fade);
   gsk_gl_program_set_uniform_texture (job->driver->cross_fade,
                                       UNIFORM_SHARED_SOURCE,
                                       GL_TEXTURE_2D,
@@ -2543,6 +2465,9 @@ gsk_gl_render_job_visit_opacity_node (GskGLRenderJob *job,
   float prev_alpha = job->alpha;
   float new_alpha = job->alpha * opacity;
 
+  job->alpha = new_alpha;
+  job->last_shared_state++;
+
   if (new_alpha >= ((float)0x00ff / (float)0xffff))
     {
       if (gsk_render_node_get_node_type (child) == GSK_CONTAINER_NODE)
@@ -2560,14 +2485,8 @@ gsk_gl_render_job_visit_opacity_node (GskGLRenderJob *job,
             return;
 
           g_assert (offscreen.texture_id);
-          g_assert (job->alpha == prev_alpha);
-
-          gsk_gl_program_begin_draw (job->driver->blit,
-                                     &job->viewport,
-                                     &job->projection,
-                                     gsk_gl_render_job_get_modelview_matrix (job),
-                                     gsk_gl_render_job_get_clip (job),
-                                     new_alpha);
+
+          gsk_gl_render_job_begin_draw (job, job->driver->blit);
           gsk_gl_program_set_uniform_texture (job->driver->blit,
                                               UNIFORM_SHARED_SOURCE,
                                               GL_TEXTURE_2D,
@@ -2578,11 +2497,12 @@ gsk_gl_render_job_visit_opacity_node (GskGLRenderJob *job,
         }
       else
         {
-          job->alpha = new_alpha;
           gsk_gl_render_job_visit_node (job, child);
-          job->alpha = prev_alpha;
         }
     }
+
+  job->alpha = prev_alpha;
+  job->last_shared_state++;
 }
 
 static void
@@ -2677,7 +2597,7 @@ gsk_gl_render_job_visit_text_node (GskGLRenderJob *job,
       glyph_x2 = glyph_x + glyph->ink_rect.width;
       glyph_y2 = glyph_y + glyph->ink_rect.height;
 
-      vertices = gsk_gl_command_queue_add_vertices (job->command_queue, NULL);
+      vertices = gsk_gl_command_queue_add_vertices (job->command_queue);
 
       vertices[0].position[0] = glyph_x;
       vertices[0].position[1] = glyph_y;
@@ -2793,12 +2713,7 @@ gsk_gl_render_job_visit_shadow_node (GskGLRenderJob *job,
         }
 
       gsk_gl_render_job_offset (job, dx, dy);
-      gsk_gl_program_begin_draw (job->driver->coloring,
-                                 &job->viewport,
-                                 &job->projection,
-                                 gsk_gl_render_job_get_modelview_matrix (job),
-                                 gsk_gl_render_job_get_clip (job),
-                                 job->alpha);
+      gsk_gl_render_job_begin_draw (job, job->driver->coloring);
       gsk_gl_program_set_uniform_texture (job->driver->coloring,
                                           UNIFORM_SHARED_SOURCE,
                                           GL_TEXTURE_2D,
@@ -2855,12 +2770,7 @@ gsk_gl_render_job_visit_blur_node (GskGLRenderJob *job,
   if (cache_texture)
     gsk_next_driver_cache_texture (job->driver, &key, offscreen.texture_id);
 
-  gsk_gl_program_begin_draw (job->driver->blit,
-                             &job->viewport,
-                             &job->projection,
-                             gsk_gl_render_job_get_modelview_matrix (job),
-                             gsk_gl_render_job_get_clip (job),
-                             job->alpha);
+  gsk_gl_render_job_begin_draw (job, job->driver->blit);
   gsk_gl_program_set_uniform_texture (job->driver->blit,
                                       UNIFORM_SHARED_SOURCE,
                                       GL_TEXTURE_2D,
@@ -2899,12 +2809,7 @@ gsk_gl_render_job_visit_blend_node (GskGLRenderJob *job,
 
   if (!gsk_gl_render_job_visit_node_with_offscreen (job, top_child, &top_offscreen))
     {
-      gsk_gl_program_begin_draw (job->driver->blit,
-                                 &job->viewport,
-                                 &job->projection,
-                                 gsk_gl_render_job_get_modelview_matrix (job),
-                                 gsk_gl_render_job_get_clip (job),
-                                 job->alpha);
+      gsk_gl_render_job_begin_draw (job, job->driver->blit);
       gsk_gl_program_set_uniform_texture (job->driver->blit,
                                           UNIFORM_SHARED_SOURCE,
                                           GL_TEXTURE_2D,
@@ -2917,12 +2822,7 @@ gsk_gl_render_job_visit_blend_node (GskGLRenderJob *job,
 
   g_assert (top_offscreen.was_offscreen);
 
-  gsk_gl_program_begin_draw (job->driver->blend,
-                             &job->viewport,
-                             &job->projection,
-                             gsk_gl_render_job_get_modelview_matrix (job),
-                             gsk_gl_render_job_get_clip (job),
-                             job->alpha);
+  gsk_gl_render_job_begin_draw (job, job->driver->blend);
   gsk_gl_program_set_uniform_texture (job->driver->blend,
                                       UNIFORM_SHARED_SOURCE,
                                       GL_TEXTURE_2D,
@@ -2961,12 +2861,7 @@ gsk_gl_render_job_visit_color_matrix_node (GskGLRenderJob *job,
 
   graphene_vec4_to_float (gsk_color_matrix_node_get_color_offset (node), offset);
 
-  gsk_gl_program_begin_draw (job->driver->color_matrix,
-                             &job->viewport,
-                             &job->projection,
-                             gsk_gl_render_job_get_modelview_matrix (job),
-                             gsk_gl_render_job_get_clip (job),
-                             job->alpha);
+  gsk_gl_render_job_begin_draw (job, job->driver->color_matrix);
   gsk_gl_program_set_uniform_texture (job->driver->color_matrix,
                                       UNIFORM_SHARED_SOURCE,
                                       GL_TEXTURE_2D,
@@ -2989,12 +2884,7 @@ gsk_gl_render_job_visit_gl_shader_node_fallback (GskGLRenderJob *job,
 {
   static const GdkRGBA pink = { 255 / 255., 105 / 255., 180 / 255., 1.0 };
 
-  gsk_gl_program_begin_draw (job->driver->color,
-                             &job->viewport,
-                             &job->projection,
-                             gsk_gl_render_job_get_modelview_matrix (job),
-                             gsk_gl_render_job_get_clip (job),
-                             job->alpha);
+  gsk_gl_render_job_begin_draw (job, job->driver->color);
   gsk_gl_program_set_uniform_color (job->driver->color,
                                     UNIFORM_COLOR_COLOR,
                                     &pink);
@@ -3222,7 +3112,7 @@ gsk_gl_render_job_visit_texture_node (GskGLRenderJob *job,
                                               GL_TEXTURE_2D,
                                               GL_TEXTURE0,
                                               slice->texture_id);
-          vertices = gsk_gl_command_queue_add_vertices (job->command_queue, NULL);
+          vertices = gsk_gl_command_queue_add_vertices (job->command_queue);
 
           vertices[0].position[0] = x1;
           vertices[0].position[1] = y1;
@@ -3611,6 +3501,8 @@ gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob       *job,
   job->offset_y = 0;
   job->alpha = 1.0f;
 
+  job->last_shared_state++;
+
   gsk_gl_render_job_visit_node (job, node);
 
   if (offscreen->reset_clip)
@@ -3831,3 +3723,31 @@ gsk_gl_render_job_free (GskGLRenderJob *job)
   g_clear_pointer (&job->clip, g_array_unref);
   g_slice_free (GskGLRenderJob, job);
 }
+
+static void
+gsk_gl_render_job_begin_draw (GskGLRenderJob *job,
+                              GskGLProgram   *program)
+{
+  /* If the program is up to our shared state value,
+   * then we can ignore callign gsk_gl_program_begin_draw()
+   * with the initial values saving a bunch of compares.
+   *
+   * Last checked this saves about 25% of the compares.
+   */
+  if G_LIKELY (program->last_shared_state == job->last_shared_state)
+    {
+      gsk_gl_command_queue_begin_draw (job->command_queue,
+                                       program->id,
+                                       &job->viewport);
+    }
+  else
+    {
+      gsk_gl_program_begin_draw (program,
+                                 &job->viewport,
+                                 &job->projection,
+                                 gsk_gl_render_job_get_modelview_matrix (job),
+                                 gsk_gl_render_job_get_clip (job),
+                                 job->alpha);
+      program->last_shared_state = job->last_shared_state;
+    }
+}


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