[gtk/wip/chergert/glproto: 80/82] start on more memory friendly version of command queue




commit e7587510de1d776ba29e33680daf887d8f44ad88
Author: Christian Hergert <chergert redhat com>
Date:   Tue Dec 22 17:08:20 2020 -0800

    start on more memory friendly version of command queue
    
    This avoids the pointers (and therefore using slice allocations w/ reuse)
    by keeping fixed sized structs for most things and multiple arrays for
    the in-flight changes.
    
    To avoid memmove of batches we have a next_batch_index int that can be
    changed as we append batches to the end of the array to reorder.
    
    I'm still thinking about what we can do to track rects as we do reordering
    as well as what operations are barriers. Will probably keep a window by
    program as we go which gets reset as we hit barriers.

 gsk/next/gskglbuffer.c              |    8 +
 gsk/next/gskglbufferprivate.h       |   15 +-
 gsk/next/gskglcommandqueue.c        | 1064 ++++++++++++++---------------------
 gsk/next/gskglcommandqueueprivate.h |   22 +-
 gsk/next/gskglprogram.c             |   51 +-
 gsk/next/gskglprogramprivate.h      |  116 ++--
 gsk/next/gskglrenderjob.c           |   75 ++-
 gsk/next/gskgltypes.h               |    2 +
 8 files changed, 585 insertions(+), 768 deletions(-)
---
diff --git a/gsk/next/gskglbuffer.c b/gsk/next/gskglbuffer.c
index 913f5ebc8e..c9ee140ec0 100644
--- a/gsk/next/gskglbuffer.c
+++ b/gsk/next/gskglbuffer.c
@@ -169,3 +169,11 @@ gsk_gl_buffer_advance (GskGLBuffer *buffer,
   g_array_set_size (buffer->buffer, buffer->buffer->len + count);
   return (guint8 *)buffer->buffer->data + (*offset * g_array_get_element_size (buffer->buffer));
 }
+
+guint
+gsk_gl_buffer_get_offset (GskGLBuffer *buffer)
+{
+  g_return_val_if_fail (buffer != NULL, 0);
+
+  return buffer->buffer->len;
+}
diff --git a/gsk/next/gskglbufferprivate.h b/gsk/next/gskglbufferprivate.h
index 2d88bb4995..3150dd7eeb 100644
--- a/gsk/next/gskglbufferprivate.h
+++ b/gsk/next/gskglbufferprivate.h
@@ -28,13 +28,14 @@ G_BEGIN_DECLS
 
 typedef struct _GskGLBuffer GskGLBuffer;
 
-GskGLBuffer *gsk_gl_buffer_new     (GLenum       target,
-                                    guint        element_size);
-void         gsk_gl_buffer_free    (GskGLBuffer *buffer);
-void         gsk_gl_buffer_submit  (GskGLBuffer *buffer);
-gpointer     gsk_gl_buffer_advance (GskGLBuffer *buffer,
-                                    guint        count,
-                                    guint       *offset);
+GskGLBuffer *gsk_gl_buffer_new        (GLenum       target,
+                                       guint        element_size);
+void         gsk_gl_buffer_free       (GskGLBuffer *buffer);
+void         gsk_gl_buffer_submit     (GskGLBuffer *buffer);
+guint        gsk_gl_buffer_get_offset (GskGLBuffer *buffer);
+gpointer     gsk_gl_buffer_advance    (GskGLBuffer *buffer,
+                                       guint        count,
+                                       guint       *offset);
 
 G_END_DECLS
 
diff --git a/gsk/next/gskglcommandqueue.c b/gsk/next/gskglcommandqueue.c
index 1dd1dfa009..f7286dcbd9 100644
--- a/gsk/next/gskglcommandqueue.c
+++ b/gsk/next/gskglcommandqueue.c
@@ -20,6 +20,7 @@
 
 #include "config.h"
 
+#include <gdk/gdkglcontextprivate.h>
 #include <gsk/gskdebugprivate.h>
 #include <epoxy/gl.h>
 
@@ -28,44 +29,25 @@
 #include "gskglcommandqueueprivate.h"
 #include "gskgluniformstateprivate.h"
 
-/* The MAX_MERGE_DISTANCE is used to reduce how far back we'll look for
- * programs to merge a batch with. This number is specific to batches using
- * the same program as a secondary index (See program_link field) is used
- * for tracking those.
- */
-#define MAX_MERGE_DISTANCE 5
-
 struct _GskGLCommandQueue
 {
   GObject parent_instance;
 
+  /* The GdkGLContext we make current before executing GL commands. */
   GdkGLContext *context;
 
-  /* Queue containing a linked list of all the GskGLCommandBatch that have
-   * been allocated so that we can reuse them on the next frame without
-   * allocating additional memory. Using stable pointers instead of offsets
-   * into an array makes this a bit easier to manage from a life-cycle
-   * standpoint as well as reordering using the links instead of memmove()s
-   * in an array.
-   */
-  GQueue unused_batches;
-
-  /* As we build the real command queue, we place the batches into this
-   * queue by pushing onto the tail. Executing commands will result in
-   * walking this queue from head to tail.
+  /* Array of GskGLCommandBatch which is a fixed size structure that will
+   * point into offsets of other arrays so that all similar data is stored
+   * together. The idea here is that we reduce the need for pointers so that
+   * using g_realloc()'d arrays is fine.
    */
-  GQueue all_batches;
+  GArray *batches;
 
-  /* When merging batches we want to skip all items between the merge
-   * candidate and the previous within it's program. To do this we keep an
-   * index of commands by program to avoid iteration overhead. This array
-   * contains a GQueue for each program which will point into the statically
-   * allocated @program_link in GskGLCommandBatch.
-   *
-   * After we find a merge candidate, we check for clipping and other
-   * changes which might make them unacceptable to merge.
+  /* Contains array of vertices and some wrapper code to help upload them
+   * to the GL driver. We can also tweak this to use double buffered arrays
+   * if we find that to be faster on some hardware and/or drivers.
    */
-  GArray *program_batches;
+  GskGLBuffer *vertices;
 
   /* The GskGLAttachmentState contains information about our FBO and texture
    * attachments as we process incoming operations. We snapshot them into
@@ -80,11 +62,24 @@ struct _GskGLCommandQueue
    */
   GskGLUniformState *uniforms;
 
-  /* Our VBO containing all the vertices to upload to the GPU before calling
-   * glDrawArrays() to draw. Each drawing operation contains 6 vec4 with the
-   * positions necessary to draw with glDrawArrays().
+  /* Array of GskGLCommandDraw which allows us to have a static size field
+   * in GskGLCommandBatch to coalesce draws. Multiple GskGLCommandDraw may
+   * be processed together (and out-of-order) to reduce the number of state
+   * changes when submitting commands.
    */
-  GskGLBuffer *vertices;
+  GArray *batch_draws;
+
+  /* Array of GskGLCommandBind which denote what textures need to be attached
+   * to which slot. GskGLCommandDraw.bind_offset and bind_count reference this
+   * array to determine what to attach.
+   */
+  GArray *batch_binds;
+
+  /* Array of GskGLCommandUniform denoting which uniforms must be updated
+   * before the glDrawArrays() may be called. These are referenced from the
+   * GskGLCommandDraw.uniform_offset and uniform_count fields.
+   */
+  GArray *batch_uniforms;
 
   /* Sometimes we want to save attachment state so that operations we do
    * cannot affect anything that is known to the command queue. We call
@@ -103,575 +98,411 @@ struct _GskGLCommandQueue
   GArray *autorelease_framebuffers;
   GArray *autorelease_textures;
 
+  /* String storage for debug groups */
+  GStringChunk *debug_groups;
+
+  /* Discovered max texture size when loading the command queue so that we
+   * can either scale down or slice textures to fit within this size. Assumed
+   * to be both height and width.
+   */
   int max_texture_size;
+
+  /* The index of the last batch in @batches, which may not be the element
+   * at the end of the array, as batches can be reordered. This is used to
+   * update the "next" index when adding a new batch.
+   */
+  int tail_batch_index;
+
+  /* If we're inside of a begin_draw()/end_draw() pair. */
+  guint in_draw : 1;
 };
 
-typedef struct _GskGLCommandDraw
+typedef enum _GskGLCommandKind
 {
-  guint vao_offset;
-  guint vao_count;
-} GskGLCommandDraw;
+  /* The batch will perform a glClear() */
+  GSK_GL_COMMAND_KIND_CLEAR,
 
-G_STATIC_ASSERT (sizeof (GskGLCommandDraw) == 8);
+  /* THe batch represents a new debug group */
+  GSK_GL_COMMAND_KIND_PUSH_DEBUG_GROUP,
 
