[gtk/wip/chergert/glproto: 496/526] reduce allocations and lookups in uniform state




commit 6e823cbd47231c49ba94c4f6d2d04db4b6bdcaf9
Author: Christian Hergert <chergert redhat com>
Date:   Wed Feb 10 14:10:52 2021 -0800

    reduce allocations and lookups in uniform state
    
    this makes programs have an "uniforms added" function which is used to
    cache information about accesses into the program. doing so allows us to
    avoid some allocations during frames as well as keeping informaion in cachelines
    close to where we're using them.
    
    also, it lets us avoid going through arrays which cost extra pointer dereferences.

 gsk/next/gskglcommandqueue.c        |  18 +-
 gsk/next/gskglcommandqueueprivate.h | 202 +----------------
 gsk/next/gskgldriver.c              |  12 +-
 gsk/next/gskglprogram.c             |  83 ++++---
 gsk/next/gskglprogramprivate.h      | 187 +++++++--------
 gsk/next/gskglrenderjob.c           |  58 ++---
 gsk/next/gskgltypesprivate.h        |   2 +
 gsk/next/gskgluniformstate.c        | 440 +++++++++++++++---------------------
 gsk/next/gskgluniformstateprivate.h | 258 +++++++++++----------
 9 files changed, 522 insertions(+), 738 deletions(-)