-typedef struct _GskGLCommandUniform
-{
-  guint offset;
-  guint array_count : 16;
-  guint location : 7;
-  guint format : 5;
-  guint flags : 4;
-} GskGLCommandUniform;
+  /* The batch represents the end of a debug group */
+  GSK_GL_COMMAND_KIND_POP_DEBUG_GROUP,
 
-G_STATIC_ASSERT (sizeof (GskGLCommandUniform) == 8);
+  /* The batch will perform a glDrawArrays() */
+  GSK_GL_COMMAND_KIND_DRAW,
+} GskGLCommandKind;
 
-typedef struct _GskGLCommandBatch
+typedef struct _GskGLCommandBind
 {
-  /* An index into GskGLCommandQueue.all_batches */
-  GList all_link;
+  /* @texture is the value passed to glActiveTexture(), the "slot" the
+   * texture will be placed into. We always use GL_TEXTURE_2D so we don't
+   * waste any bits here to indicate that.
+   */
+  guint texture : 5;
 
-  /* An index into GskGLCommandBatch.program_batches */
-  GList program_link;
+  /* The identifier for the texture created with glGenTextures(). */
+  guint id : 27;
+} GskGLCommandBind;
 
-  union {
-    GskGLBindTexture  bind;
-    GskGLBindTexture *binds;
-  };
+G_STATIC_ASSERT (sizeof (GskGLCommandBind) == 4);
 
-  union {
-    GskGLCommandDraw  draw;
-    GskGLCommandDraw *draws;
-  };
-
-  union {
-    GskGLCommandUniform  uniform;
-    GskGLCommandUniform *uniforms;
-  };
+typedef struct _GskGLCommandDraw
+{
+  /* There doesn't seem to be a limit on the framebuffer identifier that
+   * can be returned, so we have to use a whole unsigned for the framebuffer
+   * we are drawing to. When processing batches, we check to see if this
+   * changes and adjust the render target accordingly. Some sorting is
+   * performed to reduce the amount we change framebuffers.
+   */
+  guint framebuffer;
 
-  guint is_clear : 1;
-  guint program_changed : 1;
-  guint program : 14;
-  guint n_draws : 16;
-  guint n_binds : 16;
-  guint n_uniforms : 16;
+  /* The number of uniforms to change. This must be less than or equal to
+   * GL_MAX_UNIFORM_LOCATIONS but only guaranteed up to 1024 by any OpenGL
+   * implementation to be conformant.
+   */
+  guint uniform_count : 11;
 
-  /* The framebuffer to use and if it has changed */
-  GskGLBindFramebuffer framebuffer;
-} GskGLCommandBatch;
+  /* The number of textures to bind, which is only guaranteed up to 16
+   * by the OpenGL specification to be conformant.
+   */
+  guint bind_count : 5;
 
-G_DEFINE_TYPE (GskGLCommandQueue, gsk_gl_command_queue, G_TYPE_OBJECT)
+  /* GL_MAX_ELEMENTS_VERTICES specifies 33000 for this which requires 16-bit
+   * to address all possible counts <= GL_MAX_ELEMENTS_VERTICES.
+   */
+  guint vbo_count : 16;
 
-static inline gboolean
-ispow2 (guint n)
-{
-  return !(n & (n-1));
-}
+  /* The offset within the VBO containing @vbo_count vertices to send with
+   * glDrawArrays().
+   */
+  guint vbo_offset;
 
-static GskGLCommandBatch *
-gsk_gl_command_queue_alloc_batch (GskGLCommandQueue *self)
-{
-  GskGLCommandBatch *batch;
+  /* The offset within the array of uniform changes to be made containing
+   * @uniform_count #GskGLCommandUniform elements to apply.
+   */
+  guint uniform_offset;
 
-  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+  /* The offset within the array of bind changes to be made containing
+   * @bind_count #GskGLCommandBind elements to apply.
+   */
+  guint bind_offset;
+} GskGLCommandDraw;
 
-  if G_LIKELY (self->unused_batches.length > 0)
-    return g_queue_pop_head_link (&self->unused_batches)->data;
+G_STATIC_ASSERT (sizeof (GskGLCommandDraw) == 20);
 
-  batch = g_slice_new0 (GskGLCommandBatch);
-  batch->all_link.data = batch;
-  batch->program_link.data = batch;
+typedef struct _GskGLCommandUniform
+{
+  GskGLUniformInfo info;
+  guint            location;
+} GskGLCommandUniform;
 
-  return batch;
-}
+G_STATIC_ASSERT (sizeof (GskGLCommandUniform) == 8);
 
-static void
-gsk_gl_command_queue_release_batch (GskGLCommandQueue *self,
-                                    GskGLCommandBatch *batch)
+typedef struct _GskGLCommandBatch
 {
-  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
-  g_assert (batch != NULL);
-
-  g_queue_unlink (&self->all_batches, &batch->all_link);
+  /* The index of the next batch following this one. This is used
+   * as a sort of integer-based linked list to simplify out-of-order
+   * batching without moving memory around. -1 indicates last batch.
+   */
+  int next_batch_index;
 
-  g_assert (batch->program ||
-            (batch->program_link.prev == NULL &&
-             batch->program_link.next == NULL));
+  /* A GskGLCommandKind indicating what the batch will do */
+  guint kind : 8;
 
-  if (batch->program_link.prev || batch->program_link.next)
-    {
-      GQueue *queue = &g_array_index (self->program_batches, GQueue, batch->program);
-      g_queue_unlink (queue, &batch->program_link);
-    }
+  /* The program's identifier to use for determining if we can merge two
+   * batches together into a single set of draw operations. We put this
+   * here instead of the GskGLCommandDraw so that we can use the extra
+   * bits here without making the structure larger.
+   */
+  guint program : 24;
 
-  if (batch->n_draws > 1)
-    g_free (batch->draws);
+  union {
+    /* Information about what to draw */
+    GskGLCommandDraw draw;
 
-  if (batch->n_binds > 1)
-    g_free (batch->binds);
+    /* The message to apply when pushing a debug group */
+    const char *debug_group;
 
-  batch->n_binds = 0;
-  batch->n_draws = 0;
-  batch->binds = NULL;
-  batch->draws = NULL;
-  batch->framebuffer.id = 0;
-  batch->framebuffer.changed = FALSE;
-  batch->program = 0;
-  batch->program_changed = FALSE;
+    /* The bits to glClear() */
+    struct {
+      guint bits;
+      guint framebuffer;
+    } clear;
+  };
+} GskGLCommandBatch;
 
-  g_assert (batch->program_link.prev == NULL);
-  g_assert (batch->program_link.next == NULL);
-  g_assert (batch->program_link.data == batch);
-  g_assert (batch->all_link.prev == NULL);
-  g_assert (batch->all_link.next == NULL);
-  g_assert (batch->all_link.data == batch);
+G_STATIC_ASSERT (sizeof (GskGLCommandBatch) == 32);
 
-  g_queue_push_head_link (&self->unused_batches, &batch->all_link);
-}
+G_DEFINE_TYPE (GskGLCommandQueue, gsk_gl_command_queue, G_TYPE_OBJECT)
 
 static void
-gsk_gl_command_batch_apply_uniform (GskGLCommandBatch         *batch,
-                                    GskGLUniformState         *state,
-                                    const GskGLCommandUniform *uniform)
+gsk_gl_command_queue_save (GskGLCommandQueue *self)
 {
-  const union {
-    graphene_matrix_t matrix[0];
-    GskRoundedRect rounded_rect[0];
-    float fval[0];
-    int ival[0];
-  } *data;
-
-  g_assert (batch != NULL);
-  g_assert (uniform != NULL);
-
-  data = gsk_gl_uniform_state_get_uniform_data (state, uniform->offset);
-
-  switch (uniform->format)
-    {
-    case GSK_GL_UNIFORM_FORMAT_1F:
-      glUniform1f (uniform->location, data->fval[0]);
-      break;
-
-    case GSK_GL_UNIFORM_FORMAT_2F:
-      glUniform2f (uniform->location, data->fval[0], data->fval[1]);
-      break;
-
-    case GSK_GL_UNIFORM_FORMAT_3F:
-      glUniform3f (uniform->location, data->fval[0], data->fval[1], data->fval[2]);
-      break;
-
-    case GSK_GL_UNIFORM_FORMAT_4F:
-      glUniform4f (uniform->location, data->fval[0], data->fval[1], data->fval[2], data->fval[3]);
-      break;
-
-    case GSK_GL_UNIFORM_FORMAT_1FV:
-      glUniform1fv (uniform->location, uniform->array_count, data->fval);
-      break;
-
-    case GSK_GL_UNIFORM_FORMAT_2FV:
-      glUniform2fv (uniform->location, uniform->array_count, data->fval);
-      break;
-
-    case GSK_GL_UNIFORM_FORMAT_3FV:
-      glUniform3fv (uniform->location, uniform->array_count, data->fval);
-      break;
-
-    case GSK_GL_UNIFORM_FORMAT_4FV:
-      glUniform4fv (uniform->location, uniform->array_count, data->fval);
-      break;
-
-    case GSK_GL_UNIFORM_FORMAT_1I:
-    case GSK_GL_UNIFORM_FORMAT_TEXTURE:
-      glUniform1i (uniform->location, data->ival[0]);
-      break;
-
-    case GSK_GL_UNIFORM_FORMAT_2I:
-      glUniform2i (uniform->location, data->ival[0], data->ival[1]);
-      break;
-
-    case GSK_GL_UNIFORM_FORMAT_3I:
-      glUniform3i (uniform->location, data->ival[0], data->ival[1], data->ival[2]);
-      break;
-
-    case GSK_GL_UNIFORM_FORMAT_4I:
-      glUniform4i (uniform->location, data->ival[0], data->ival[1], data->ival[2], data->ival[3]);
-      break;
-
-    case GSK_GL_UNIFORM_FORMAT_MATRIX: {
-      float mat[16];
-      graphene_matrix_to_float (&data->matrix[0], mat);
-      glUniformMatrix4fv (uniform->location, 1, GL_FALSE, mat);
-      break;
-    }
-
-    case GSK_GL_UNIFORM_FORMAT_COLOR:
-      glUniform4fv (uniform->location, 1, &data->fval[0]);
-      break;
-
-    case GSK_GL_UNIFORM_FORMAT_ROUNDED_RECT:
-      if (uniform->flags & GSK_GL_UNIFORM_FLAGS_SEND_CORNERS)
-        glUniform4fv (uniform->location, 3, (const float *)&data->rounded_rect[0]);
-      else
-        glUniform4fv (uniform->location, 1, (const float *)&data->rounded_rect[0]);
-      break;
+  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
 
-    default:
-      break;
-    }
+  g_ptr_array_add (self->saved_state,
+                   gsk_gl_attachment_state_save (self->attachments));
 }
 
 static void
-gsk_gl_command_batch_draw (GskGLCommandBatch *batch,
-                           guint              vao_offset,
-                           guint              vao_count)
+gsk_gl_command_queue_restore (GskGLCommandQueue *self)
 {
-  GskGLCommandDraw *last;
-
-  g_assert (batch != NULL);
-
-  if (batch->n_draws == 0)
-    {
-      batch->draw.vao_offset = vao_offset;
-      batch->draw.vao_count = vao_count;
-      batch->n_draws = 1;
-      return;
-    }
-
-  last = batch->n_draws == 1 ? &batch->draw : &batch->draws[batch->n_draws-1];
-
-  if (last->vao_offset + last->vao_count == vao_offset)
-    {
-      batch->draw.vao_count += vao_count;
-    }
-  else if (batch->n_draws == 1)
-    {
-      GskGLCommandDraw *draws = g_new (GskGLCommandDraw, 16);
+  GskGLAttachmentState *saved;
 
-      draws[0].vao_offset = batch->draw.vao_offset;
-      draws[0].vao_count = batch->draw.vao_count;
-      draws[1].vao_offset = vao_offset;
-      draws[1].vao_count = vao_count;
+  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_assert (self->saved_state->len > 0);
 
-      batch->draws = draws;
-      batch->n_draws = 2;
-    }
-  else
-    {
-      if G_UNLIKELY (batch->n_draws >= 16 && ispow2 (batch->n_draws))
-        batch->draws = g_realloc (batch->draws, sizeof (GskGLCommandDraw) * batch->n_draws * 2);
+  saved = g_ptr_array_steal_index (self->saved_state,
+                                   self->saved_state->len - 1);
 
-      batch->draws[batch->n_draws].vao_count = vao_count;
-      batch->draws[batch->n_draws].vao_offset = vao_offset;
-      batch->n_draws++;
-    }
+  gsk_gl_attachment_state_restore (saved);
 }
 
 static void
-gsk_gl_command_batch_execute (GskGLCommandBatch *batch,
-                              GskGLUniformState *uniforms)
+gsk_gl_command_queue_dispose (GObject *object)
 {
-  g_assert (batch != NULL);
-  g_assert (batch->n_draws > 0);
-
-  if (batch->framebuffer.changed)
-    glBindFramebuffer (GL_FRAMEBUFFER, batch->framebuffer.id);
-
-  if (batch->program_changed)
-    glUseProgram (batch->program);
-
-  if (batch->n_binds == 1)
-    {
-      g_assert (batch->bind.changed);
-
-      glActiveTexture (batch->bind.texture);
-      glBindTexture (batch->bind.target, batch->bind.id);
-    }
-  else if (batch->n_binds > 1)
-    {
-      for (guint i = 0; i < batch->n_binds; i++)
-        {
-          const GskGLBindTexture *bind = &batch->binds[i];
-
-          g_assert (bind->changed);
-
-          glActiveTexture (bind->texture);
-          glBindTexture (bind->target, bind->id);
-        }
-    }
-
-  if (batch->n_uniforms == 1)
-    {
-      gsk_gl_command_batch_apply_uniform (batch, uniforms, &batch->uniform);
-    }
-  else if (batch->n_uniforms > 0)
-    {
-      for (guint i = 0; i < batch->n_uniforms; i++)
-        {
-          const GskGLCommandUniform *uniform = &batch->uniforms[i];
-          gsk_gl_command_batch_apply_uniform (batch, uniforms, uniform);
-        }
-    }
+  GskGLCommandQueue *self = (GskGLCommandQueue *)object;
 
-  if (batch->is_clear)
-    {
-      glClearColor (0, 0, 0, 0);
-      glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
-    }
-  else if (batch->n_draws == 1)
-    {
-      glDrawArrays (GL_TRIANGLES, batch->draw.vao_offset, batch->draw.vao_count);
-    }
-  else if (batch->n_draws > 1)
-    {
-      for (guint i = 0; i < batch->n_draws; i++)
-        {
-          const GskGLCommandDraw *draw = &batch->draws[i];
+  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
 
-          g_assert (draw->vao_count > 0);
+  g_clear_object (&self->context);
+  g_clear_pointer (&self->batches, g_array_unref);
+  g_clear_pointer (&self->attachments, gsk_gl_attachment_state_free);
+  g_clear_pointer (&self->uniforms, gsk_gl_uniform_state_free);
+  g_clear_pointer (&self->vertices, gsk_gl_buffer_free);
+  g_clear_pointer (&self->batch_draws, g_array_unref);
+  g_clear_pointer (&self->batch_binds, g_array_unref);
+  g_clear_pointer (&self->batch_uniforms, g_array_unref);
+  g_clear_pointer (&self->saved_state, g_ptr_array_unref);
+  g_clear_pointer (&self->autorelease_framebuffers, g_array_unref);
+  g_clear_pointer (&self->autorelease_textures, g_array_unref);
 
-          glDrawArrays (GL_TRIANGLES, draw->vao_offset, draw->vao_count);
-        }
-    }
+  G_OBJECT_CLASS (gsk_gl_command_queue_parent_class)->dispose (object);
 }
 
 static void
-gsk_gl_command_batch_uniform_cb (const GskGLUniformInfo *info,
-                                 guint                   location,
-                                 gpointer                user_data)
+gsk_gl_command_queue_class_init (GskGLCommandQueueClass *klass)
 {
-  GskGLCommandBatch *batch = user_data;
-  GskGLCommandUniform *u;
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-  g_assert (batch != NULL);
-  g_assert (info != NULL);
+  object_class->dispose = gsk_gl_command_queue_dispose;
+}
 