---
diff --git a/gsk/next/gskglcommandqueue.c b/gsk/next/gskglcommandqueue.c
index 30bff3ef68..a17ca72adc 100644
--- a/gsk/next/gskglcommandqueue.c
+++ b/gsk/next/gskglcommandqueue.c
@@ -344,7 +344,7 @@ discard_batch (GskGLCommandQueue *self)
 
 void
 gsk_gl_command_queue_begin_draw (GskGLCommandQueue     *self,
-                                 guint                  program,
+                                 GskGLUniformProgram   *program,
                                  const graphene_rect_t *viewport)
 {
   GskGLCommandBatch *batch;
@@ -353,9 +353,11 @@ gsk_gl_command_queue_begin_draw (GskGLCommandQueue     *self,
   g_assert (self->in_draw == FALSE);
   g_assert (viewport != NULL);
 
+  self->program_info = program;
+
   batch = begin_next_batch (self);
   batch->any.kind = GSK_GL_COMMAND_KIND_DRAW;
-  batch->any.program = program;
+  batch->any.program = program->program_id;
   batch->any.next_batch_index = -1;
   batch->any.viewport.width = viewport->size.width;
   batch->any.viewport.height = viewport->size.height;
@@ -417,7 +419,7 @@ gsk_gl_command_queue_end_draw (GskGLCommandQueue *self)
    * array to be large enough for all our changes. Then we just ++
    * from the callback.
    */
-  n_changed = gsk_gl_uniform_state_get_n_changed (self->uniforms, batch->any.program);
+  n_changed = self->program_info->n_changed;
 
   if (n_changed)
     {
@@ -427,7 +429,7 @@ gsk_gl_command_queue_end_draw (GskGLCommandQueue *self)
       /* Store information about uniforms that changed */
       batch->draw.uniform_offset = self->batch_uniforms->len;
       gsk_gl_uniform_state_snapshot (self->uniforms,
-                                     batch->any.program,
+                                     self->program_info,
                                      gsk_gl_command_queue_uniform_snapshot_cb,
                                      self->batch_uniforms);
       batch->draw.uniform_count = n_changed;
@@ -493,6 +495,7 @@ gsk_gl_command_queue_end_draw (GskGLCommandQueue *self)
     }
 
   self->in_draw = FALSE;
+  self->program_info = NULL;
 }
 
 /**
@@ -513,19 +516,19 @@ void
 gsk_gl_command_queue_split_draw (GskGLCommandQueue *self)
 {
   GskGLCommandBatch *batch;
+  GskGLUniformProgram *program;
   graphene_rect_t viewport;
-  guint program;
 
   g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
   g_assert (self->batches->len > 0);
   g_assert (self->in_draw == TRUE);
 
+  program = self->program_info;
+
   batch = &g_array_index (self->batches, GskGLCommandBatch, self->batches->len-1);
 
   g_assert (batch->any.kind == GSK_GL_COMMAND_KIND_DRAW);
 
-  program = batch->any.program;
-
   viewport.origin.x = 0;
   viewport.origin.y = 0;
   viewport.size.width = batch->any.viewport.width;
@@ -628,7 +631,6 @@ gsk_gl_command_queue_delete_program (GskGLCommandQueue *self,
   g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
 
   glDeleteProgram (program);
-  gsk_gl_uniform_state_clear_program (self->uniforms, program);
 }
 
 static inline void
diff --git a/gsk/next/gskglcommandqueueprivate.h b/gsk/next/gskglcommandqueueprivate.h
index 21cd84d0e1..b073556d6f 100644
--- a/gsk/next/gskglcommandqueueprivate.h
+++ b/gsk/next/gskglcommandqueueprivate.h
@@ -198,6 +198,11 @@ struct _GskGLCommandQueue
    */
   GskGLUniformState *uniforms;
 
+  /* Current program if we are in a draw so that we can send commands
+   * to the uniform state as needed.
+   */
+  GskGLUniformProgram *program_info;
+
   /* The profiler instance to deliver timing/etc data */
   GskProfiler *profiler;
   GskGLProfiler *gl_profiler;
@@ -299,7 +304,7 @@ void               gsk_gl_command_queue_push_debug_group     (GskGLCommandQueue
                                                               const char               *message);
 void               gsk_gl_command_queue_pop_debug_group      (GskGLCommandQueue        *self);
 void               gsk_gl_command_queue_begin_draw           (GskGLCommandQueue        *self,
-                                                              guint                     program,
+                                                              GskGLUniformProgram      *program_info,
                                                               const graphene_rect_t    *viewport);
 void               gsk_gl_command_queue_end_draw             (GskGLCommandQueue        *self);
 void               gsk_gl_command_queue_split_draw           (GskGLCommandQueue        *self);
@@ -323,201 +328,6 @@ gsk_gl_command_queue_bind_framebuffer (GskGLCommandQueue *self,
   gsk_gl_attachment_state_bind_framebuffer (self->attachments, framebuffer);
 }
 
-static inline void
-gsk_gl_command_queue_set_uniform1ui (GskGLCommandQueue *self,
-                                     guint              program,
-                                     guint              location,
-                                     int                value0)
-{
-  gsk_gl_uniform_state_set1ui (self->uniforms, program, location, value0);
-}
-
-static inline void
-gsk_gl_command_queue_set_uniform1i (GskGLCommandQueue *self,
-                                    guint              program,
-                                    guint              location,
-                                    int                value0)
-{
-  gsk_gl_uniform_state_set1i (self->uniforms, program, location, value0);
-}
-
-static inline void
-gsk_gl_command_queue_set_uniform2i (GskGLCommandQueue *self,
-                                    guint              program,
-                                    guint              location,
-                                    int                value0,
-                                    int                value1)
-{
-  gsk_gl_uniform_state_set2i (self->uniforms, program, location, value0, value1);
-}
-
-static inline void
-gsk_gl_command_queue_set_uniform3i (GskGLCommandQueue *self,
-                                    guint              program,
-                                    guint              location,
-                                    int                value0,
-                                    int                value1,
-                                    int                value2)
-{
-  gsk_gl_uniform_state_set3i (self->uniforms, program, location, value0, value1, value2);
-}
-
-static inline void
-gsk_gl_command_queue_set_uniform4i (GskGLCommandQueue *self,
-                                    guint              program,
-                                    guint              location,
-                                    int                value0,
-                                    int                value1,
-                                    int                value2,
-                                    int                value3)
-{
-  gsk_gl_uniform_state_set4i (self->uniforms, program, location, value0, value1, value2, value3);
-}
-
-static inline void
-gsk_gl_command_queue_set_uniform1f (GskGLCommandQueue *self,
-                                    guint              program,
-                                    guint              location,
-                                    float              value0)
-{
-  gsk_gl_uniform_state_set1f (self->uniforms, program, location, value0);
-}
-
-static inline void
-gsk_gl_command_queue_set_uniform2f (GskGLCommandQueue *self,
-                                    guint              program,
-                                    guint              location,
-                                    float              value0,
-                                    float              value1)
-{
-  gsk_gl_uniform_state_set2f (self->uniforms, program, location, value0, value1);
-}
-
-static inline void
-gsk_gl_command_queue_set_uniform3f (GskGLCommandQueue *self,
-                                    guint              program,
-                                    guint              location,
-                                    float              value0,
-                                    float              value1,
-                                    float              value2)
-{
-  gsk_gl_uniform_state_set3f (self->uniforms, program, location, value0, value1, value2);
-}
-
-static inline void
-gsk_gl_command_queue_set_uniform4f (GskGLCommandQueue *self,
-                                    guint              program,
-                                    guint              location,
-                                    float              value0,
-                                    float              value1,
-                                    float              value2,
-                                    float              value3)
-{
-  gsk_gl_uniform_state_set4f (self->uniforms, program, location, value0, value1, value2, value3);
-}
-
-static inline void
-gsk_gl_command_queue_set_uniform1fv (GskGLCommandQueue *self,
-                                     guint              program,
-                                     guint              location,
-                                     gsize              count,
-                                     const float       *value)
-{
-  gsk_gl_uniform_state_set1fv (self->uniforms, program, location, count, value);
-}
-
-static inline void
-gsk_gl_command_queue_set_uniform2fv (GskGLCommandQueue *self,
-                                     guint              program,
-                                     guint              location,
-                                     gsize              count,
-                                     const float       *value)
-{
-  gsk_gl_uniform_state_set2fv (self->uniforms, program, location, count, value);
-}
-
-static inline void
-gsk_gl_command_queue_set_uniform3fv (GskGLCommandQueue *self,
-                                     guint              program,
-                                     guint              location,
-                                     gsize              count,
-                                     const float       *value)
-{
-  gsk_gl_uniform_state_set3fv (self->uniforms, program, location, count, value);
-}
-
-static inline void
-gsk_gl_command_queue_set_uniform4fv (GskGLCommandQueue *self,
-                                     guint              program,
-                                     guint              location,
-                                     gsize              count,
-                                     const float       *value)
-{
-  gsk_gl_uniform_state_set4fv (self->uniforms, program, location, count, value);
-}
-
-static inline void
-gsk_gl_command_queue_set_uniform_matrix (GskGLCommandQueue       *self,
-                                         guint                    program,
-                                         guint                    location,
-                                         const graphene_matrix_t *matrix)
-{
-  gsk_gl_uniform_state_set_matrix (self->uniforms, program, location, matrix);
-}
-
-static inline void
-gsk_gl_command_queue_set_uniform_color (GskGLCommandQueue *self,
-                                        guint              program,
-                                        guint              location,
-                                        const GdkRGBA     *color)
-{
-  gsk_gl_uniform_state_set_color (self->uniforms, program, location, color);
-}
-
-/**
- * gsk_gl_command_queue_set_uniform_texture:
- * @self: A #GskGLCommandQueue
- * @program: the program id
- * @location: the location of the uniform
- * @texture_target: a texture target such as %GL_TEXTURE_2D
- * @texture_slot: the texture slot such as %GL_TEXTURE0 or %GL_TEXTURE1
- * @texture_id: the id of the texture from glGenTextures()
- *
- * This sets the value of a uniform to map to @texture_slot (after subtracting
- * GL_TEXTURE0 from the value) and ensures that @texture_id is available in the
- * same texturing slot, ensuring @texture_target.
- */
-static inline void
-gsk_gl_command_queue_set_uniform_texture (GskGLCommandQueue *self,
-                                          guint              program,
-                                          guint              location,
-                                          GLenum             texture_target,
-                                          GLenum             texture_slot,
-                                          guint              texture_id)
-{
-  gsk_gl_attachment_state_bind_texture (self->attachments, texture_target, texture_slot, texture_id);
-  gsk_gl_uniform_state_set_texture (self->uniforms, program, location, texture_slot);
-}
-
-/**
- * gsk_gl_command_queue_set_uniform_rounded_rect:
- * @self: a #GskGLCommandQueue
- * @program: the program to execute
- * @location: the location of the uniform
- * @rounded_rect: the rounded rect to apply
- *
- * Sets a uniform that is expecting a rounded rect. This is stored as a
- * 4fv using glUniform4fv() when uniforms are applied to the progrma.
- */
-static inline void
-gsk_gl_command_queue_set_uniform_rounded_rect (GskGLCommandQueue    *self,
-                                               guint                 program,
-                                               guint                 location,
-                                               const GskRoundedRect *rounded_rect)
-{
-  gsk_gl_uniform_state_set_rounded_rect (self->uniforms, program, location, rounded_rect);
-}
-
 G_END_DECLS
 
 #endif /* __GSK_GL_COMMAND_QUEUE_PRIVATE_H__ */
diff --git a/gsk/next/gskgldriver.c b/gsk/next/gskgldriver.c
index ac49620939..bd60570393 100644
--- a/gsk/next/gskgldriver.c
+++ b/gsk/next/gskgldriver.c
@@ -380,6 +380,8 @@ gsk_next_driver_load_programs (GskNextDriver  *self,
                                                                                                 \
     uniforms                                                                                    \
                                                                                                 \
+    gsk_gl_program_uniforms_added (program);                                                    \
+                                                                                                \
     if (have_alpha)                                                                             \
       gsk_gl_program_set_uniform1f (program, UNIFORM_SHARED_ALPHA, 1.0f);                       \
                                                                                                 \
@@ -1101,13 +1103,14 @@ gsk_next_driver_lookup_shader (GskNextDriver  *self,
 
       if ((program = gsk_gl_compiler_compile (compiler, NULL, error)))
         {
+          gboolean have_alpha;
+
           gsk_gl_program_add_uniform (program, "u_source", UNIFORM_SHARED_SOURCE);
           gsk_gl_program_add_uniform (program, "u_clip_rect", UNIFORM_SHARED_CLIP_RECT);
           gsk_gl_program_add_uniform (program, "u_viewport", UNIFORM_SHARED_VIEWPORT);
           gsk_gl_program_add_uniform (program, "u_projection", UNIFORM_SHARED_PROJECTION);
           gsk_gl_program_add_uniform (program, "u_modelview", UNIFORM_SHARED_MODELVIEW);
-          if (gsk_gl_program_add_uniform (program, "u_alpha", UNIFORM_SHARED_ALPHA))
-            gsk_gl_program_set_uniform1f (program, UNIFORM_SHARED_ALPHA, 1.0f);
+          have_alpha = gsk_gl_program_add_uniform (program, "u_alpha", UNIFORM_SHARED_ALPHA);
 
           gsk_gl_program_add_uniform (program, "u_size", UNIFORM_CUSTOM_SIZE);
           gsk_gl_program_add_uniform (program, "u_texture1", UNIFORM_CUSTOM_TEXTURE1);
@@ -1127,6 +1130,11 @@ gsk_next_driver_lookup_shader (GskNextDriver  *self,
           for (guint i = n_uniforms; i < G_N_ELEMENTS (program->args_locations); i++)
             program->args_locations[i] = -1;
 
+          gsk_gl_program_uniforms_added (program);
+
+          if (have_alpha)
+            gsk_gl_program_set_uniform1f (program, UNIFORM_SHARED_ALPHA, 1.0f);
+
           g_hash_table_insert (self->shader_cache, shader, program);
           g_object_weak_ref (G_OBJECT (shader),
                              gsk_next_driver_shader_weak_cb,
diff --git a/gsk/next/gskglprogram.c b/gsk/next/gskglprogram.c
index 2a4339693b..23dad322c3 100644
--- a/gsk/next/gskglprogram.c
+++ b/gsk/next/gskglprogram.c
@@ -40,6 +40,7 @@ gsk_gl_program_new (GskNextDriver *driver,
   self->id = program_id;
   self->name = g_strdup (name);
   self->driver = g_object_ref (driver);
+  self->n_uniforms = 0;
 
   return self;
 }
@@ -120,6 +121,9 @@ gsk_gl_program_add_uniform (GskGLProgram *self,
 
   self->uniform_locations[key] = location;
 
+  if (location >= self->n_uniforms)
+    self->n_uniforms = location + 1;
+
 #if 0
   g_print ("program [%d] %s uniform %s at location %d.\n",
            self->id, self->name, name, location);
@@ -161,47 +165,68 @@ gsk_gl_program_begin_draw (GskGLProgram            *self,
   g_assert (modelview != NULL);
   g_assert (clip != NULL);
 
-  gsk_gl_command_queue_begin_draw (self->driver->command_queue, self->id, viewport);
+  gsk_gl_command_queue_begin_draw (self->driver->command_queue, self->program_info, viewport);
 
   if (self->uniform_locations[UNIFORM_SHARED_VIEWPORT] > -1)
-    gsk_gl_command_queue_set_uniform4fv (self->driver->command_queue,
-                                         self->id,
-                                         self->uniform_locations[UNIFORM_SHARED_VIEWPORT],
-                                         1,
-                                         (const float *)viewport);
+    gsk_gl_uniform_state_set4fv (self->uniforms,
+                                 self->program_info,
+                                 self->uniform_locations[UNIFORM_SHARED_VIEWPORT],
+                                 1,
+                                 (const float *)viewport);
 
   if (self->uniform_locations[UNIFORM_SHARED_MODELVIEW] > -1)
-    gsk_gl_command_queue_set_uniform_matrix (self->driver->command_queue,
-                                             self->id,
-                                             self->uniform_locations[UNIFORM_SHARED_MODELVIEW],
-                                             modelview);
+    gsk_gl_uniform_state_set_matrix (self->uniforms,
+                                     self->program_info,
+                                     self->uniform_locations[UNIFORM_SHARED_MODELVIEW],
+                                     modelview);
 
   if (self->uniform_locations[UNIFORM_SHARED_PROJECTION] > -1)
-    gsk_gl_command_queue_set_uniform_matrix (self->driver->command_queue,
-                                             self->id,
-                                             self->uniform_locations[UNIFORM_SHARED_PROJECTION],
-                                             projection);
+    gsk_gl_uniform_state_set_matrix (self->uniforms,
+                                     self->program_info,
+                                     self->uniform_locations[UNIFORM_SHARED_PROJECTION],
+                                     projection);
 
   if (self->uniform_locations[UNIFORM_SHARED_CLIP_RECT] > -1)
     {
       if (clip != NULL)
-        gsk_gl_command_queue_set_uniform_rounded_rect (self->driver->command_queue,
-                                                       self->id,
-                                                       self->uniform_locations[UNIFORM_SHARED_CLIP_RECT],
-                                                       clip);
+        gsk_gl_uniform_state_set_rounded_rect (self->uniforms,
+                                               self->program_info,
+                                               self->uniform_locations[UNIFORM_SHARED_CLIP_RECT],
+                                               clip);
       else
-        gsk_gl_command_queue_set_uniform_rounded_rect (self->driver->command_queue,
-                                                       self->id,
-                                                       self->uniform_locations[UNIFORM_SHARED_CLIP_RECT],
-                                                       &GSK_ROUNDED_RECT_INIT (0,
-                                                                               0,
-                                                                               viewport->size.width,
-                                                                               viewport->size.height));
+        gsk_gl_uniform_state_set_rounded_rect (self->uniforms,
+                                               self->program_info,
+                                               self->uniform_locations[UNIFORM_SHARED_CLIP_RECT],
+                                               &GSK_ROUNDED_RECT_INIT (0,
+                                                                       0,
+                                                                       viewport->size.width,
+                                                                       viewport->size.height));
     }
 
   if (self->uniform_locations[UNIFORM_SHARED_ALPHA] > -1)
-    gsk_gl_command_queue_set_uniform1f (self->driver->command_queue,
-                                        self->id,
-                                        self->uniform_locations[UNIFORM_SHARED_ALPHA],
-                                        alpha);
+    gsk_gl_uniform_state_set1f (self->uniforms,
+                                self->program_info,
+                                self->uniform_locations[UNIFORM_SHARED_ALPHA],
+                                alpha);
+}
+
+/**
+ * gsk_gl_program_uniforms_added:
+ * @self: a #GskGLProgram
+ *
+ * This function should be called after all of the uniforms ahve
+ * been added with gsk_gl_program_add_uniform().
+ *
+ * This function will setup the uniform state so that the program
+ * has fast access to the data buffers without as many lookups at
+ * runtime for comparison data.
+ */
+void
+gsk_gl_program_uniforms_added (GskGLProgram *self)
+{
+  g_return_if_fail (GSK_IS_GL_PROGRAM (self));
+  g_return_if_fail (self->uniforms == NULL);
+
+  self->uniforms = self->driver->command_queue->uniforms;
+  self->program_info = gsk_gl_uniform_state_get_program (self->uniforms, self->id, self->n_uniforms);
 }
diff --git a/gsk/next/gskglprogramprivate.h b/gsk/next/gskglprogramprivate.h
index b7ef6ec5f6..c66f6dabe2 100644
--- a/gsk/next/gskglprogramprivate.h
+++ b/gsk/next/gskglprogramprivate.h
@@ -40,6 +40,16 @@ struct _GskGLProgram
   char *name;
   GskNextDriver *driver;
 
+  /* In reality, this is the largest uniform position
+   * as returned after linking so that we can use direct
+   * indexes based on location.
+   */
+  guint n_uniforms;
+
+  /* Cached pointer to avoid lots of pointer chasing/lookups */
+  GskGLUniformState *uniforms;
+  GskGLUniformProgram *program_info;
+
   /* For custom programs */
   int texture_locations[4];
   int args_locations[8];
@@ -52,19 +62,20 @@ struct _GskGLProgram
   int uniform_locations[32];
 };
 
-GskGLProgram *gsk_gl_program_new         (GskNextDriver           *driver,
-                                          const char              *name,
-                                          int                      program_id);
-gboolean      gsk_gl_program_add_uniform (GskGLProgram            *self,
-                                          const char              *name,
-                                          guint                    key);
-void          gsk_gl_program_delete      (GskGLProgram            *self);
-void          gsk_gl_program_begin_draw  (GskGLProgram            *self,
-                                          const graphene_rect_t   *viewport,
-                                          const graphene_matrix_t *projection,
-                                          const graphene_matrix_t *modelview,
-                                          const GskRoundedRect    *clip,
-                                          float                    alpha);
+GskGLProgram *gsk_gl_program_new            (GskNextDriver           *driver,
+                                             const char              *name,
+                                             int                      program_id);
+gboolean      gsk_gl_program_add_uniform    (GskGLProgram            *self,
+                                             const char              *name,
+                                             guint                    key);
+void          gsk_gl_program_uniforms_added (GskGLProgram            *self);
+void          gsk_gl_program_delete         (GskGLProgram            *self);
+void          gsk_gl_program_begin_draw     (GskGLProgram            *self,
+                                             const graphene_rect_t   *viewport,
+                                             const graphene_matrix_t *projection,
+                                             const graphene_matrix_t *modelview,
+                                             const GskRoundedRect    *clip,
+                                             float                    alpha);
 
 static inline void
 gsk_gl_program_end_draw (GskGLProgram *self)
@@ -78,13 +89,7 @@ gsk_gl_program_split_draw (GskGLProgram *self)
   gsk_gl_command_queue_split_draw (self->driver->command_queue);
 }
 
-static inline int
-gsk_gl_program_get_uniform_location (GskGLProgram *self,
-                                     guint         key)
-{
-  g_assert (key < 32);
-  return self->uniform_locations[key];
-}
+#define gsk_gl_program_get_uniform_location(s,k) ((s)->uniform_locations[(k)])
 
 static inline void
 gsk_gl_program_set_uniform1fv (GskGLProgram *self,
@@ -92,9 +97,9 @@ gsk_gl_program_set_uniform1fv (GskGLProgram *self,
                                guint         count,
                                const float  *values)
 {
-  gsk_gl_command_queue_set_uniform1fv (self->driver->command_queue, self->id,
-                                       gsk_gl_program_get_uniform_location (self, key),
-                                       count, values);
+  gsk_gl_uniform_state_set1fv (self->uniforms, self->program_info,
+                               gsk_gl_program_get_uniform_location (self, key),
+                               count, values);
 }
 
 static inline void
@@ -103,9 +108,9 @@ gsk_gl_program_set_uniform2fv (GskGLProgram *self,
                                guint         count,
                                const float  *values)
 {
-  gsk_gl_command_queue_set_uniform2fv (self->driver->command_queue, self->id,
-                                       gsk_gl_program_get_uniform_location (self, key),
-                                       count, values);
+  gsk_gl_uniform_state_set2fv (self->uniforms, self->program_info,
+                               gsk_gl_program_get_uniform_location (self, key),
+                               count, values);
 }
 
 static inline void
@@ -114,9 +119,9 @@ gsk_gl_program_set_uniform4fv (GskGLProgram *self,
                                guint         count,
                                const float  *values)
 {
-  gsk_gl_command_queue_set_uniform4fv (self->driver->command_queue, self->id,
-                                       gsk_gl_program_get_uniform_location (self, key),
-                                       count, values);
+  gsk_gl_uniform_state_set4fv (self->uniforms, self->program_info,
+                               gsk_gl_program_get_uniform_location (self, key),
+                               count, values);
 }
 
 static inline void
@@ -124,9 +129,9 @@ gsk_gl_program_set_uniform_rounded_rect (GskGLProgram         *self,
                                          guint                 key,
                                          const GskRoundedRect *rounded_rect)
 {
-  gsk_gl_command_queue_set_uniform_rounded_rect (self->driver->command_queue, self->id,
-                                                 gsk_gl_program_get_uniform_location (self, key),
-                                                 rounded_rect);
+  gsk_gl_uniform_state_set_rounded_rect (self->uniforms, self->program_info,
+                                         gsk_gl_program_get_uniform_location (self, key),
+                                         rounded_rect);
 }
 
 static inline void
@@ -134,10 +139,10 @@ gsk_gl_program_set_uniform1i (GskGLProgram *self,
                               guint         key,
                               int           value0)
 {
-  gsk_gl_command_queue_set_uniform1i (self->driver->command_queue,
-                                      self->id,
-                                      gsk_gl_program_get_uniform_location (self, key),
-                                      value0);
+  gsk_gl_uniform_state_set1i (self->uniforms,
+                              self->program_info,
+                              gsk_gl_program_get_uniform_location (self, key),
+                              value0);
 }
 
 static inline void
@@ -146,11 +151,11 @@ gsk_gl_program_set_uniform2i (GskGLProgram *self,
                               int           value0,
                               int           value1)
 {
-  gsk_gl_command_queue_set_uniform2i (self->driver->command_queue,
-                                      self->id,
-                                      gsk_gl_program_get_uniform_location (self, key),
-                                      value0,
-                                      value1);
+  gsk_gl_uniform_state_set2i (self->uniforms,
+                              self->program_info,
+                              gsk_gl_program_get_uniform_location (self, key),
+                              value0,
+                              value1);
 }
 
 static inline void
@@ -160,12 +165,12 @@ gsk_gl_program_set_uniform3i (GskGLProgram *self,
                               int           value1,
                               int           value2)
 {
-  gsk_gl_command_queue_set_uniform3i (self->driver->command_queue,
-                                      self->id,
-                                      gsk_gl_program_get_uniform_location (self, key),
-                                      value0,
-                                      value1,
-                                      value2);
+  gsk_gl_uniform_state_set3i (self->uniforms,
+                              self->program_info,
+                              gsk_gl_program_get_uniform_location (self, key),
+                              value0,
+                              value1,
+                              value2);
 }
 
 static inline void
@@ -176,13 +181,13 @@ gsk_gl_program_set_uniform4i (GskGLProgram *self,
                               int           value2,
                               int           value3)
 {
-  gsk_gl_command_queue_set_uniform4i (self->driver->command_queue,
-                                      self->id,
-                                      gsk_gl_program_get_uniform_location (self, key),
-                                      value0,
-                                      value1,
-                                      value2,
-                                      value3);
+  gsk_gl_uniform_state_set4i (self->uniforms,
+                              self->program_info,
+                              gsk_gl_program_get_uniform_location (self, key),
+                              value0,
+                              value1,
+                              value2,
+                              value3);
 }
 
 static inline void
@@ -190,10 +195,10 @@ gsk_gl_program_set_uniform1f (GskGLProgram *self,
                               guint         key,
                               float         value0)
 {
-  gsk_gl_command_queue_set_uniform1f (self->driver->command_queue,
-                                      self->id,
-                                      gsk_gl_program_get_uniform_location (self, key),
-                                      value0);
+  gsk_gl_uniform_state_set1f (self->uniforms,
+                              self->program_info,
+                              gsk_gl_program_get_uniform_location (self, key),
+                              value0);
 }
 
 static inline void
@@ -202,11 +207,11 @@ gsk_gl_program_set_uniform2f (GskGLProgram *self,
                               float         value0,
                               float         value1)
 {
-  gsk_gl_command_queue_set_uniform2f (self->driver->command_queue,
-                                      self->id,
-                                      gsk_gl_program_get_uniform_location (self, key),
-                                      value0,
-                                      value1);
+  gsk_gl_uniform_state_set2f (self->uniforms,
+                              self->program_info,
+                              gsk_gl_program_get_uniform_location (self, key),
+                              value0,
+                              value1);
 }
 
 static inline void
@@ -216,12 +221,12 @@ gsk_gl_program_set_uniform3f (GskGLProgram *self,
                               float         value1,
                               float         value2)
 {
-  gsk_gl_command_queue_set_uniform3f (self->driver->command_queue,
-                                      self->id,
-                                      gsk_gl_program_get_uniform_location (self, key),
-                                      value0,
-                                      value1,
-                                      value2);
+  gsk_gl_uniform_state_set3f (self->uniforms,
+                              self->program_info,
+                              gsk_gl_program_get_uniform_location (self, key),
+                              value0,
+                              value1,
+                              value2);
 }
 
 static inline void
@@ -232,13 +237,13 @@ gsk_gl_program_set_uniform4f (GskGLProgram *self,
                               float         value2,
                               float         value3)
 {
-  gsk_gl_command_queue_set_uniform4f (self->driver->command_queue,
-                                      self->id,
-                                      gsk_gl_program_get_uniform_location (self, key),
-                                      value0,
-                                      value1,
-                                      value2,
-                                      value3);
+  gsk_gl_uniform_state_set4f (self->uniforms,
+                              self->program_info,
+                              gsk_gl_program_get_uniform_location (self, key),
+                              value0,
+                              value1,
+                              value2,
+                              value3);
 }
 
 static inline void
@@ -246,10 +251,10 @@ gsk_gl_program_set_uniform_color (GskGLProgram  *self,
                                   guint          key,
                                   const GdkRGBA *color)
 {
-  gsk_gl_command_queue_set_uniform_color (self->driver->command_queue,
-                                          self->id,
-                                          gsk_gl_program_get_uniform_location (self, key),
-                                          color);
+  gsk_gl_uniform_state_set_color (self->uniforms,
+                                  self->program_info,
+                                  gsk_gl_program_get_uniform_location (self, key),
+                                  color);
 }
 
 static inline void
@@ -259,12 +264,14 @@ gsk_gl_program_set_uniform_texture (GskGLProgram *self,
                                     GLenum        texture_slot,
                                     guint         texture_id)
 {
-  gsk_gl_command_queue_set_uniform_texture (self->driver->command_queue,
-                                            self->id,
-                                            gsk_gl_program_get_uniform_location (self, key),
-                                            texture_target,
-                                            texture_slot,
-                                            texture_id);
+  gsk_gl_attachment_state_bind_texture (self->driver->command_queue->attachments,
+                                        texture_target,
+                                        texture_slot,
+                                        texture_id);
+  gsk_gl_uniform_state_set_texture (self->uniforms,
+                                    self->program_info,
+                                    gsk_gl_program_get_uniform_location (self, key),
+                                    texture_slot);
 }
 
 static inline void
@@ -272,10 +279,10 @@ gsk_gl_program_set_uniform_matrix (GskGLProgram            *self,
                                    guint                    key,
                                    const graphene_matrix_t *matrix)
 {
-  gsk_gl_command_queue_set_uniform_matrix (self->driver->command_queue,
-                                           self->id,
-                                           gsk_gl_program_get_uniform_location (self, key),
-                                           matrix);
+  gsk_gl_uniform_state_set_matrix (self->uniforms,
+                                   self->program_info,
+                                   gsk_gl_program_get_uniform_location (self, key),
+                                   matrix);
 }
 
 G_END_DECLS
diff --git a/gsk/next/gskglrenderjob.c b/gsk/next/gskglrenderjob.c
index e05758d0e3..b6a91d8e90 100644
--- a/gsk/next/gskglrenderjob.c
+++ b/gsk/next/gskglrenderjob.c
@@ -202,7 +202,7 @@ gsk_gl_render_job_begin_draw (GskGLRenderJob *job,
    */
   if G_LIKELY (program->last_shared_state == job->driver->last_shared_state)
     {
-      gsk_gl_command_queue_begin_draw (job->command_queue, program->id, &job->viewport);
+      gsk_gl_command_queue_begin_draw (job->command_queue, program->program_info, &job->viewport);
     }
   else
     {
@@ -2984,45 +2984,45 @@ gsk_gl_render_job_visit_gl_shader_node (GskGLRenderJob      *job,
             case GSK_GL_UNIFORM_TYPE_NONE:
               break;
             case GSK_GL_UNIFORM_TYPE_FLOAT:
-              gsk_gl_command_queue_set_uniform1fv (job->command_queue,
-                                                   program->id,
-                                                   program->args_locations[i],
-                                                   1,
-                                                   (const float *)data);
+              gsk_gl_uniform_state_set1fv (job->command_queue->uniforms,
+                                           program->program_info,
+                                           program->args_locations[i],
+                                           1,
+                                           (const float *)data);
               break;
             case GSK_GL_UNIFORM_TYPE_INT:
-              gsk_gl_command_queue_set_uniform1i (job->command_queue,
-                                                  program->id,
-                                                  program->args_locations[i],
-                                                  *(const gint32 *)data);
+              gsk_gl_uniform_state_set1i (job->command_queue->uniforms,
+                                          program->program_info,
+                                          program->args_locations[i],
+                                          *(const gint32 *)data);
               break;
             case GSK_GL_UNIFORM_TYPE_UINT:
             case GSK_GL_UNIFORM_TYPE_BOOL:
-              gsk_gl_command_queue_set_uniform1ui (job->command_queue,
-                                                   program->id,
-                                                   program->args_locations[i],
-                                                   *(const guint32 *)data);
+              gsk_gl_uniform_state_set1ui (job->command_queue->uniforms,
+                                           program->program_info,
+                                           program->args_locations[i],
+                                           *(const guint32 *)data);
               break;
             case GSK_GL_UNIFORM_TYPE_VEC2:
-              gsk_gl_command_queue_set_uniform2fv (job->command_queue,
-                                                   program->id,
-                                                   program->args_locations[i],
-                                                   1,
-                                                   (const float *)data);
+              gsk_gl_uniform_state_set2fv (job->command_queue->uniforms,
+                                           program->program_info,
+                                           program->args_locations[i],
+                                           1,
+                                           (const float *)data);
               break;
             case GSK_GL_UNIFORM_TYPE_VEC3:
-              gsk_gl_command_queue_set_uniform3fv (job->command_queue,
-                                                   program->id,
-                                                   program->args_locations[i],
-                                                   1,
-                                                   (const float *)data);
+              gsk_gl_uniform_state_set3fv (job->command_queue->uniforms,
+                                           program->program_info,
+                                           program->args_locations[i],
+                                           1,
+                                           (const float *)data);
               break;
             case GSK_GL_UNIFORM_TYPE_VEC4:
-              gsk_gl_command_queue_set_uniform4fv (job->command_queue,
-                                                   program->id,
-                                                   program->args_locations[i],
-                                                   1,
-                                                   (const float *)data);
+              gsk_gl_uniform_state_set4fv (job->command_queue->uniforms,
+                                           program->program_info,
+                                           program->args_locations[i],
+                                           1,
+                                           (const float *)data);
               break;
             }
         }
diff --git a/gsk/next/gskgltypesprivate.h b/gsk/next/gskgltypesprivate.h
index b8571a59af..044f350850 100644
--- a/gsk/next/gskgltypesprivate.h
+++ b/gsk/next/gskgltypesprivate.h
@@ -46,6 +46,8 @@ typedef struct _GskGLTextureSlice GskGLTextureSlice;
 typedef struct _GskGLTextureAtlas GskGLTextureAtlas;
 typedef struct _GskGLTextureLibrary GskGLTextureLibrary;
 typedef struct _GskGLTextureNineSlice GskGLTextureNineSlice;
+typedef struct _GskGLUniformInfo GskGLUniformInfo;
+typedef struct _GskGLUniformProgram GskGLUniformProgram;
 typedef struct _GskGLUniformState GskGLUniformState;
 typedef struct _GskNextDriver GskNextDriver;
 
diff --git a/gsk/next/gskgluniformstate.c b/gsk/next/gskgluniformstate.c
index 9a7686f9b5..0c6c010d67 100644
--- a/gsk/next/gskgluniformstate.c
+++ b/gsk/next/gskgluniformstate.c
@@ -101,28 +101,17 @@ rounded_rect_equal (const GskRoundedRect *r1,
   return memcmp (r1, r2, sizeof *r1) == 0;
 }
 
-static void
-clear_program_info (gpointer data)
-{
-  GskGLUniformProgram *program_info = data;
-
-  g_clear_pointer (&program_info->uniform_info, g_free);
-  g_clear_pointer (&program_info->changed, g_free);
-}
-
 GskGLUniformState *
 gsk_gl_uniform_state_new (void)
 {
   GskGLUniformState *state;
 
   state = g_atomic_rc_box_new0 (GskGLUniformState);
-  state->program_info = g_array_new (FALSE, TRUE, sizeof (GskGLUniformProgram));
+  state->programs = g_hash_table_new_full (NULL, NULL, NULL, g_free);
   state->values_len = 4096;
   state->values_pos = 0;
   state->values_buf = g_malloc (4096);
 
-  g_array_set_clear_func (state->program_info, clear_program_info);
-
   return g_steal_pointer (&state);
 }
 
@@ -137,7 +126,7 @@ gsk_gl_uniform_state_finalize (gpointer data)
 {
   GskGLUniformState *state = data;
 
-  g_clear_pointer (&state->program_info, g_array_unref);
+  g_clear_pointer (&state->programs, g_hash_table_unref);
   g_clear_pointer (&state->values_buf, g_free);
 }
 
@@ -148,45 +137,20 @@ gsk_gl_uniform_state_unref (GskGLUniformState *state)
 }
 
 static inline void
-program_changed (GskGLUniformState *state,
-                 GskGLUniformInfo  *info,
-                 guint              program,
-                 guint              location)
+program_changed (GskGLUniformState   *state,
+                 GskGLUniformInfo    *info,
+                 GskGLUniformProgram *program,
+                 guint                location)
 {
   if (info->changed == FALSE)
     {
-      GskGLUniformProgram *program_info = &g_array_index (state->program_info, GskGLUniformProgram, program);
-
-      g_assert (program < state->program_info->len);
-
       info->changed = TRUE;
       info->initial = FALSE;
 
-      program_info->changed[program_info->n_changed++] = location;
+      program->changed[program->n_changed++] = location;
     }
 }
 
-void
-gsk_gl_uniform_state_clear_program (GskGLUniformState *state,
-                                    guint              program)
-{
-  GskGLUniformProgram *program_info;
-
-  g_return_if_fail (state != NULL);
-
-  if (program == 0 || program >= state->program_info->len)
-    return;
-
-  program_info = &g_array_index (state->program_info, GskGLUniformProgram, program);
-
-  g_clear_pointer (&program_info->uniform_info, g_free);
-  program_info->uniform_info_len = 0;
-
-  program_info->n_changed = 0;
-  program_info->changed_len = 0;
-  g_clear_pointer (&program_info->changed, g_free);
-}
-
 static inline guint
 alloc_alignment (guint current_pos,
                  guint size)
@@ -221,169 +185,109 @@ alloc_uniform_data (GskGLUniformState *state,
 }
 
 static gpointer
-create_uniform (GskGLUniformState   *state,
-                guint                program,
-                GskGLUniformFormat   format,
-                guint                array_count,
-                guint                location,
-                GskGLUniformInfo   **infoptr)
+create_uniform (GskGLUniformState    *state,
+                GskGLUniformProgram  *program,
+                GskGLUniformFormat    format,
+                guint                 array_count,
+                guint                 location,
+                GskGLUniformInfo    **infoptr)
 {
-  GskGLUniformProgram *program_info;
   GskGLUniformInfo *info;
   guint offset;
 
   g_assert (state != NULL);
-  g_assert (program > 0);
   g_assert (array_count < 256);
   g_assert ((int)format >= 0 && format < GSK_GL_UNIFORM_FORMAT_LAST);
   g_assert (format > 0);
+  g_assert (program != NULL);
   g_assert (location < GL_MAX_UNIFORM_LOCATIONS || location == (guint)-1);
 
   /* Handle unused uniforms gracefully */
   if G_UNLIKELY (location == (guint)-1)
     return NULL;
 
-  /* Fast path for common case (state already initialized) */
-  if G_LIKELY (program < state->program_info->len &&
-               (program_info = &g_array_index (state->program_info, GskGLUniformProgram, program)) &&
-               location < program_info->uniform_info_len)
-    {
-      info = &program_info->uniform_info[location];
-
-      if G_LIKELY (format == info->format)
-        {
-          if G_LIKELY (array_count <= info->array_count)
-            {
-              *infoptr = info;
-              return state->values_buf + info->offset;
-            }
+  info = &program->uniforms[location];
 
-          /* We found the uniform, but there is not enough space for the
-           * amount that was requested. Instead, allocate new space and
-           * set the value to "initial" so that the caller just writes
-           * over the previous value.
-           *
-           * This can happen when using dynamic array lengths like the
-           * "n_color_stops" in gradient shaders.
-           */
-          goto setup_info;
-        }
-      else if (info->format == 0)
-        {
-          goto setup_info;
-        }
-      else
+  if G_LIKELY (format == info->format)
+    {
+      if G_LIKELY (array_count <= info->array_count)
         {
-          g_critical ("Attempt to access uniform with different type of value "
-                      "than it was initialized with. Program %u Location %u. "
-                      "Was %d now %d (array length %d now %d).",
-                      program, location, info->format, format,
-                      info->array_count, array_count);
-          *infoptr = NULL;
-          return NULL;
+          *infoptr = info;
+          return state->values_buf + info->offset;
         }
-    }
 
-setup_info:
-
-  if (program >= state->program_info->len ||
-      g_array_index (state->program_info, GskGLUniformProgram, program).uniform_info == NULL)
+      /* We found the uniform, but there is not enough space for the
+       * amount that was requested. Instead, allocate new space and
+       * set the value to "initial" so that the caller just writes
+       * over the previous value.
+       *
+       * This can happen when using dynamic array lengths like the
+       * "n_color_stops" in gradient shaders.
+       */
+      goto setup_info;
+    }
+  else if (info->format == 0)
     {
-      if (program >= state->program_info->len)
-        g_array_set_size (state->program_info, program + 1);
-
-      program_info = &g_array_index (state->program_info, GskGLUniformProgram, program);
-      program_info->uniform_info = g_new0 (GskGLUniformInfo, location + 1);
-      program_info->uniform_info_len = location + 1;
-      program_info->changed = NULL;
-      program_info->n_changed = 0;
-      program_info->changed_len = 0;
-
-      for (guint i = 0; i < program_info->uniform_info_len; i++)
-        program_info->uniform_info[i].initial = TRUE;
+      goto setup_info;
     }
-
-  g_assert (program_info != NULL);
-  g_assert (program_info->uniform_info != NULL);
-
-  if (location >= program_info->uniform_info_len)
+  else
     {
-      guint prev = program_info->uniform_info_len;
-
-      program_info->uniform_info = g_realloc_n (program_info->uniform_info, location + 1, sizeof 
(GskGLUniformInfo));
-      program_info->uniform_info_len = location + 1;
-
-      memset (&program_info->uniform_info[prev], 0,
-              (program_info->uniform_info_len - prev) * sizeof (GskGLUniformInfo));
-      for (guint i = prev; i < program_info->uniform_info_len; i++)
-        program_info->uniform_info[i].initial = TRUE;
+      g_critical ("Attempt to access uniform with different type of value "
+                  "than it was initialized with. Program %u Location %u. "
+                  "Was %d now %d (array length %d now %d).",
+                  program->program_id, location, info->format, format,
+                  info->array_count, array_count);
+      *infoptr = NULL;
+      return NULL;
     }
 
+setup_info:
+
   alloc_uniform_data (state,
                       uniform_sizes[format] * MAX (1, array_count),
                       &offset);
 
-  info = &program_info->uniform_info[location];
   info->changed = FALSE;
   info->format = format;
   info->offset = offset;
   info->array_count = array_count;
   info->initial = TRUE;
 
-  /* Ensure we have enough space in program_info->changed to hold
-   * values up to this location so that we never have to allocate
-   * when marking a program changed.
-   */
-  if (location >= program_info->changed_len)
-    {
-      program_info->changed_len = location + 1;
-      program_info->changed = g_realloc_n (program_info->changed, program_info->changed_len, sizeof (guint));
-    }
-
   *infoptr = info;
 
   return state->values_buf + offset;
 }
 
 static inline gpointer
-get_uniform (GskGLUniformState   *state,
-             guint                program,
-             GskGLUniformFormat   format,
-             guint                array_count,
-             guint                location,
-             GskGLUniformInfo   **infoptr)
+get_uniform (GskGLUniformState    *state,
+             GskGLUniformProgram  *program,
+             GskGLUniformFormat    format,
+             guint                 array_count,
+             guint                 location,
+             GskGLUniformInfo    **infoptr)
 {
-  /* Fast path for common case (state already initialized) */
-  if G_LIKELY (program < state->program_info->len)
-    {
-      const GskGLUniformProgram *program_info = &g_array_index (state->program_info, GskGLUniformProgram, 
program);
-
-      if G_LIKELY (location < program_info->uniform_info_len)
-        {
-          GskGLUniformInfo *info = &program_info->uniform_info[location];
+  GskGLUniformInfo *info = &program->uniforms[location];
 
-          if G_LIKELY (format == info->format && array_count <= info->array_count)
-            {
-              *infoptr = info;
-              return state->values_buf + info->offset;
-            }
-        }
+  if G_LIKELY (format == info->format && array_count <= info->array_count)
+    {
+      *infoptr = info;
+      return state->values_buf + info->offset;
     }
 
   return create_uniform (state, program, format, array_count, location, infoptr);
 }
 
 void
-gsk_gl_uniform_state_set1f (GskGLUniformState *state,
-                            guint              program,
-                            guint              location,
-                            float              value0)
+gsk_gl_uniform_state_set1f (GskGLUniformState   *state,
+                            GskGLUniformProgram *program,
+                            guint                location,
+                            float                value0)
 {
   Uniform1f *u;
   GskGLUniformInfo *info;
 
   g_assert (state != NULL);
-  g_assert (program > 0);
+  g_assert (program != 0);
 
   if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_1F, 1, location, &info)))
     {
@@ -397,17 +301,17 @@ gsk_gl_uniform_state_set1f (GskGLUniformState *state,
 }
 
 void
-gsk_gl_uniform_state_set2f (GskGLUniformState *state,
-                            guint              program,
-                            guint              location,
-                            float              value0,
-                            float              value1)
+gsk_gl_uniform_state_set2f (GskGLUniformState   *state,
+                            GskGLUniformProgram *program,
+                            guint                location,
+                            float                value0,
+                            float                value1)
 {
   Uniform2f *u;
   GskGLUniformInfo *info;
 
   g_assert (state != NULL);
-  g_assert (program > 0);
+  g_assert (program != NULL);
 
   if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_2F, 1, location, &info)))
     {
@@ -422,18 +326,18 @@ gsk_gl_uniform_state_set2f (GskGLUniformState *state,
 }
 
 void
-gsk_gl_uniform_state_set3f (GskGLUniformState *state,
-                            guint              program,
-                            guint              location,
-                            float              value0,
-                            float              value1,
-                            float              value2)
+gsk_gl_uniform_state_set3f (GskGLUniformState   *state,
+                            GskGLUniformProgram *program,
+                            guint                location,
+                            float                value0,
+                            float                value1,
+                            float                value2)
 {
   Uniform3f *u;
   GskGLUniformInfo *info;
 
   g_assert (state != NULL);
-  g_assert (program > 0);
+  g_assert (program != NULL);
 
   if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_3F, 1, location, &info)))
     {
@@ -449,19 +353,19 @@ gsk_gl_uniform_state_set3f (GskGLUniformState *state,
 }
 
 void
-gsk_gl_uniform_state_set4f (GskGLUniformState *state,
-                            guint              program,
-                            guint              location,
-                            float              value0,
-                            float              value1,
-                            float              value2,
-                            float              value3)
+gsk_gl_uniform_state_set4f (GskGLUniformState   *state,
+                            GskGLUniformProgram *program,
+                            guint                location,
+                            float                value0,
+                            float                value1,
+                            float                value2,
+                            float                value3)
 {
   Uniform4f *u;
   GskGLUniformInfo *info;
 
   g_assert (state != NULL);
-  g_assert (program > 0);
+  g_assert (program != NULL);
 
   if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_4F, 1, location, &info)))
     {
@@ -478,16 +382,16 @@ gsk_gl_uniform_state_set4f (GskGLUniformState *state,
 }
 
 void
-gsk_gl_uniform_state_set1ui (GskGLUniformState *state,
-                             guint              program,
-                             guint              location,
-                             guint              value0)
+gsk_gl_uniform_state_set1ui (GskGLUniformState   *state,
+                             GskGLUniformProgram *program,
+                             guint                location,
+                             guint                value0)
 {
   Uniform1ui *u;
   GskGLUniformInfo *info;
 
   g_assert (state != NULL);
-  g_assert (program > 0);
+  g_assert (program != NULL);
 
   if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_1UI, 1, location, &info)))
     {
@@ -501,16 +405,16 @@ gsk_gl_uniform_state_set1ui (GskGLUniformState *state,
 }
 
 void
-gsk_gl_uniform_state_set1i (GskGLUniformState *state,
-                            guint              program,
-                            guint              location,
-                            int                value0)
+gsk_gl_uniform_state_set1i (GskGLUniformState   *state,
+                            GskGLUniformProgram *program,
+                            guint                location,
+                            int                  value0)
 {
   Uniform1i *u;
   GskGLUniformInfo *info;
 
   g_assert (state != NULL);
-  g_assert (program > 0);
+  g_assert (program != NULL);
 
   if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_1I, 1, location, &info)))
     {
@@ -524,17 +428,17 @@ gsk_gl_uniform_state_set1i (GskGLUniformState *state,
 }
 
 void
-gsk_gl_uniform_state_set2i (GskGLUniformState *state,
-                            guint              program,
-                            guint              location,
-                            int                value0,
-                            int                value1)
+gsk_gl_uniform_state_set2i (GskGLUniformState   *state,
+                            GskGLUniformProgram *program,
+                            guint                location,
+                            int                  value0,
+                            int                  value1)
 {
   Uniform2i *u;
   GskGLUniformInfo *info;
 
   g_assert (state != NULL);
-  g_assert (program > 0);
+  g_assert (program != NULL);
 
   if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_2I, 1, location, &info)))
     {
@@ -549,18 +453,18 @@ gsk_gl_uniform_state_set2i (GskGLUniformState *state,
 }
 
 void
-gsk_gl_uniform_state_set3i (GskGLUniformState *state,
-                            guint              program,
-                            guint              location,
-                            int                value0,
-                            int                value1,
-                            int                value2)
+gsk_gl_uniform_state_set3i (GskGLUniformState   *state,
+                            GskGLUniformProgram *program,
+                            guint                location,
+                            int                  value0,
+                            int                  value1,
+                            int                  value2)
 {
   Uniform3i *u;
   GskGLUniformInfo *info;
 
   g_assert (state != NULL);
-  g_assert (program > 0);
+  g_assert (program != NULL);
 
   if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_3I, 1, location, &info)))
     {
@@ -576,19 +480,19 @@ gsk_gl_uniform_state_set3i (GskGLUniformState *state,
 }
 
 void
-gsk_gl_uniform_state_set4i (GskGLUniformState *state,
-                            guint              program,
-                            guint              location,
-                            int                value0,
-                            int                value1,
-                            int                value2,
-                            int                value3)
+gsk_gl_uniform_state_set4i (GskGLUniformState   *state,
+                            GskGLUniformProgram *program,
+                            guint                location,
+                            int                  value0,
+                            int                  value1,
+                            int                  value2,
+                            int                  value3)
 {
   Uniform4i *u;
   GskGLUniformInfo *info;
 
   g_assert (state != NULL);
-  g_assert (program > 0);
+  g_assert (program != NULL);
 
   if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_4I, 1, location, &info)))
     {
@@ -606,7 +510,7 @@ gsk_gl_uniform_state_set4i (GskGLUniformState *state,
 
 void
 gsk_gl_uniform_state_set_rounded_rect (GskGLUniformState    *state,
-                                       guint                 program,
+                                       GskGLUniformProgram  *program,
                                        guint                 location,
                                        const GskRoundedRect *rounded_rect)
 {
@@ -614,7 +518,7 @@ gsk_gl_uniform_state_set_rounded_rect (GskGLUniformState    *state,
   GskGLUniformInfo *info;
 
   g_assert (state != NULL);
-  g_assert (program > 0);
+  g_assert (program != NULL);
   g_assert (rounded_rect != NULL);
   g_assert (uniform_sizes[GSK_GL_UNIFORM_FORMAT_ROUNDED_RECT] == sizeof *rounded_rect);
 
@@ -641,7 +545,7 @@ gsk_gl_uniform_state_set_rounded_rect (GskGLUniformState    *state,
 
 void
 gsk_gl_uniform_state_set_matrix (GskGLUniformState       *state,
-                                 guint                    program,
+                                 GskGLUniformProgram     *program,
                                  guint                    location,
                                  const graphene_matrix_t *matrix)
 {
@@ -649,7 +553,7 @@ gsk_gl_uniform_state_set_matrix (GskGLUniformState       *state,
   GskGLUniformInfo *info;
 
   g_assert (state != NULL);
-  g_assert (program > 0);
+  g_assert (program != NULL);
   g_assert (matrix != NULL);
 
   if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_MATRIX, 1, location, &info)))
@@ -678,10 +582,10 @@ gsk_gl_uniform_state_set_matrix (GskGLUniformState       *state,
  * 1 for GL_TEXTURE1, and so on.
  */
 void
-gsk_gl_uniform_state_set_texture (GskGLUniformState *state,
-                                  guint              program,
-                                  guint              location,
-                                  guint              texture_slot)
+gsk_gl_uniform_state_set_texture (GskGLUniformState   *state,
+                                  GskGLUniformProgram *program,
+                                  guint                location,
+                                  guint                texture_slot)
 {
   GskGLUniformInfo *info;
   guint *u;
@@ -714,17 +618,17 @@ gsk_gl_uniform_state_set_texture (GskGLUniformState *state,
  * in other portions of the renderer.
  */
 void
-gsk_gl_uniform_state_set_color (GskGLUniformState *state,
-                                guint              program,
-                                guint              location,
-                                const GdkRGBA     *color)
+gsk_gl_uniform_state_set_color (GskGLUniformState   *state,
+                                GskGLUniformProgram *program,
+                                guint                location,
+                                const GdkRGBA       *color)
 {
   static const GdkRGBA transparent = {0};
   GskGLUniformInfo *info;
   GdkRGBA *u;
 
   g_assert (state != NULL);
-  g_assert (program > 0);
+  g_assert (program != NULL);
 
   if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_COLOR, 1, location, &info)))
     {
@@ -741,17 +645,17 @@ gsk_gl_uniform_state_set_color (GskGLUniformState *state,
 }
 
 void
-gsk_gl_uniform_state_set1fv (GskGLUniformState *state,
-                             guint              program,
-                             guint              location,
-                             guint              count,
-                             const float       *value)
+gsk_gl_uniform_state_set1fv (GskGLUniformState   *state,
+                             GskGLUniformProgram *program,
+                             guint                location,
+                             guint                count,
+                             const float         *value)
 {
   Uniform1f *u;
   GskGLUniformInfo *info;
 
   g_assert (state != NULL);
-  g_assert (program > 0);
+  g_assert (program != NULL);
   g_assert (count > 0);
 
   if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_1FV, count, location, &info)))
@@ -768,17 +672,17 @@ gsk_gl_uniform_state_set1fv (GskGLUniformState *state,
 }
 
 void
-gsk_gl_uniform_state_set2fv (GskGLUniformState *state,
-                             guint              program,
-                             guint              location,
-                             guint              count,
-                             const float       *value)
+gsk_gl_uniform_state_set2fv (GskGLUniformState   *state,
+                             GskGLUniformProgram *program,
+                             guint                location,
+                             guint                count,
+                             const float         *value)
 {
   Uniform2f *u;
   GskGLUniformInfo *info;
 
   g_assert (state != NULL);
-  g_assert (program > 0);
+  g_assert (program != NULL);
   g_assert (count > 0);
 
   if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_2FV, count, location, &info)))
@@ -795,17 +699,17 @@ gsk_gl_uniform_state_set2fv (GskGLUniformState *state,
 }
 
 void
-gsk_gl_uniform_state_set3fv (GskGLUniformState *state,
-                             guint              program,
-                             guint              location,
-                             guint              count,
-                             const float       *value)
+gsk_gl_uniform_state_set3fv (GskGLUniformState   *state,
+                             GskGLUniformProgram *program,
+                             guint                location,
+                             guint                count,
+                             const float         *value)
 {
   Uniform3f *u;
   GskGLUniformInfo *info;
 
   g_assert (state != NULL);
-  g_assert (program > 0);
+  g_assert (program != NULL);
   g_assert (count > 0);
 
   if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_3FV, count, location, &info)))