-  if (batch->n_uniforms == 0)
-    {
-      u = &batch->uniform;
-      batch->n_uniforms = 1;
-    }
-  else if (batch->n_uniforms == 1)
-    {
-      u = g_new (GskGLCommandUniform, 2);
-      u[0] = batch->uniform;
-      batch->uniforms = u;
-      batch->n_uniforms = 2;
-      u = &u[1];
-    }
-  else
-    {
-      u = g_realloc_n (batch->uniforms, batch->n_uniforms+1, sizeof (GskGLCommandUniform));
-      batch->uniforms = u;
-      u = &u[batch->n_uniforms];
-      batch->n_uniforms++;
-    }
+static void
+gsk_gl_command_queue_init (GskGLCommandQueue *self)
+{
+  self->max_texture_size = -1;
 
-  u->format = info->format;
-  u->flags = info->flags;
-  u->array_count = info->array_count;
-  u->location = location;
-  u->offset = info->offset;
+  self->batches = g_array_new (FALSE, TRUE, sizeof (GskGLCommandBatch));
+  self->batch_draws = g_array_new (FALSE, FALSE, sizeof (GskGLCommandDraw));
+  self->batch_binds = g_array_new (FALSE, FALSE, sizeof (GskGLCommandBind));
+  self->batch_uniforms = g_array_new (FALSE, FALSE, sizeof (GskGLCommandUniform));
+  self->attachments = gsk_gl_attachment_state_new ();
+  self->vertices = gsk_gl_buffer_new (GL_ARRAY_BUFFER, sizeof (GskGLDrawVertex));
+  self->uniforms = gsk_gl_uniform_state_new ();
+  self->saved_state = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_attachment_state_free);
+  self->autorelease_textures = g_array_new (FALSE, FALSE, sizeof (GLuint));
+  self->autorelease_framebuffers = g_array_new (FALSE, FALSE, sizeof (GLuint));
+  self->debug_groups = g_string_chunk_new (4096);
 }
 
-static gboolean
-gsk_gl_command_batch_mergeable (GskGLCommandBatch *batch,
-                                GskGLCommandBatch *other)
+GskGLCommandQueue *
+gsk_gl_command_queue_new (GdkGLContext *context)
 {
-  g_assert (batch != NULL);
-  g_assert (other != NULL);
-  g_assert (batch != other);
+  GskGLCommandQueue *self;
 
-  if (batch->program != other->program)
-    return FALSE;
+  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
 
-  return FALSE;
+  self = g_object_new (GSK_TYPE_GL_COMMAND_QUEUE, NULL);
+  self->context = g_object_ref (context);
+  
+  return g_steal_pointer (&self);
 }
 
-static void
-gsk_gl_command_queue_try_merge (GskGLCommandQueue *self,
-                                GskGLCommandBatch *batch)
+void
+gsk_gl_command_queue_begin_draw (GskGLCommandQueue *self,
+                                 guint              program)
 {
-  guint count = 0;
-
-  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
-  g_assert (batch != NULL);
-  g_assert (batch->program != 0);
+  GskGLCommandBatch *batch;
 
-  /* We probably only want to look at the past couple by program to
-   * avoid pathological situations. In most cases, they will naturally
-   * come within the last few submissions.
-   */
+  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_return_if_fail (self->in_draw == FALSE);
 
-  for (const GList *iter = batch->program_link.prev;
-       iter != NULL && count < MAX_MERGE_DISTANCE;
-       iter = iter->prev, count++)
-    {
-      GskGLCommandBatch *predecessor = iter->data;
+  g_array_set_size (self->batches, self->batches->len + 1);
 
-      if (gsk_gl_command_batch_mergeable (predecessor, batch))
-        {
-          /* We need to check all the intermediates for overdrawing. */
-        }
-    }
+  batch = &g_array_index (self->batches, GskGLCommandBatch, self->batches->len - 1);
+  batch->kind = GSK_GL_COMMAND_KIND_DRAW;
+  batch->program = program;
+  batch->next_batch_index = -1;
+  batch->draw.framebuffer = 0;
+  batch->draw.uniform_count = 0;
+  batch->draw.uniform_offset = self->batch_uniforms->len;
+  batch->draw.bind_count = 0;
+  batch->draw.bind_offset = self->batch_binds->len;
+  batch->draw.vbo_count = 0;
+  batch->draw.vbo_offset = gsk_gl_buffer_get_offset (self->vertices);
 }
 
-static inline GskGLCommandBatch *
-gsk_gl_command_queue_get_batch (GskGLCommandQueue *self)
+static void
+gsk_gl_command_queue_uniform_snapshot_cb (const GskGLUniformInfo *info,
+                                          guint                   location,
+                                          gpointer                user_data)
 {
+  GskGLCommandQueue *self = user_data;
+  GskGLCommandUniform uniform;
+
+  g_assert (info != NULL);
   g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
 
-  return self->all_batches.tail->data;
+  uniform.location = location;
+  uniform.info = *info;
+
+  g_array_append_val (self->batch_uniforms, uniform);
 }
 
-static GskGLCommandBatch *
-gsk_gl_command_queue_advance (GskGLCommandQueue *self,
-                              guint              new_program)
+void
+gsk_gl_command_queue_end_draw (GskGLCommandQueue *self)
 {
-  GskGLCommandBatch *last_batch = NULL;
   GskGLCommandBatch *batch;
 
-  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
-
-  if (self->all_batches.length > 0)
+  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_return_if_fail (self->batches->len > 0);
+  g_return_if_fail (self->in_draw == TRUE);
+
+  batch = &g_array_index (self->batches, GskGLCommandBatch, self->batches->len - 1);
+  
+  g_assert (batch->kind == GSK_GL_COMMAND_KIND_DRAW);
+
+  /* Track the destination framebuffer in case it changed */
+  batch->draw.framebuffer = self->attachments->fbo.id;
+  self->attachments->fbo.changed = FALSE;
+
+  /* Track the list of uniforms that changed */
+  batch->draw.uniform_offset = self->batch_uniforms->len;
+  gsk_gl_uniform_state_snapshot (self->uniforms,
+                                 batch->program,
+                                 gsk_gl_command_queue_uniform_snapshot_cb,
+                                 self);
+  batch->draw.uniform_count = self->batch_uniforms->len - batch->draw.uniform_offset;
+
+  /* Track the bind attachments that changed */
+  batch->draw.bind_offset = self->batch_binds->len;
+  batch->draw.bind_count = 0;
+  for (guint i = 0; i < G_N_ELEMENTS (self->attachments->textures); i++)
     {
-      last_batch = self->all_batches.tail->data;
-
-      gsk_gl_uniform_state_snapshot (self->uniforms,
-                                     last_batch->program,
-                                     gsk_gl_command_batch_uniform_cb,
-                                     last_batch);
-    }
+      GskGLBindTexture *texture = &self->attachments->textures[i];
 
-  batch = gsk_gl_command_queue_alloc_batch (self);
+      if (texture->changed)
+        {
+          GskGLCommandBind bind;
 
-  if G_LIKELY (last_batch != NULL)
-    {
-      batch->program = new_program ? new_program : last_batch->program;
-      batch->program_changed = batch->program != last_batch->program;
-      batch->framebuffer = last_batch->framebuffer;
-      batch->framebuffer.changed = FALSE;
-    }
-  else
-    {
-      batch->program = new_program;
-      batch->program_changed = TRUE;
-      batch->framebuffer.id = 0;
-      batch->framebuffer.changed = FALSE;
-    }
+          texture->changed = FALSE;
 
-  g_queue_push_tail_link (&self->all_batches, &batch->all_link);
+          bind.texture = texture->texture;
+          bind.id = texture->id;
 
-  if (batch->program)
-    {
-      GQueue *q;
+          g_array_append_val (self->batch_binds, bind);
 
-      if (self->program_batches->len <= batch->program)
-        g_array_set_size (self->program_batches, batch->program + 1);
+          batch->draw.bind_count++;
+        }
+    }
 
-      q = &g_array_index (self->program_batches, GQueue, batch->program);
-      g_queue_push_tail_link (q, &batch->program_link);
+  if (self->tail_batch_index > -1)
+    {
+      GskGLCommandBatch *last_batch = &g_array_index (self->batches, GskGLCommandBatch, 
self->tail_batch_index);
+      last_batch->next_batch_index = self->batches->len - 1;
     }
 
-  if (last_batch != NULL)
-    gsk_gl_command_queue_try_merge (self, last_batch);
+  self->tail_batch_index = self->batches->len - 1;
 
-  return g_steal_pointer (&batch);
+  self->in_draw = FALSE;
 }
 
-static inline gboolean
-gsk_gl_command_queue_batch_is_complete (GskGLCommandQueue *self)
+GskGLDrawVertex *
+gsk_gl_command_queue_add_vertices (GskGLCommandQueue     *self,
+                                   const GskGLDrawVertex  vertices[GSK_GL_N_VERTICES])
 {
-  GskGLCommandBatch *batch = gsk_gl_command_queue_get_batch (self);
+  GskGLCommandBatch *batch;
+  GskGLDrawVertex *dest;
+  guint offset;
 
-  return batch->is_clear || (batch->program && batch->n_draws > 0);
-}
+  g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self), NULL);
+  g_return_val_if_fail (self->in_draw == TRUE, NULL);
 
-static void
-gsk_gl_command_queue_dispose (GObject *object)
-{
-  GskGLCommandQueue *self = (GskGLCommandQueue *)object;
+  batch = &g_array_index (self->batches, GskGLCommandBatch, self->batches->len - 1);
+  batch->draw.vbo_count += GSK_GL_N_VERTICES;
 
-  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+  dest = gsk_gl_buffer_advance (self->vertices, GSK_GL_N_VERTICES, &offset);
 
-  while (self->all_batches.length > 0)
+  if (vertices != NULL)
     {
-      GskGLCommandBatch *batch = self->all_batches.head->data;
-
-      gsk_gl_command_queue_release_batch (self, batch);
+      memcpy (dest, vertices, sizeof (GskGLDrawVertex) * GSK_GL_N_VERTICES);
+      return NULL;
     }
 
-  while (self->unused_batches.length > 0)
-    {
-      GskGLCommandBatch *batch = self->unused_batches.head->data;
+  return dest;
+}
 
-      g_queue_unlink (&self->unused_batches, self->unused_batches.head->data);
-      g_slice_free (GskGLCommandBatch, batch);
-    }
+void
+gsk_gl_command_queue_clear (GskGLCommandQueue *self,
+                            guint              clear_bits)
+{
+  GskGLCommandBatch *batch;
 
-#ifndef G_DISABLE_DEBUG
-  g_assert (self->unused_batches.length == 0);
-  g_assert (self->all_batches.length == 0);
+  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_return_if_fail (self->in_draw == FALSE);
+  g_return_if_fail (self->batches->len < G_MAXINT);
 
-  for (guint i = 0; i < self->program_batches->len; i++)
-    {
-      GQueue *q = &g_array_index (self->program_batches, GQueue, i);
-      g_assert (q->length == 0);
-    }
-#endif
+  if (clear_bits == 0)
+    clear_bits = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
 
-  g_clear_pointer (&self->saved_state, g_ptr_array_unref);
-  g_clear_pointer (&self->attachments, gsk_gl_attachment_state_free);
-  g_clear_object (&self->context);
-  g_clear_pointer (&self->uniforms, gsk_gl_uniform_state_free);
-  g_clear_pointer (&self->vertices, gsk_gl_buffer_free);
-  g_clear_pointer (&self->program_batches, g_array_unref);
-  g_clear_pointer (&self->autorelease_framebuffers, g_array_unref);
-  g_clear_pointer (&self->autorelease_textures, g_array_unref);
-
-  G_OBJECT_CLASS (gsk_gl_command_queue_parent_class)->dispose (object);
-}
+  g_array_set_size (self->batches, self->batches->len + 1);
 
-static void
-gsk_gl_command_queue_class_init (GskGLCommandQueueClass *klass)
-{
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  batch = &g_array_index (self->batches, GskGLCommandBatch, self->batches->len - 1);
+  batch->kind = GSK_GL_COMMAND_KIND_CLEAR;
+  batch->clear.bits = clear_bits;
+  batch->clear.framebuffer = self->attachments->fbo.id;
+  batch->next_batch_index = -1;
+  batch->program = 0;
 
-  object_class->dispose = gsk_gl_command_queue_dispose;
+  self->attachments->fbo.changed = FALSE;
 }
 
-static void
-gsk_gl_command_queue_init (GskGLCommandQueue *self)
+void
+gsk_gl_command_queue_push_debug_group (GskGLCommandQueue *self,
+                                       const char        *debug_group)
 {
-  self->max_texture_size = -1;
+  GskGLCommandBatch *batch;
 
-  self->attachments = gsk_gl_attachment_state_new ();
-  self->vertices = gsk_gl_buffer_new (GL_ARRAY_BUFFER, sizeof (GskGLDrawVertex));
-  self->uniforms = gsk_gl_uniform_state_new ();
-  self->program_batches = g_array_new (FALSE, TRUE, sizeof (GQueue));
-  self->saved_state = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_attachment_state_free);
-  self->autorelease_textures = g_array_new (FALSE, FALSE, sizeof (GLuint));
-  self->autorelease_framebuffers = g_array_new (FALSE, FALSE, sizeof (GLuint));
+  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_return_if_fail (self->in_draw == FALSE);
+  g_return_if_fail (self->batches->len < G_MAXINT);
 
-  gsk_gl_command_queue_advance (self, 0);
+  g_array_set_size (self->batches, self->batches->len + 1);
+
+  batch = &g_array_index (self->batches, GskGLCommandBatch, self->batches->len - 1);
+  batch->kind = GSK_GL_COMMAND_KIND_PUSH_DEBUG_GROUP;
+  batch->debug_group = g_string_chunk_insert (self->debug_groups, debug_group);
+  batch->next_batch_index = -1;
+  batch->program = 0;
 }
 
-GskGLCommandQueue *
-gsk_gl_command_queue_new (GdkGLContext *context)
+void
+gsk_gl_command_queue_pop_debug_group (GskGLCommandQueue *self)
 {
-  GskGLCommandQueue *self;
-
-  g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+  GskGLCommandBatch *batch;
 
-  self = g_object_new (GSK_TYPE_GL_COMMAND_QUEUE, NULL);
-  self->context = g_object_ref (context);
+  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_return_if_fail (self->in_draw == FALSE);
+  g_return_if_fail (self->batches->len < G_MAXINT);
 
-  if (self->max_texture_size < 0)
-    {
-      gdk_gl_context_make_current (context);
-      glGetIntegerv (GL_MAX_TEXTURE_SIZE, (GLint *)&self->max_texture_size);
-      GSK_NOTE (OPENGL, g_message ("GL max texture size: %d", self->max_texture_size));
-    }
+  g_array_set_size (self->batches, self->batches->len + 1);
 
-  return g_steal_pointer (&self);
+  batch = &g_array_index (self->batches, GskGLCommandBatch, self->batches->len - 1);
+  batch->kind = GSK_GL_COMMAND_KIND_POP_DEBUG_GROUP;
+  batch->debug_group = NULL;
+  batch->next_batch_index = -1;
+  batch->program = 0;
 }
 
 GdkGLContext *
@@ -691,30 +522,6 @@ gsk_gl_command_queue_make_current (GskGLCommandQueue *self)
   gdk_gl_context_make_current (self->context);
 }
 
-void
-gsk_gl_command_queue_use_program (GskGLCommandQueue *self,
-                                  guint              program)
-{
-  GskGLCommandBatch *batch;
-
-  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
-
-  batch = gsk_gl_command_queue_get_batch (self);
-
-  if (batch->program == program || program == 0)
-    return;
-
-  if (batch->n_draws == 0)
-    {
-      batch->program = program;
-      return;
-    }
-
-  batch = gsk_gl_command_queue_advance (self, program);
-  batch->program = program;
-  batch->program_changed = TRUE;
-}
-
 void
 gsk_gl_command_queue_delete_program (GskGLCommandQueue *self,
                                      guint              program)
@@ -725,30 +532,6 @@ gsk_gl_command_queue_delete_program (GskGLCommandQueue *self,
   gsk_gl_uniform_state_clear_program (self->uniforms, program);
 }
 