@@ -822,17 +726,17 @@ gsk_gl_uniform_state_set3fv (GskGLUniformState *state,
 }
 
 void
-gsk_gl_uniform_state_set4fv (GskGLUniformState *state,
-                             guint              program,
-                             guint              location,
-                             guint              count,
-                             const float       *value)
+gsk_gl_uniform_state_set4fv (GskGLUniformState   *state,
+                             GskGLUniformProgram *program,
+                             guint                location,
+                             guint                count,
+                             const float         *value)
 {
   Uniform4f *u;
   GskGLUniformInfo *info;
 
   g_assert (state != NULL);
-  g_assert (program > 0);
+  g_assert (program != NULL);
   g_assert (count > 0);
 
   if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_4FV, count, location, &info)))
@@ -851,6 +755,8 @@ gsk_gl_uniform_state_set4fv (GskGLUniformState *state,
 void
 gsk_gl_uniform_state_end_frame (GskGLUniformState *state)
 {
+  GHashTableIter iter;
+  GskGLUniformProgram *program;
   guint allocator = 0;
 
   g_return_if_fail (state != NULL);
@@ -861,16 +767,12 @@ gsk_gl_uniform_state_end_frame (GskGLUniformState *state)
    * discard it but keep an allocation around to reuse.
    */
 
-  for (guint i = 0; i < state->program_info->len; i++)
+  g_hash_table_iter_init (&iter, state->programs);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&program))
     {
-      GskGLUniformProgram *program_info = &g_array_index (state->program_info, GskGLUniformProgram, i);
-
-      if (program_info->uniform_info == NULL)
-        continue;
-
-      for (guint j = 0; j < program_info->uniform_info_len; j++)
+      for (guint j = 0; j < program->n_uniforms; j++)
         {
-          GskGLUniformInfo *info = &program_info->uniform_info[j];
+          GskGLUniformInfo *info = &program->uniforms[j];
           guint size;
 
           if (info->format == 0)
@@ -891,7 +793,7 @@ gsk_gl_uniform_state_end_frame (GskGLUniformState *state)
           allocator += size;
         }
 
-      program_info->n_changed = 0;
+      program->n_changed = 0;
     }
 
   state->values_pos = allocator;