-GskGLDrawVertex *
-gsk_gl_command_queue_draw (GskGLCommandQueue    *self,
-                           const GskGLDrawVertex  vertices[6])
-{
-  GskGLCommandBatch *batch;
-  GskGLDrawVertex *dest;
-  guint offset;
-
-  g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self), NULL);
-
-  batch = gsk_gl_command_queue_get_batch (self);
-  dest = gsk_gl_buffer_advance (self->vertices, 6, &offset);
-
-  gsk_gl_command_batch_draw (batch, offset, 6);
-
-  if (vertices != NULL)
-    {
-      memcpy (dest, vertices, sizeof (GskGLDrawVertex) * 6);
-      return NULL;
-    }
-
-  return dest;
-}
-
 void
 gsk_gl_command_queue_set_uniform1i (GskGLCommandQueue *self,
                                     guint              program,
@@ -982,21 +765,15 @@ gsk_gl_command_queue_set_uniform_rounded_rect (GskGLCommandQueue    *self,
 void
 gsk_gl_command_queue_execute (GskGLCommandQueue *self)
 {
-  GskGLCommandBatch *last_batch;
   GLuint vao_id;
+  int next_batch_index;
 
   g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_return_if_fail (self->in_draw == FALSE);
 
-  if (self->all_batches.length == 0)
+  if (self->batches->len == 0)
     return;
 
-  /* First advance the queue to ensure that we have stashed all the
-   * state we need and possibly merged the final batch.
-   */
-  last_batch = gsk_gl_command_queue_get_batch (self);
-  if (last_batch->program != 0 && last_batch->n_draws > 0)
-    gsk_gl_command_queue_advance (self, 0);
-
   gsk_gl_command_queue_make_current (self);
 
   glEnable (GL_DEPTH_TEST);
@@ -1024,12 +801,34 @@ gsk_gl_command_queue_execute (GskGLCommandQueue *self)
                          sizeof (GskGLDrawVertex),
                          (void *) G_STRUCT_OFFSET (GskGLDrawVertex, uv));
 
-  for (const GList *iter = self->all_batches.head; iter != NULL; iter = iter->next)
+  next_batch_index = 0;
+
+  while (next_batch_index >= 0)
     {
-      GskGLCommandBatch *batch = self->all_batches.head->data;
+      const GskGLCommandBatch *batch = &g_array_index (self->batches, GskGLCommandBatch, next_batch_index);
+
+      switch (batch->kind)
+        {
+        case GSK_GL_COMMAND_KIND_CLEAR:
+          glClear (batch->clear.bits);
+          break;
 
-      if (batch->n_draws > 0)
-        gsk_gl_command_batch_execute (batch, self->uniforms);
+        case GSK_GL_COMMAND_KIND_PUSH_DEBUG_GROUP:
+          gdk_gl_context_push_debug_group (self->context, batch->debug_group);
+          break;
+
+        case GSK_GL_COMMAND_KIND_POP_DEBUG_GROUP:
+          gdk_gl_context_pop_debug_group (self->context);
+          break;
+
+        case GSK_GL_COMMAND_KIND_DRAW:
+          break;
+
+        default:
+          g_assert_not_reached ();
+        }
+
+      next_batch_index = batch->next_batch_index;
     }
 
   glDeleteVertexArrays (1, &vao_id);
@@ -1040,6 +839,8 @@ gsk_gl_command_queue_begin_frame (GskGLCommandQueue *self)
 {
   g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
 
+  self->tail_batch_index = -1;
+
   glBindFramebuffer (GL_FRAMEBUFFER, 0);
 
   for (guint i = 0; i < 8; i++)
@@ -1069,14 +870,10 @@ gsk_gl_command_queue_end_frame (GskGLCommandQueue *self)
 {
   g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
 
-  while (self->all_batches.length > 0)
-    {
-      GskGLCommandBatch *batch = self->all_batches.head->data;
-      gsk_gl_command_queue_release_batch (self, batch);
-    }
-
   gsk_gl_uniform_state_end_frame (self->uniforms);
 
+  self->batches->len = 0;
+
   /* Release autoreleased framebuffers */
   if (self->autorelease_framebuffers->len > 0)
     glDeleteFramebuffers (self->autorelease_framebuffers->len,
@@ -1087,100 +884,17 @@ gsk_gl_command_queue_end_frame (GskGLCommandQueue *self)
     glDeleteTextures (self->autorelease_textures->len,
                       (GLuint *)(gpointer)self->autorelease_textures->data);
 
-  /* Allocate first batch for next round so we never have an empty
-   * GskGLCommandQueue.all_batches array to check for elsewhere.
-   */
-  gsk_gl_command_queue_advance (self, 0);
-}
-
-void
-gsk_gl_command_queue_bind_framebuffer (GskGLCommandQueue *self,
-                                       guint              framebuffer)
-{
-  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
-
-  gsk_gl_attachment_state_bind_framebuffer (self->attachments, framebuffer);
+  g_string_chunk_clear (self->debug_groups);
 }
 
 void
-gsk_gl_command_queue_set_viewport (GskGLCommandQueue     *self,
-                                   const graphene_rect_t *viewport)
+gsk_gl_command_queue_change_viewport (GskGLCommandQueue     *self,
+                                      const graphene_rect_t *viewport)
 {
   g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_return_if_fail (viewport != NULL);
 
-  /* TODO: Set viewport as part of batch */
-}
-
-static void
-gsk_gl_command_queue_save (GskGLCommandQueue *self)
-{
-  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
-
-  g_ptr_array_add (self->saved_state,
-                   gsk_gl_attachment_state_save (self->attachments));
-}
-
-static void
-gsk_gl_command_queue_restore (GskGLCommandQueue *self)
-{
-  GskGLAttachmentState *saved;
-
-  g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
-  g_assert (self->saved_state->len > 0);
-
-  saved = g_ptr_array_steal_index (self->saved_state,
-                                   self->saved_state->len - 1);
-
-  gsk_gl_attachment_state_restore (saved);
-}
-
-int
-gsk_gl_command_queue_create_texture (GskGLCommandQueue *self,
-                                     int                width,
-                                     int                height,
-                                     int                min_filter,
-                                     int                mag_filter)
-{
-  GLuint texture_id;
-
-  g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self), -1);
-
-  if (width > self->max_texture_size || height > self->max_texture_size)
-    return -1;
-
-  gsk_gl_command_queue_save (self);
-  gsk_gl_command_queue_make_current (self);
-
-  glGenTextures (1, &texture_id);
-
-  glActiveTexture (GL_TEXTURE0);
-  glBindTexture (GL_TEXTURE_2D, texture_id);
-
-  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
-  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
-  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
-  if (gdk_gl_context_get_use_es (self->context))
-    glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-  else
-    glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
-
-  gsk_gl_command_queue_restore (self);
-
-  return (int)texture_id;
-}
-
-guint
-gsk_gl_command_queue_create_framebuffer (GskGLCommandQueue *self)
-{
-  GLuint fbo_id;
-
-  g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self), -1);
-
-  gsk_gl_command_queue_make_current (self);
-  glGenFramebuffers (1, &fbo_id);
-  return fbo_id;
+  
 }
 
 gboolean
@@ -1245,20 +959,62 @@ gsk_gl_command_queue_autorelease_texture (GskGLCommandQueue *self,
   g_array_append_val (self->autorelease_textures, texture_id);
 }
 
-void
-gsk_gl_command_queue_clear (GskGLCommandQueue *self)
+int
+gsk_gl_command_queue_create_texture (GskGLCommandQueue *self,
+                                     int                width,
+                                     int                height,
+                                     int                min_filter,
+                                     int                mag_filter)
 {
-  GskGLCommandBatch *batch;
+  GLuint texture_id;
 
-  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+  g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self), -1);
 
-  batch = gsk_gl_command_queue_get_batch (self);
+  if (width > self->max_texture_size || height > self->max_texture_size)
+    return -1;
 
-  if (batch->is_clear)
-    return;
+  gsk_gl_command_queue_save (self);
+  gsk_gl_command_queue_make_current (self);
+
+  glGenTextures (1, &texture_id);
+
+  glActiveTexture (GL_TEXTURE0);
+  glBindTexture (GL_TEXTURE_2D, texture_id);
+
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+  if (gdk_gl_context_get_use_es (self->context))
+    glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+  else
+    glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+
+  gsk_gl_command_queue_restore (self);
+
+  return (int)texture_id;
+}
+
+guint
+gsk_gl_command_queue_create_framebuffer (GskGLCommandQueue *self)
+{
+  GLuint fbo_id;
+
+  g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self), -1);
+
+  gsk_gl_command_queue_make_current (self);
+
+  glGenFramebuffers (1, &fbo_id);
+
+  return fbo_id;
+}
 
-  if (gsk_gl_command_queue_batch_is_complete (self))
-    batch = gsk_gl_command_queue_advance (self, 0);
+void
+gsk_gl_command_queue_bind_framebuffer (GskGLCommandQueue *self,
+                                       guint              framebuffer)
+{
+  g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
 
-  batch->is_clear = TRUE;
+  gsk_gl_attachment_state_bind_framebuffer (self->attachments, framebuffer);
 }
diff --git a/gsk/next/gskglcommandqueueprivate.h b/gsk/next/gskglcommandqueueprivate.h
index 8759041a90..1a5ccae8e2 100644
--- a/gsk/next/gskglcommandqueueprivate.h
+++ b/gsk/next/gskglcommandqueueprivate.h
@@ -35,13 +35,8 @@ void               gsk_gl_command_queue_make_current             (GskGLCommandQu
 void               gsk_gl_command_queue_begin_frame              (GskGLCommandQueue        *self);
 void               gsk_gl_command_queue_end_frame                (GskGLCommandQueue        *self);
 void               gsk_gl_command_queue_execute                  (GskGLCommandQueue        *self);
-void               gsk_gl_command_queue_set_viewport             (GskGLCommandQueue        *self,
+void               gsk_gl_command_queue_change_viewport          (GskGLCommandQueue        *self,
                                                                   const graphene_rect_t    *viewport);
-GdkTexture        *gsk_gl_command_queue_download                 (GskGLCommandQueue        *self,
-                                                                  GError                  **error);
-GdkMemoryTexture  *gsk_gl_command_queue_download_texture         (GskGLCommandQueue        *self,
-                                                                  guint                     texture_id,
-                                                                  GError                  **error);
 guint              gsk_gl_command_queue_upload_texture           (GskGLCommandQueue        *self,
                                                                   GdkTexture               *texture,
                                                                   GError                  **error);
@@ -58,11 +53,10 @@ gboolean           gsk_gl_command_queue_create_render_target     (GskGLCommandQu
                                                                   guint                    *out_texture_id);
 void               gsk_gl_command_queue_delete_program           (GskGLCommandQueue        *self,
                                                                   guint                     program_id);
-void               gsk_gl_command_queue_use_program              (GskGLCommandQueue        *self,
-                                                                  guint                     program_id);
 void               gsk_gl_command_queue_bind_framebuffer         (GskGLCommandQueue        *self,
                                                                   guint                     framebuffer);
-void               gsk_gl_command_queue_clear                    (GskGLCommandQueue        *self);
+void               gsk_gl_command_queue_clear                    (GskGLCommandQueue        *self,
+                                                                  guint                     clear_bits);
 void               gsk_gl_command_queue_set_uniform1i            (GskGLCommandQueue        *self,
                                                                   guint                     program,
                                                                   guint                     location,
@@ -140,12 +134,18 @@ void               gsk_gl_command_queue_set_uniform_rounded_rect (GskGLCommandQu
                                                                   guint                     program,
                                                                   guint                     location,
                                                                   const GskRoundedRect     *rounded_rect);
+void               gsk_gl_command_queue_push_debug_group         (GskGLCommandQueue        *self,
+                                                                  const char               *message);
+void               gsk_gl_command_queue_pop_debug_group          (GskGLCommandQueue        *self);
 void               gsk_gl_command_queue_autorelease_framebuffer  (GskGLCommandQueue        *self,
                                                                   guint                     framebuffer_id);
 void               gsk_gl_command_queue_autorelease_texture      (GskGLCommandQueue        *self,
                                                                   guint                     texture_id);
-GskGLDrawVertex   *gsk_gl_command_queue_draw                     (GskGLCommandQueue        *self,
-                                                                  const GskGLDrawVertex     vertices[6]);
+void               gsk_gl_command_queue_begin_draw               (GskGLCommandQueue        *self,
+                                                                  guint                     program);
+void               gsk_gl_command_queue_end_draw                 (GskGLCommandQueue        *self);
+GskGLDrawVertex   *gsk_gl_command_queue_add_vertices             (GskGLCommandQueue        *self,
+                                                                  const GskGLDrawVertex     
vertices[GSK_GL_N_VERTICES]);
 
 G_END_DECLS
 
diff --git a/gsk/next/gskglprogram.c b/gsk/next/gskglprogram.c
index 2c13bb0b65..e6280c6574 100644
--- a/gsk/next/gskglprogram.c
+++ b/gsk/next/gskglprogram.c
@@ -84,35 +84,6 @@ gsk_gl_program_init (GskGLProgram *self)
   self->uniform_locations = g_array_new (FALSE, TRUE, sizeof (GLint));
 }
 
-/**
- * gsk_gl_program_use:
- * @self: a #GskGLProgram
- *
- * Sets @self as the current program.
- */
-void
-gsk_gl_program_use (GskGLProgram *self)
-{
-  g_return_if_fail (GSK_IS_GL_PROGRAM (self));
-  g_return_if_fail (self->command_queue != NULL);
-
-  gsk_gl_command_queue_use_program (self->command_queue, self->id);
-}
-
-/**
- * gsk_gl_program_unuse:
- * @self: a #GskGLProgram
- *
- * Changes the program to 0 and cleans up any necessary state.
- */
-void
-gsk_gl_program_unuse (GskGLProgram *self)
-{
-  g_return_if_fail (GSK_IS_GL_PROGRAM (self));
-
-  gsk_gl_command_queue_use_program (self->command_queue, 0);
-}
-
 /**
  * gsk_gl_program_add_uniform:
  * @self: a #GskGLProgram
@@ -351,11 +322,27 @@ gsk_gl_program_set_uniform_rounded_rect (GskGLProgram         *self,
                                                  rounded_rect);
 }
 
+void
+gsk_gl_program_begin_draw (GskGLProgram *self)
+{
+  g_assert (GSK_IS_GL_PROGRAM (self));
+
+  return gsk_gl_command_queue_begin_draw (self->command_queue, self->id);
+}
+
+void
+gsk_gl_program_end_draw (GskGLProgram *self)
+{
+  g_assert (GSK_IS_GL_PROGRAM (self));
+
+  return gsk_gl_command_queue_end_draw (self->command_queue);
+}
+
 GskGLDrawVertex *
-gsk_gl_program_draw (GskGLProgram          *self,
-                     const GskGLDrawVertex  vertices[6])
+gsk_gl_program_add_vertices (GskGLProgram                   *self,
+                             const GskGLDrawVertex vertices[GSK_GL_N_VERTICES])
 {
   g_assert (GSK_IS_GL_PROGRAM (self));
 
-  return gsk_gl_command_queue_draw (self->command_queue, vertices);
+  return gsk_gl_command_queue_add_vertices (self->command_queue, vertices);
 }
diff --git a/gsk/next/gskglprogramprivate.h b/gsk/next/gskglprogramprivate.h
index e223a5263a..42c41e2850 100644
--- a/gsk/next/gskglprogramprivate.h
+++ b/gsk/next/gskglprogramprivate.h
@@ -29,64 +29,64 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GskGLProgram, gsk_gl_program, GSK, GL_PROGRAM, GObject)
 
-GskGLProgram    *gsk_gl_program_new                      (GskGLCommandQueue    *command_queue,
-                                                          const char           *name,
-                                                          int                   program_id);
-gboolean         gsk_gl_program_add_uniform              (GskGLProgram         *self,
-                                                          const char           *name,
-                                                          guint                 key);
-void             gsk_gl_program_use                      (GskGLProgram         *self);
-void             gsk_gl_program_unuse                    (GskGLProgram         *self);
-void             gsk_gl_program_delete                   (GskGLProgram         *self);
-void             gsk_gl_program_set_uniform1i            (GskGLProgram         *self,
-                                                          guint                 key,
-                                                          int                   value0);
-void             gsk_gl_program_set_uniform2i            (GskGLProgram         *self,
-                                                          guint                 key,
-                                                          int                   value0,
-                                                          int                   value1);
-void             gsk_gl_program_set_uniform3i            (GskGLProgram         *self,
-                                                          guint                 key,
-                                                          int                   value0,
-                                                          int                   value1,
-                                                          int                   value2);
-void             gsk_gl_program_set_uniform4i            (GskGLProgram         *self,
-                                                          guint                 key,
-                                                          int                   value0,
-                                                          int                   value1,
-                                                          int                   value2,
-                                                          int                   value3);
-void             gsk_gl_program_set_uniform1f            (GskGLProgram         *self,
-                                                          guint                 key,
-                                                          float                 value0);
-void             gsk_gl_program_set_uniform2f            (GskGLProgram         *self,
-                                                          guint                 key,
-                                                          float                 value0,
-                                                          float                 value1);
-void             gsk_gl_program_set_uniform3f            (GskGLProgram         *self,
-                                                          guint                 key,
-                                                          float                 value0,
-                                                          float                 value1,
-                                                          float                 value2);
-void             gsk_gl_program_set_uniform4f            (GskGLProgram         *self,
-                                                          guint                 key,
-                                                          float                 value0,
-                                                          float                 value1,
-                                                          float                 value2,
-                                                          float                 value3);
-void             gsk_gl_program_set_uniform_color        (GskGLProgram         *self,
-                                                          guint                 key,
-                                                          const GdkRGBA        *color);
-void             gsk_gl_program_set_uniform_texture      (GskGLProgram         *self,
-                                                          guint                 key,
-                                                          GLenum                texture_target,
-                                                          GLenum                texture_slot,
-                                                          guint                 texture_id);
-void             gsk_gl_program_set_uniform_rounded_rect (GskGLProgram         *self,
-                                                          guint                 key,
-                                                          const GskRoundedRect *rounded_rect);
-GskGLDrawVertex *gsk_gl_program_draw                     (GskGLProgram          *self,
-                                                          const GskGLDrawVertex  vertices[6]);
+GskGLProgram    *gsk_gl_program_new                      (GskGLCommandQueue     *command_queue,
+                                                          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_set_uniform1i            (GskGLProgram          *self,
+                                                          guint                  key,
+                                                          int                    value0);
+void             gsk_gl_program_set_uniform2i            (GskGLProgram          *self,
+                                                          guint                  key,
+                                                          int                    value0,
+                                                          int                    value1);
+void             gsk_gl_program_set_uniform3i            (GskGLProgram          *self,
+                                                          guint                  key,
+                                                          int                    value0,
+                                                          int                    value1,
+                                                          int                    value2);
+void             gsk_gl_program_set_uniform4i            (GskGLProgram          *self,
+                                                          guint                  key,
+                                                          int                    value0,
+                                                          int                    value1,
+                                                          int                    value2,
+                                                          int                    value3);
+void             gsk_gl_program_set_uniform1f            (GskGLProgram          *self,
+                                                          guint                  key,
+                                                          float                  value0);
+void             gsk_gl_program_set_uniform2f            (GskGLProgram          *self,
+                                                          guint                  key,
+                                                          float                  value0,
+                                                          float                  value1);
+void             gsk_gl_program_set_uniform3f            (GskGLProgram          *self,
+                                                          guint                  key,
+                                                          float                  value0,
+                                                          float                  value1,
+                                                          float                  value2);
+void             gsk_gl_program_set_uniform4f            (GskGLProgram          *self,
+                                                          guint                  key,
+                                                          float                  value0,
+                                                          float                  value1,
+                                                          float                  value2,
+                                                          float                  value3);
+void             gsk_gl_program_set_uniform_color        (GskGLProgram          *self,
+                                                          guint                  key,
+                                                          const GdkRGBA         *color);
+void             gsk_gl_program_set_uniform_texture      (GskGLProgram          *self,
+                                                          guint                  key,
+                                                          GLenum                 texture_target,
+                                                          GLenum                 texture_slot,
+                                                          guint                  texture_id);
+void             gsk_gl_program_set_uniform_rounded_rect (GskGLProgram          *self,
+                                                          guint                  key,
+                                                          const GskRoundedRect  *rounded_rect);
+void             gsk_gl_program_begin_draw               (GskGLProgram          *self);
+void             gsk_gl_program_end_draw                 (GskGLProgram          *self);
+GskGLDrawVertex *gsk_gl_program_add_vertices             (GskGLProgram          *self,
+                                                          const GskGLDrawVertex  
vertices[GSK_GL_N_VERTICES]);
 
 G_END_DECLS
 
diff --git a/gsk/next/gskglrenderjob.c b/gsk/next/gskglrenderjob.c
index 021865dc93..9694be4538 100644
--- a/gsk/next/gskglrenderjob.c
+++ b/gsk/next/gskglrenderjob.c
@@ -38,6 +38,7 @@
 struct _GskGLRenderJob
 {
   GskNextDriver     *driver;
+  GskGLCommandQueue *command_queue;
   cairo_region_t    *region;
   guint              framebuffer;
   graphene_rect_t    viewport;
@@ -334,6 +335,7 @@ gsk_gl_render_job_new (GskNextDriver         *driver,
 
   job = g_slice_new0 (GskGLRenderJob);
   job->driver = g_object_ref (driver);
+  job->command_queue = driver->command_queue;
   job->clip = g_array_new (FALSE, FALSE, sizeof (GskGLRenderClip));
   job->modelview = g_array_new (FALSE, FALSE, sizeof (GskGLRenderModelview));
   job->framebuffer = framebuffer;
@@ -442,11 +444,74 @@ gsk_gl_render_job_visit_node (GskGLRenderJob *job,
 {
   g_assert (job != NULL);
   g_assert (node != NULL);
+  g_assert (GSK_IS_NEXT_DRIVER (job->driver));
+  g_assert (GSK_IS_GL_COMMAND_QUEUE (job->command_queue));
 
   if (node_is_invisible (node) ||
       !gsk_gl_render_job_node_overlaps_clip (job, node))
     return;
 
+  switch (gsk_render_node_get_node_type (node))
+    {
+    case GSK_CONTAINER_NODE:
+      {
+        guint i;
+        guint p;
+
+        for (i = 0, p = gsk_container_node_get_n_children (node);
+             i < p;
+             i ++)
+          {
+            GskRenderNode *child = gsk_container_node_get_child (node, i);
+            gsk_gl_render_job_visit_node (job, child);
+          }
+      }
+    break;
+
+    case GSK_DEBUG_NODE:
+      {
+        const char *message = gsk_debug_node_get_message (node);
+
+        if (message != NULL)
+          gsk_gl_command_queue_push_debug_group (job->command_queue, message);
+
+        gsk_gl_render_job_visit_node (job, gsk_debug_node_get_child (node));
+
+        if (message != NULL)
+          gsk_gl_command_queue_pop_debug_group (job->command_queue);
+      }
+    break;
+
+    case GSK_CAIRO_NODE:
+    case GSK_COLOR_NODE:
+    case GSK_LINEAR_GRADIENT_NODE:
+    case GSK_REPEATING_LINEAR_GRADIENT_NODE:
+    case GSK_RADIAL_GRADIENT_NODE:
+    case GSK_REPEATING_RADIAL_GRADIENT_NODE:
+    case GSK_CONIC_GRADIENT_NODE:
+    case GSK_BORDER_NODE:
+    case GSK_TEXTURE_NODE:
+    case GSK_INSET_SHADOW_NODE:
+    case GSK_OUTSET_SHADOW_NODE:
+    case GSK_TRANSFORM_NODE:
+    case GSK_OPACITY_NODE:
+    case GSK_COLOR_MATRIX_NODE:
+    case GSK_REPEAT_NODE:
+    case GSK_CLIP_NODE:
+    case GSK_ROUNDED_CLIP_NODE:
+    case GSK_SHADOW_NODE:
+    case GSK_BLEND_NODE:
+    case GSK_CROSS_FADE_NODE:
+    case GSK_TEXT_NODE:
+    case GSK_BLUR_NODE:
+    case GSK_GL_SHADER_NODE:
+    break;
+
+    case GSK_NOT_A_RENDER_NODE:
+    default:
+      g_assert_not_reached ();
+    break;
+    }
 
 
 }
@@ -455,7 +520,6 @@ void
 gsk_gl_render_job_prepare (GskGLRenderJob *job,
                            GskRenderNode  *root)
 {
-  GskGLCommandQueue *command_queue;
   GdkGLContext *context;
 
   g_return_if_fail (job != NULL);
@@ -463,13 +527,12 @@ gsk_gl_render_job_prepare (GskGLRenderJob *job,
   g_return_if_fail (GSK_IS_NEXT_DRIVER (job->driver));
 
   context = gsk_next_driver_get_context (job->driver);
-  command_queue = job->driver->command_queue;
 
   gdk_gl_context_push_debug_group (context, "Adding render ops");
 
-  gsk_gl_command_queue_bind_framebuffer (command_queue, job->framebuffer);
-  gsk_gl_command_queue_set_viewport (command_queue, &job->viewport);
-  gsk_gl_command_queue_clear (command_queue);
+  gsk_gl_command_queue_bind_framebuffer (job->command_queue, job->framebuffer);
+  gsk_gl_command_queue_change_viewport (job->command_queue, &job->viewport);
+  gsk_gl_command_queue_clear (job->command_queue, 0);
 
   gsk_gl_render_job_visit_node (job, root);
 
@@ -483,6 +546,6 @@ gsk_gl_render_job_render (GskGLRenderJob *job)
   g_return_if_fail (GSK_IS_NEXT_DRIVER (job->driver));
 
   gsk_next_driver_begin_frame (job->driver);
-  gsk_gl_command_queue_execute (job->driver->command_queue);
+  gsk_gl_command_queue_execute (job->command_queue);
   gsk_next_driver_end_frame (job->driver);
 }
diff --git a/gsk/next/gskgltypes.h b/gsk/next/gskgltypes.h
index 232786438e..d4d48e74d4 100644
--- a/gsk/next/gskgltypes.h
+++ b/gsk/next/gskgltypes.h
@@ -28,6 +28,8 @@
 
 G_BEGIN_DECLS
 
+#define GSK_GL_N_VERTICES 6
+
 typedef struct _GskGLAttachmentState GskGLAttachmentState;
 typedef struct _GskGLCommandQueue GskGLCommandQueue;
 typedef struct _GskGLCompiler GskGLCompiler;


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