@@ -907,3 +809,37 @@ gsk_gl_uniform_format_size (GskGLUniformFormat format)
 
   return uniform_sizes[format];
 }
+
+GskGLUniformProgram *
+gsk_gl_uniform_state_get_program (GskGLUniformState *state,
+                                  guint              program,
+                                  guint              n_uniforms)
+{
+  GskGLUniformProgram *ret;
+
+  g_return_val_if_fail (state != NULL, NULL);
+  g_return_val_if_fail (program > 0, NULL);
+  g_return_val_if_fail (program < G_MAXUINT, NULL);
+
+  ret = g_hash_table_lookup (state->programs, GUINT_TO_POINTER (program));
+
+  if (ret == NULL)
+    {
+      gsize uniform_size = n_uniforms * sizeof (GskGLUniformInfo);
+      gsize changed_size = n_uniforms * sizeof (guint);
+      gsize size = sizeof (GskGLUniformProgram) + uniform_size + changed_size;
+
+      ret = g_malloc0 (size);
+      ret->program_id = program;
+      ret->n_uniforms = n_uniforms;
+      ret->n_changed = 0;
+      ret->changed = (guint *)&ret->uniforms[n_uniforms];
+
+      for (guint i = 0; i < n_uniforms; i++)
+        ret->uniforms[i].initial = TRUE;
+
+      g_hash_table_insert (state->programs, GUINT_TO_POINTER (program), ret);
+    }
+
+  return ret;
+}
diff --git a/gsk/next/gskgluniformstateprivate.h b/gsk/next/gskgluniformstateprivate.h
index c08f568cb3..29c3104a18 100644
--- a/gsk/next/gskgluniformstateprivate.h
+++ b/gsk/next/gskgluniformstateprivate.h
@@ -40,25 +40,28 @@ G_STATIC_ASSERT (sizeof (GskGLUniformInfo) == 4);
 
 typedef struct _GskGLUniformProgram
 {
-  /* Array of GskGLUniformInfo, index is GLSL location */
-  GskGLUniformInfo *uniform_info;
-  guint uniform_info_len;
+  guint program_id;
+  guint n_uniforms;
 
   /* To avoid walking unchanged locations in @uniform_info (or sparse
    * elements used to map location->info), we use this to determine the
    * specific uniforms that changed this frame.
    */
   guint *changed;
-  guint  changed_len : 16;
-  guint  n_changed : 16;
+  guint n_changed;
+
+  /* Uniforms are provided inline at the end of structure to avoid
+   * an extra dereference.
+   */
+  GskGLUniformInfo uniforms[0];
 } GskGLUniformProgram;
 
 typedef struct _GskGLUniformState
 {
-  GArray *program_info;
+  GHashTable *programs;
   guint8 *values_buf;
-  guint   values_pos;
-  guint   values_len;
+  guint values_pos;
+  guint values_len;
 } GskGLUniformState;
 
 /**
@@ -103,101 +106,102 @@ typedef enum _GskGLUniformKind
   GSK_GL_UNIFORM_FORMAT_LAST
 } GskGLUniformFormat;
 
-GskGLUniformState *gsk_gl_uniform_state_new              (void);
-GskGLUniformState *gsk_gl_uniform_state_ref              (GskGLUniformState         *state);
-void               gsk_gl_uniform_state_unref            (GskGLUniformState         *state);
-void               gsk_gl_uniform_state_clear_program    (GskGLUniformState         *state,
-                                                          guint                      program);
-void               gsk_gl_uniform_state_end_frame        (GskGLUniformState         *state);
-void               gsk_gl_uniform_state_set1f            (GskGLUniformState         *state,
-                                                          guint                      program,
-                                                          guint                      location,
-                                                          float                      value0);
-void               gsk_gl_uniform_state_set2f            (GskGLUniformState         *state,
-                                                          guint                      program,
-                                                          guint                      location,
-                                                          float                      value0,
-                                                          float                      value1);
-void               gsk_gl_uniform_state_set3f            (GskGLUniformState         *state,
-                                                          guint                      program,
-                                                          guint                      location,
-                                                          float                      value0,
-                                                          float                      value1,
-                                                          float                      value2);
-void               gsk_gl_uniform_state_set4f            (GskGLUniformState         *state,
-                                                          guint                      program,
-                                                          guint                      location,
-                                                          float                      value0,
-                                                          float                      value1,
-                                                          float                      value2,
-                                                          float                      value3);
-void               gsk_gl_uniform_state_set1fv           (GskGLUniformState         *state,
-                                                          guint                      program,
-                                                          guint                      location,
-                                                          guint                      count,
-                                                          const float               *value);
-void               gsk_gl_uniform_state_set2fv           (GskGLUniformState         *state,
-                                                          guint                      program,
-                                                          guint                      location,
-                                                          guint                      count,
-                                                          const float               *value);
-void               gsk_gl_uniform_state_set3fv           (GskGLUniformState         *state,
-                                                          guint                      program,
-                                                          guint                      location,
-                                                          guint                      count,
-                                                          const float               *value);
-void               gsk_gl_uniform_state_set4fv           (GskGLUniformState         *state,
-                                                          guint                      program,
-                                                          guint                      location,
-                                                          guint                      count,
-                                                          const float               *value);
-void               gsk_gl_uniform_state_set1ui           (GskGLUniformState         *state,
-                                                          guint                      program,
-                                                          guint                      location,
-                                                          guint                      value0);
-void               gsk_gl_uniform_state_set1i            (GskGLUniformState         *state,
-                                                          guint                      program,
-                                                          guint                      location,
-                                                          int                        value0);
-void               gsk_gl_uniform_state_set2i            (GskGLUniformState         *state,
-                                                          guint                      program,
-                                                          guint                      location,
-                                                          int                        value0,
-                                                          int                        value1);
-void               gsk_gl_uniform_state_set3i            (GskGLUniformState         *state,
-                                                          guint                      program,
-                                                          guint                      location,
-                                                          int                        value0,
-                                                          int                        value1,
-                                                          int                        value2);
-void               gsk_gl_uniform_state_set4i            (GskGLUniformState         *state,
-                                                          guint                      program,
-                                                          guint                      location,
-                                                          int                        value0,
-                                                          int                        value1,
-                                                          int                        value2,
-                                                          int                        value3);
-void               gsk_gl_uniform_state_set_rounded_rect (GskGLUniformState         *self,
-                                                          guint                      program,
-                                                          guint                      location,
-                                                          const GskRoundedRect      *rect);
-void               gsk_gl_uniform_state_set_matrix       (GskGLUniformState         *self,
-                                                          guint                      program,
-                                                          guint                      location,
-                                                          const graphene_matrix_t   *value);
-void               gsk_gl_uniform_state_set_texture      (GskGLUniformState         *self,
-                                                          guint                      program,
-                                                          guint                      location,
-                                                          guint                      texture_slot);
-void               gsk_gl_uniform_state_set_color        (GskGLUniformState         *self,
-                                                          guint                      program,
-                                                          guint                      location,
-                                                          const GdkRGBA             *color);
-void               gsk_gl_uniform_state_snapshot         (GskGLUniformState         *self,
-                                                          guint                      program_id,
-                                                          GskGLUniformStateCallback  callback,
-                                                          gpointer                   user_data);
-gsize              gsk_gl_uniform_format_size            (GskGLUniformFormat         format);
+GskGLUniformState   *gsk_gl_uniform_state_new              (void);
+GskGLUniformState   *gsk_gl_uniform_state_ref              (GskGLUniformState         *state);
+void                 gsk_gl_uniform_state_unref            (GskGLUniformState         *state);
+GskGLUniformProgram *gsk_gl_uniform_state_get_program      (GskGLUniformState         *state,
+                                                            guint                      program,
+                                                            guint                      n_uniforms);
+void                 gsk_gl_uniform_state_end_frame        (GskGLUniformState         *state);
+void                 gsk_gl_uniform_state_set1f            (GskGLUniformState         *state,
+                                                            GskGLUniformProgram       *program,
+                                                            guint                      location,
+                                                            float                      value0);
+void                 gsk_gl_uniform_state_set2f            (GskGLUniformState         *state,
+                                                            GskGLUniformProgram       *program,
+                                                            guint                      location,
+                                                            float                      value0,
+                                                            float                      value1);
+void                 gsk_gl_uniform_state_set3f            (GskGLUniformState         *state,
+                                                            GskGLUniformProgram       *program,
+                                                            guint                      location,
+                                                            float                      value0,
+                                                            float                      value1,
+                                                            float                      value2);
+void                 gsk_gl_uniform_state_set4f            (GskGLUniformState         *state,
+                                                            GskGLUniformProgram       *program,
+                                                            guint                      location,
+                                                            float                      value0,
+                                                            float                      value1,
+                                                            float                      value2,
+                                                            float                      value3);
+void                 gsk_gl_uniform_state_set1fv           (GskGLUniformState         *state,
+                                                            GskGLUniformProgram       *program,
+                                                            guint                      location,
+                                                            guint                      count,
+                                                            const float               *value);
+void                 gsk_gl_uniform_state_set2fv           (GskGLUniformState         *state,
+                                                            GskGLUniformProgram       *program,
+                                                            guint                      location,
+                                                            guint                      count,
+                                                            const float               *value);
+void                 gsk_gl_uniform_state_set3fv           (GskGLUniformState         *state,
+                                                            GskGLUniformProgram       *program,
+                                                            guint                      location,
+                                                            guint                      count,
+                                                            const float               *value);
+void                 gsk_gl_uniform_state_set4fv           (GskGLUniformState         *state,
+                                                            GskGLUniformProgram       *program,
+                                                            guint                      location,
+                                                            guint                      count,
+                                                            const float               *value);
+void                 gsk_gl_uniform_state_set1ui           (GskGLUniformState         *state,
+                                                            GskGLUniformProgram       *program,
+                                                            guint                      location,
+                                                            guint                      value0);
+void                 gsk_gl_uniform_state_set1i            (GskGLUniformState         *state,
+                                                            GskGLUniformProgram       *program,
+                                                            guint                      location,
+                                                            int                        value0);
+void                 gsk_gl_uniform_state_set2i            (GskGLUniformState         *state,
+                                                            GskGLUniformProgram       *program,
+                                                            guint                      location,
+                                                            int                        value0,
+                                                            int                        value1);
+void                 gsk_gl_uniform_state_set3i            (GskGLUniformState         *state,
+                                                            GskGLUniformProgram       *program,
+                                                            guint                      location,
+                                                            int                        value0,
+                                                            int                        value1,
+                                                            int                        value2);
+void                 gsk_gl_uniform_state_set4i            (GskGLUniformState         *state,
+                                                            GskGLUniformProgram       *program,
+                                                            guint                      location,
+                                                            int                        value0,
+                                                            int                        value1,
+                                                            int                        value2,
+                                                            int                        value3);
+void                 gsk_gl_uniform_state_set_rounded_rect (GskGLUniformState         *self,
+                                                            GskGLUniformProgram       *program,
+                                                            guint                      location,
+                                                            const GskRoundedRect      *rect);
+void                 gsk_gl_uniform_state_set_matrix       (GskGLUniformState         *self,
+                                                            GskGLUniformProgram       *program,
+                                                            guint                      location,
+                                                            const graphene_matrix_t   *value);
+void                 gsk_gl_uniform_state_set_texture      (GskGLUniformState         *self,
+                                                            GskGLUniformProgram       *program,
+                                                            guint                      location,
+                                                            guint                      texture_slot);
+void                 gsk_gl_uniform_state_set_color        (GskGLUniformState         *self,
+                                                            GskGLUniformProgram       *program,
+                                                            guint                      location,
+                                                            const GdkRGBA             *color);
+void                 gsk_gl_uniform_state_snapshot         (GskGLUniformState         *self,
+                                                            guint                      program_id,
+                                                            GskGLUniformStateCallback  callback,
+                                                            gpointer                   user_data);
+gsize                gsk_gl_uniform_format_size            (GskGLUniformFormat         format);
 
 static inline gconstpointer
 gsk_gl_uniform_state_get_uniform_data (const GskGLUniformState *state,
@@ -206,37 +210,27 @@ gsk_gl_uniform_state_get_uniform_data (const GskGLUniformState *state,
   return (gconstpointer)(state->values_buf + offset);
 }
 
-#define gsk_gl_uniform_state_snapshot(state, program_id, callback, user_data)                      \
-  G_STMT_START {                                                                                   \
-    GskGLUniformProgram *program_info;                                                             \
-                                                                                                   \
-    program_info = &g_array_index (state->program_info, GskGLUniformProgram, program_id);          \
-    if (program_info->n_changed == 0)                                                              \
-      break;                                                                                       \
-                                                                                                   \
-    for (guint z = 0; z < program_info->n_changed; z++)                                            \
-      {                                                                                            \
-        guint location = program_info->changed[z];                                                 \
-        GskGLUniformInfo *info = &program_info->uniform_info[location];                            \
-                                                                                                   \
-        g_assert (info->changed);                                                                  \
-                                                                                                   \
-        callback (info, location, user_data);                                                      \
-                                                                                                   \
-        info->changed = FALSE;                                                                     \
-        info->send_corners = FALSE;                                                                \
-      }                                                                                            \
-                                                                                                   \
-    program_info->n_changed = 0;                                                                   \
+#define gsk_gl_uniform_state_snapshot(state, program_info, callback, user_data) \
+  G_STMT_START {                                                                \
+    if (program_info->n_changed)                                                \
+      {                                                                         \
+        for (guint z = 0; z < program_info->n_changed; z++)                     \
+          {                                                                     \
+            guint location = program_info->changed[z];                          \
+            GskGLUniformInfo *info = &program_info->uniforms[location];         \
+                                                                                \
+            g_assert (info->changed);                                           \
+                                                                                \
+            callback (info, location, user_data);                               \
+                                                                                \
+            info->changed = FALSE;                                              \
+            info->send_corners = FALSE;                                         \
+          }                                                                     \
+      }                                                                         \
+                                                                                \
+    program_info->n_changed = 0;                                                \
   } G_STMT_END
 
-static inline guint
-gsk_gl_uniform_state_get_n_changed (GskGLUniformState *self,
-                                    guint              program_id)
-{
-  return g_array_index (self->program_info, GskGLUniformProgram, program_id).n_changed;
-}
-
 G_END_DECLS
 
 #endif /* GSK_GL_UNIFORM_STATE_PRIVATE_H */


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