[gtk/wip/chergert/glproto: 542/920] stub out prototype bits for different GL renderer design
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/chergert/glproto: 542/920] stub out prototype bits for different GL renderer design
- Date: Mon, 8 Feb 2021 19:15:26 +0000 (UTC)
commit ca5631d34e9a30d130393e3c7e3191f9fa5e8a19
Author: Christian Hergert <chergert redhat com>
Date: Fri Dec 18 17:36:59 2020 -0800
stub out prototype bits for different GL renderer design
gsk/gskrenderer.c | 3 +
gsk/meson.build | 17 +-
gsk/next/gskglattachmentstate.c | 153 ++++
gsk/next/gskglattachmentstateprivate.h | 71 ++
gsk/next/gskglbuffer.c | 171 +++++
gsk/next/gskglbufferprivate.h | 41 ++
gsk/next/gskglcommandqueue.c | 1212 ++++++++++++++++++++++++++++++++
gsk/next/gskglcommandqueueprivate.h | 148 ++++
gsk/next/gskglcompiler.c | 661 +++++++++++++++++
gsk/next/gskglcompilerprivate.h | 69 ++
gsk/next/gskgldriver.c | 219 ++++++
gsk/next/gskgldriverprivate.h | 87 +++
gsk/next/gskglglyphlibrary.c | 50 ++
gsk/next/gskglglyphlibraryprivate.h | 53 ++
gsk/next/gskgliconlibrary.c | 50 ++
gsk/next/gskgliconlibraryprivate.h | 38 +
gsk/next/gskglprogram.c | 361 ++++++++++
gsk/next/gskglprogramprivate.h | 93 +++
gsk/next/gskglprograms.defs | 85 +++
gsk/next/gskglrenderer.c | 310 ++++++++
gsk/next/gskglrenderer.h | 47 ++
gsk/next/gskglrenderjob.c | 124 ++++
gsk/next/gskglrenderjobprivate.h | 36 +
gsk/next/gskglshadowlibrary.c | 50 ++
gsk/next/gskglshadowlibraryprivate.h | 42 ++
gsk/next/gskgltextureatlas.c | 46 ++
gsk/next/gskgltextureatlasprivate.h | 36 +
gsk/next/gskgltexturelibrary.c | 156 ++++
gsk/next/gskgltexturelibraryprivate.h | 61 ++
gsk/next/gskgltypes.h | 53 ++
gsk/next/gskgluniformstate.c | 746 ++++++++++++++++++++
gsk/next/gskgluniformstateprivate.h | 189 +++++
32 files changed, 5477 insertions(+), 1 deletion(-)
---
diff --git a/gsk/gskrenderer.c b/gsk/gskrenderer.c
index dbbfd28f35..c86dd98382 100644
--- a/gsk/gskrenderer.c
+++ b/gsk/gskrenderer.c
@@ -39,6 +39,7 @@
#include "gskcairorenderer.h"
#include "gskdebugprivate.h"
#include "gl/gskglrenderer.h"
+#include "next/gskglrenderer.h"
#include "gskprofilerprivate.h"
#include "gskrendernodeprivate.h"
@@ -496,6 +497,8 @@ get_renderer_for_name (const char *renderer_name)
else if (g_ascii_strcasecmp (renderer_name, "opengl") == 0
|| g_ascii_strcasecmp (renderer_name, "gl") == 0)
return GSK_TYPE_GL_RENDERER;
+ else if (g_ascii_strcasecmp (renderer_name, "next") == 0)
+ return GSK_TYPE_NEXT_RENDERER;
#ifdef GDK_RENDERING_VULKAN
else if (g_ascii_strcasecmp (renderer_name, "vulkan") == 0)
return GSK_TYPE_VULKAN_RENDERER;
diff --git a/gsk/meson.build b/gsk/meson.build
index 748f4738a1..8b63ccbb65 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -31,6 +31,7 @@ gsk_public_sources = files([
'gskroundedrect.c',
'gsktransform.c',
'gl/gskglrenderer.c',
+ 'next/gskglrenderer.c',
])
gsk_private_sources = files([
@@ -48,6 +49,19 @@ gsk_private_sources = files([
'gl/gskgliconcache.c',
'gl/opbuffer.c',
'gl/stb_rect_pack.c',
+ 'next/gskglattachmentstate.c',
+ 'next/gskglbuffer.c',
+ 'next/gskglcommandqueue.c',
+ 'next/gskglcompiler.c',
+ 'next/gskgldriver.c',
+ 'next/gskglglyphlibrary.c',
+ 'next/gskgliconlibrary.c',
+ 'next/gskglprogram.c',
+ 'next/gskglrenderjob.c',
+ 'next/gskglshadowlibrary.c',
+ 'next/gskgltextureatlas.c',
+ 'next/gskgltexturelibrary.c',
+ 'next/gskgluniformstate.c',
])
gsk_public_headers = files([
@@ -64,7 +78,8 @@ gsk_public_headers = files([
install_headers(gsk_public_headers, 'gsk.h', subdir: 'gtk-4.0/gsk')
gsk_public_gl_headers = files([
- 'gl/gskglrenderer.h'
+ 'gl/gskglrenderer.h',
+ 'next/gskglrenderer.h',
])
install_headers(gsk_public_gl_headers, subdir: 'gtk-4.0/gsk/gl')
gsk_public_headers += gsk_public_gl_headers
diff --git a/gsk/next/gskglattachmentstate.c b/gsk/next/gskglattachmentstate.c
new file mode 100644
index 0000000000..404bf3c11c
--- /dev/null
+++ b/gsk/next/gskglattachmentstate.c
@@ -0,0 +1,153 @@
+/* gskglattachmentstate.c
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include "gskglattachmentstateprivate.h"
+
+GskGLAttachmentState *
+gsk_gl_attachment_state_new (void)
+{
+ GskGLAttachmentState *self;
+
+ self = g_new0 (GskGLAttachmentState, 1);
+
+ self->fbo.changed = FALSE;
+ self->fbo.id = 0;
+
+ /* Initialize textures, assume we are 2D by default since it
+ * doesn't really matter until we bind something other than
+ * GL_TEXTURE0 to it anyway.
+ */
+ for (guint i = 0; i < G_N_ELEMENTS (self->textures); i++)
+ {
+ self->textures[i].target = GL_TEXTURE_2D;
+ self->textures[i].texture = GL_TEXTURE0;
+ self->textures[i].id = 0;
+ self->textures[i].changed = FALSE;
+ self->textures[i].initial = TRUE;
+ }
+
+ return self;
+}
+
+void
+gsk_gl_attachment_state_free (GskGLAttachmentState *self)
+{
+ g_free (self);
+}
+
+void
+gsk_gl_attachment_state_bind_texture (GskGLAttachmentState *self,
+ GLenum target,
+ GLenum texture,
+ guint id)
+{
+ GskGLBindTexture *attach;
+
+ g_assert (self != NULL);
+ g_assert (target == GL_TEXTURE_1D ||
+ target == GL_TEXTURE_2D ||
+ target == GL_TEXTURE_3D);
+ g_assert (texture >= GL_TEXTURE0 && texture <= GL_TEXTURE16);
+
+ attach = &self->textures[texture - GL_TEXTURE0];
+
+ if (attach->target != target || attach->texture != texture || attach->id != id)
+ {
+ attach->target = target;
+ attach->texture = texture;
+ attach->id = id;
+ attach->changed = TRUE;
+ attach->initial = FALSE;
+ self->has_texture_change = TRUE;
+ }
+}
+
+void
+gsk_gl_attachment_state_bind_framebuffer (GskGLAttachmentState *self,
+ guint id)
+{
+ g_assert (self != NULL);
+
+ if (self->fbo.id != id)
+ {
+ self->fbo.id = id;
+ self->fbo.changed = TRUE;
+ }
+}
+
+/**
+ * gsk_gl_attachment_state_save:
+ * @self: a #GskGLAttachmentState
+ *
+ * Creates a copy of @self that represents the current attachments
+ * as known to @self.
+ *
+ * This can be used to restore state later, such as after running
+ * various GL commands that are external to the GL renderer.
+ *
+ * This must be freed by calling either gsk_gl_attachment_state_free()
+ * or gsk_gl_attachment_state_restore().
+ *
+ * Returns: (transfer full): a new #GskGLAttachmentState or %NULL
+ */
+GskGLAttachmentState *
+gsk_gl_attachment_state_save (GskGLAttachmentState *self)
+{
+ GskGLAttachmentState *ret;
+
+ if (self == NULL)
+ return NULL;
+
+ ret = g_slice_dup (GskGLAttachmentState, self);
+ ret->fbo.changed = FALSE;
+ for (guint i = 0; i < G_N_ELEMENTS (ret->textures); i++)
+ ret->textures[i].changed = FALSE;
+
+ return g_steal_pointer (&ret);
+}
+
+/**
+ * gsk_gl_attachment_state_restore:
+ * @self: (transfer full): the #GskGLAttachmentState
+ *
+ * Restores the attachment state and frees @self.
+ */
+void
+gsk_gl_attachment_state_restore (GskGLAttachmentState *self)
+{
+ if (self == NULL)
+ return;
+
+ glBindFramebuffer (GL_FRAMEBUFFER, self->fbo.id);
+
+ for (guint i = 0; i < G_N_ELEMENTS (self->textures); i++)
+ {
+ if (!self->textures[i].initial)
+ {
+ glActiveTexture (GL_TEXTURE0 + i);
+ glBindTexture (self->textures[i].target,
+ self->textures[i].id);
+ }
+ }
+
+ g_slice_free (GskGLAttachmentState, self);
+}
diff --git a/gsk/next/gskglattachmentstateprivate.h b/gsk/next/gskglattachmentstateprivate.h
new file mode 100644
index 0000000000..fe2bf328d4
--- /dev/null
+++ b/gsk/next/gskglattachmentstateprivate.h
@@ -0,0 +1,71 @@
+/* gskglattachmentstateprivate.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GSK_GL_ATTACHMENT_STATE_PRIVATE_H__
+#define __GSK_GL_ATTACHMENT_STATE_PRIVATE_H__
+
+#include "gskgltypes.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskGLAttachmentState GskGLAttachmentState;
+typedef struct _GskGLBindFramebuffer GskGLBindFramebuffer;
+typedef struct _GskGLBindTexture GskGLBindTexture;
+
+struct _GskGLBindTexture
+{
+ guint changed : 1;
+ guint initial : 1;
+ GLenum target : 30;
+ GLenum texture;
+ guint id;
+};
+
+G_STATIC_ASSERT (sizeof (GskGLBindTexture) == 12);
+
+struct _GskGLBindFramebuffer
+{
+ guint changed : 1;
+ guint id : 31;
+};
+
+G_STATIC_ASSERT (sizeof (GskGLBindFramebuffer) == 4);
+
+struct _GskGLAttachmentState
+{
+ GskGLBindFramebuffer fbo;
+ GskGLBindTexture textures[8];
+ guint has_texture_change : 1;
+};
+
+GskGLAttachmentState *gsk_gl_attachment_state_new (void);
+GskGLAttachmentState *gsk_gl_attachment_state_save (GskGLAttachmentState *self);
+void gsk_gl_attachment_state_restore (GskGLAttachmentState *self);
+void gsk_gl_attachment_state_free (GskGLAttachmentState *self);
+void gsk_gl_attachment_state_bind_texture (GskGLAttachmentState *self,
+ GLenum target,
+ GLenum texture,
+ guint id);
+void gsk_gl_attachment_state_bind_framebuffer (GskGLAttachmentState *self,
+ guint id);
+
+G_END_DECLS
+
+#endif /* __GSK_GL_ATTACHMENT_STATE_PRIVATE_H__ */
diff --git a/gsk/next/gskglbuffer.c b/gsk/next/gskglbuffer.c
new file mode 100644
index 0000000000..913f5ebc8e
--- /dev/null
+++ b/gsk/next/gskglbuffer.c
@@ -0,0 +1,171 @@
+/* gskglbufferprivate.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <epoxy/gl.h>
+
+#include "gskglbufferprivate.h"
+
+#define N_BUFFERS 2
+#define RESERVED_SIZE 1024
+
+typedef struct
+{
+ GLuint id;
+ guint size_on_gpu;
+} GskGLBufferShadow;
+
+struct _GskGLBuffer
+{
+ GArray *buffer;
+ GskGLBufferShadow shadows[N_BUFFERS];
+ GLenum target;
+ guint current;
+};
+
+static void
+gsk_gl_buffer_shadow_init (GskGLBufferShadow *shadow,
+ GLenum target,
+ guint element_size,
+ guint reserved_size)
+{
+ GLuint id;
+
+ glGenBuffers (1, &id);
+ glBindBuffer (target, id);
+ glBufferData (target, element_size * reserved_size, NULL, GL_STATIC_DRAW);
+ glBindBuffer (target, 0);
+
+ shadow->id = id;
+ shadow->size_on_gpu = element_size * reserved_size;
+}
+
+static void
+gsk_gl_buffer_shadow_destroy (GskGLBufferShadow *shadow)
+{
+ shadow->size_on_gpu = 0;
+
+ if (shadow->id > 0)
+ {
+ glDeleteBuffers (1, &shadow->id);
+ shadow->id = 0;
+ }
+}
+
+static void
+gsk_gl_buffer_shadow_submit (GskGLBufferShadow *shadow,
+ GLenum target,
+ GArray *buffer)
+{
+ guint to_upload = buffer->len * g_array_get_element_size (buffer);
+
+ /* If what we generated is larger than our size on the GPU, then we need
+ * to release our previous buffer and create a new one of the appropriate
+ * size. We add some padding to make it more likely the next frame with this
+ * buffer does not need to do the same thing again. We also try to keep
+ * things aligned to the size of a whole (4096 byte) page.
+ */
+ if G_UNLIKELY (to_upload > shadow->size_on_gpu)
+ {
+ guint size_on_gpu = (to_upload & ~0xFFF) + (4 * 4096L);
+
+ glBindBuffer (target, 0);
+ glDeleteBuffers (1, &shadow->id);
+ glGenBuffers (1, &shadow->id);
+ glBindBuffer (target, shadow->id);
+ glBufferData (target, size_on_gpu, NULL, GL_STATIC_DRAW);
+ glBufferSubData (target, 0, to_upload, buffer->data);
+ shadow->size_on_gpu = size_on_gpu;
+ }
+ else
+ {
+ glBindBuffer (target, shadow->id);
+ glBufferSubData (target, 0, to_upload, buffer->data);
+ }
+}
+
+/**
+ * gsk_gl_buffer_new:
+ * @target: the target buffer such as %GL_ARRAY_BUFFER or %GL_UNIFORM_BUFFER
+ * @element_size: the size of elements within the buffer
+ *
+ * Creates a new #GskGLBuffer which can be used to deliver data to shaders
+ * within a GLSL program. You can use this to store vertices such as with
+ * %GL_ARRAY_BUFFER or uniform data with %GL_UNIFORM_BUFFER.
+ *
+ * Note that only writing to this buffer is allowed (see %GL_WRITE_ONLY for
+ * more details).
+ *
+ * The buffer will be bound to target upon returning from this function.
+ */
+GskGLBuffer *
+gsk_gl_buffer_new (GLenum target,
+ guint element_size)
+{
+ GskGLBuffer *buffer;
+ GLuint id = 0;
+
+ glGenBuffers (1, &id);
+
+ buffer = g_new0 (GskGLBuffer, 1);
+ buffer->buffer = g_array_sized_new (FALSE, FALSE, element_size, RESERVED_SIZE);
+ buffer->target = target;
+ buffer->current = 0;
+
+ for (guint i = 0; i < N_BUFFERS; i++)
+ gsk_gl_buffer_shadow_init (&buffer->shadows[i],
+ target,
+ element_size,
+ RESERVED_SIZE);
+
+ return g_steal_pointer (&buffer);
+}
+
+void
+gsk_gl_buffer_submit (GskGLBuffer *buffer)
+{
+ gsk_gl_buffer_shadow_submit (&buffer->shadows[buffer->current],
+ buffer->target,
+ buffer->buffer);
+ buffer->current = (buffer->current + 1) % N_BUFFERS;
+ buffer->buffer->len = 0;
+}
+
+void
+gsk_gl_buffer_free (GskGLBuffer *buffer)
+{
+ buffer->target = 0;
+ buffer->current = 0;
+ for (guint i = 0; i < N_BUFFERS; i++)
+ gsk_gl_buffer_shadow_destroy (&buffer->shadows[i]);
+ g_free (buffer);
+}
+
+gpointer
+gsk_gl_buffer_advance (GskGLBuffer *buffer,
+ guint count,
+ guint *offset)
+{
+
+ *offset = buffer->buffer->len;
+ g_array_set_size (buffer->buffer, buffer->buffer->len + count);
+ return (guint8 *)buffer->buffer->data + (*offset * g_array_get_element_size (buffer->buffer));
+}
diff --git a/gsk/next/gskglbufferprivate.h b/gsk/next/gskglbufferprivate.h
new file mode 100644
index 0000000000..2d88bb4995
--- /dev/null
+++ b/gsk/next/gskglbufferprivate.h
@@ -0,0 +1,41 @@
+/* gskglbufferprivate.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GSK_GL_BUFFER_PRIVATE_H__
+#define __GSK_GL_BUFFER_PRIVATE_H__
+
+#include <glib.h>
+#include <epoxy/gl.h>
+
+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);
+
+G_END_DECLS
+
+#endif /* __GSK_GL_BUFFER_PRIVATE_H__ */
diff --git a/gsk/next/gskglcommandqueue.c b/gsk/next/gskglcommandqueue.c
new file mode 100644
index 0000000000..fbef0fcee0
--- /dev/null
+++ b/gsk/next/gskglcommandqueue.c
@@ -0,0 +1,1212 @@
+/* gskglcommandqueue.c
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <gsk/gskdebugprivate.h>
+#include <epoxy/gl.h>
+
+#include "gskglattachmentstateprivate.h"
+#include "gskglbufferprivate.h"
+#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;
+
+ 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.
+ */
+ GQueue all_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.
+ */
+ GArray *program_batches;
+
+ /* The GskGLAttachmentState contains information about our FBO and texture
+ * attachments as we process incoming operations. We snapshot them into
+ * various batches so that we can compare differences between merge
+ * candidates.
+ */
+ GskGLAttachmentState *attachments;
+
+ /* The uniform state across all programs. We snapshot this into batches so
+ * that we can compare uniform state between batches to give us more
+ * chances at merging draw commands.
+ */
+ 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().
+ */
+ GskGLBuffer *vertices;
+
+ /* Sometimes we want to save attachment state so that operations we do
+ * cannot affect anything that is known to the command queue. We call
+ * gsk_gl_command_queue_save()/restore() which stashes attachment state
+ * into this pointer array.
+ */
+ GPtrArray *saved_state;
+
+ /* Sometimes it is handy to keep a number of textures or framebuffers
+ * around until the frame has finished drawing. That way some objects can
+ * be used immediately even though they won't have any rendering until the
+ * frame has finished.
+ *
+ * When end_frame is called, we remove these resources.
+ */
+ GArray *autorelease_framebuffers;
+ GArray *autorelease_textures;
+
+ int max_texture_size;
+};
+
+typedef struct _GskGLCommandDraw
+{
+ guint vao_offset;
+ guint vao_count;
+} GskGLCommandDraw;
+
+G_STATIC_ASSERT (sizeof (GskGLCommandDraw) == 8);
+
+typedef struct _GskGLCommandUniform
+{
+ guint offset;
+ guint array_count : 16;
+ guint location : 7;
+ guint format : 5;
+ guint flags : 4;
+} GskGLCommandUniform;
+
+G_STATIC_ASSERT (sizeof (GskGLCommandUniform) == 8);
+
+typedef struct _GskGLCommandBatch
+{
+ /* An index into GskGLCommandQueue.all_batches */
+ GList all_link;
+
+ /* An index into GskGLCommandBatch.program_batches */
+ GList program_link;
+
+ union {
+ GskGLBindTexture bind;
+ GskGLBindTexture *binds;
+ };
+
+ union {
+ GskGLCommandDraw draw;
+ GskGLCommandDraw *draws;
+ };
+
+ union {
+ GskGLCommandUniform uniform;
+ GskGLCommandUniform *uniforms;
+ };
+
+ guint program_changed : 1;
+ guint program : 15;
+ guint n_draws : 16;
+ guint n_binds : 16;
+ guint n_uniforms : 16;
+
+ /* The framebuffer to use and if it has changed */
+ GskGLBindFramebuffer framebuffer;
+} GskGLCommandBatch;
+
+G_DEFINE_TYPE (GskGLCommandQueue, gsk_gl_command_queue, G_TYPE_OBJECT)
+
+static inline gboolean
+ispow2 (guint n)
+{
+ return !(n & (n-1));
+}
+
+static GskGLCommandBatch *
+gsk_gl_command_queue_alloc_batch (GskGLCommandQueue *self)
+{
+ GskGLCommandBatch *batch;
+
+ g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ if G_LIKELY (self->unused_batches.length > 0)
+ return g_queue_pop_head_link (&self->unused_batches)->data;
+
+ batch = g_slice_new0 (GskGLCommandBatch);
+ batch->all_link.data = batch;
+ batch->program_link.data = batch;
+
+ return batch;
+}
+
+static void
+gsk_gl_command_queue_release_batch (GskGLCommandQueue *self,
+ GskGLCommandBatch *batch)
+{
+ g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+ g_assert (batch != NULL);
+
+ g_queue_unlink (&self->all_batches, &batch->all_link);
+
+ g_assert (batch->program ||
+ (batch->program_link.prev == NULL &&
+ batch->program_link.next == NULL));
+
+ 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);
+ }
+
+ if (batch->n_draws > 1)
+ g_free (batch->draws);
+
+ if (batch->n_binds > 1)
+ g_free (batch->binds);
+
+ 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;
+
+ 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_queue_push_head_link (&self->unused_batches, &batch->all_link);
+}
+
+static void
+gsk_gl_command_batch_apply_uniform (GskGLCommandBatch *batch,
+ GskGLUniformState *state,
+ const GskGLCommandUniform *uniform)
+{
+ 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;
+
+ default:
+ break;
+ }
+}
+
+static void
+gsk_gl_command_batch_draw (GskGLCommandBatch *batch,
+ guint vao_offset,
+ guint vao_count)
+{
+ 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);
+
+ 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;
+
+ 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);
+
+ batch->draws[batch->n_draws].vao_count = vao_count;
+ batch->draws[batch->n_draws].vao_offset = vao_offset;
+ batch->n_draws++;
+ }
+}
+
+static void
+gsk_gl_command_batch_execute (GskGLCommandBatch *batch,
+ GskGLUniformState *uniforms)
+{
+ 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);
+ }
+ }
+
+ 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 (draw->vao_count > 0);
+
+ glDrawArrays (GL_TRIANGLES, draw->vao_offset, draw->vao_count);
+ }
+ }
+}
+
+static void
+gsk_gl_command_batch_uniform_cb (const GskGLUniformInfo *info,
+ guint location,
+ gpointer user_data)
+{
+ GskGLCommandBatch *batch = user_data;
+ GskGLCommandUniform *u;
+
+ g_assert (batch != NULL);
+ g_assert (info != NULL);
+
+ 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++;
+ }
+
+ u->format = info->format;
+ u->flags = info->flags;
+ u->array_count = info->array_count;
+ u->location = location;
+ u->offset = info->offset;
+}
+
+static gboolean
+gsk_gl_command_batch_mergeable (GskGLCommandBatch *batch,
+ GskGLCommandBatch *other)
+{
+ g_assert (batch != NULL);
+ g_assert (other != NULL);
+ g_assert (batch != other);
+
+ if (batch->program != other->program)
+ return FALSE;
+
+ return FALSE;
+}
+
+static void
+gsk_gl_command_queue_try_merge (GskGLCommandQueue *self,
+ GskGLCommandBatch *batch)
+{
+ guint count = 0;
+
+ g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+ g_assert (batch != NULL);
+ g_assert (batch->program != 0);
+
+ /* 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.
+ */
+
+ for (const GList *iter = batch->program_link.prev;
+ iter != NULL && count < MAX_MERGE_DISTANCE;
+ iter = iter->prev, count++)
+ {
+ GskGLCommandBatch *predecessor = iter->data;
+
+ if (gsk_gl_command_batch_mergeable (predecessor, batch))
+ {
+ /* We need to check all the intermediates for overdrawing. */
+ }
+ }
+}
+
+static inline GskGLCommandBatch *
+gsk_gl_command_queue_get_batch (GskGLCommandQueue *self)
+{
+ g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ return self->all_batches.tail->data;
+}
+
+static GskGLCommandBatch *
+gsk_gl_command_queue_advance (GskGLCommandQueue *self,
+ guint new_program)
+{
+ GskGLCommandBatch *last_batch = NULL;
+ GskGLCommandBatch *batch;
+
+ g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ if (self->all_batches.length > 0)
+ {
+ 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);
+ }
+
+ batch = gsk_gl_command_queue_alloc_batch (self);
+
+ 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;
+ }
+
+ g_queue_push_tail_link (&self->all_batches, &batch->all_link);
+
+ if (batch->program)
+ {
+ GQueue *q;
+
+ if (self->program_batches->len <= batch->program)
+ g_array_set_size (self->program_batches, batch->program + 1);
+
+ q = &g_array_index (self->program_batches, GQueue, batch->program);
+ g_queue_push_tail_link (q, &batch->program_link);
+ }
+
+ if (last_batch != NULL)
+ gsk_gl_command_queue_try_merge (self, last_batch);
+
+ return g_steal_pointer (&batch);
+}
+
+static void
+gsk_gl_command_queue_dispose (GObject *object)
+{
+ GskGLCommandQueue *self = (GskGLCommandQueue *)object;
+
+ g_assert (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);
+ }
+
+ while (self->unused_batches.length > 0)
+ {
+ GskGLCommandBatch *batch = self->unused_batches.head->data;
+
+ g_queue_unlink (&self->unused_batches, self->unused_batches.head->data);
+ g_slice_free (GskGLCommandBatch, batch);
+ }
+
+#ifndef G_DISABLE_DEBUG
+ g_assert (self->unused_batches.length == 0);
+ g_assert (self->all_batches.length == 0);
+
+ 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
+
+ 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);
+}
+
+static void
+gsk_gl_command_queue_class_init (GskGLCommandQueueClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gsk_gl_command_queue_dispose;
+}
+
+static void
+gsk_gl_command_queue_init (GskGLCommandQueue *self)
+{
+ self->max_texture_size = -1;
+
+ 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));
+
+ gsk_gl_command_queue_advance (self, 0);
+}
+
+GskGLCommandQueue *
+gsk_gl_command_queue_new (GdkGLContext *context)
+{
+ GskGLCommandQueue *self;
+
+ g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+
+ self = g_object_new (GSK_TYPE_GL_COMMAND_QUEUE, NULL);
+ self->context = g_object_ref (context);
+
+ 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));
+ }
+
+ return g_steal_pointer (&self);
+}
+
+GdkGLContext *
+gsk_gl_command_queue_get_context (GskGLCommandQueue *self)
+{
+ g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self), NULL);
+
+ return self->context;
+}
+
+void
+gsk_gl_command_queue_make_current (GskGLCommandQueue *self)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+ g_return_if_fail (GDK_IS_GL_CONTEXT (self->context));
+
+ 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)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ glDeleteProgram (program);
+ 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,
+ guint location,
+ int value0)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ gsk_gl_uniform_state_set1i (self->uniforms, program, location, value0);
+}
+
+void
+gsk_gl_command_queue_set_uniform2i (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ int value0,
+ int value1)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ gsk_gl_uniform_state_set2i (self->uniforms, program, location, value0, value1);
+}
+
+void
+gsk_gl_command_queue_set_uniform3i (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ int value0,
+ int value1,
+ int value2)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ gsk_gl_uniform_state_set3i (self->uniforms, program, location, value0, value1, value2);
+}
+
+void
+gsk_gl_command_queue_set_uniform4i (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ int value0,
+ int value1,
+ int value2,
+ int value3)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ gsk_gl_uniform_state_set4i (self->uniforms, program, location, value0, value1, value2, value3);
+}
+
+void
+gsk_gl_command_queue_set_uniform1f (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ float value0)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ gsk_gl_uniform_state_set1f (self->uniforms, program, location, value0);
+}
+
+void
+gsk_gl_command_queue_set_uniform2f (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ float value0,
+ float value1)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ gsk_gl_uniform_state_set2f (self->uniforms, program, location, value0, value1);
+}
+
+void
+gsk_gl_command_queue_set_uniform3f (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ float value0,
+ float value1,
+ float value2)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ gsk_gl_uniform_state_set3f (self->uniforms, program, location, value0, value1, value2);
+}
+
+void
+gsk_gl_command_queue_set_uniform4f (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ float value0,
+ float value1,
+ float value2,
+ float value3)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ gsk_gl_uniform_state_set4f (self->uniforms, program, location, value0, value1, value2, value3);
+}
+
+void
+gsk_gl_command_queue_set_uniform2fv (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ gsize count,
+ const float *value)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ gsk_gl_uniform_state_set2fv (self->uniforms, program, location, count, value);
+}
+
+void
+gsk_gl_command_queue_set_uniform1fv (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ gsize count,
+ const float *value)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ gsk_gl_uniform_state_set1fv (self->uniforms, program, location, count, value);
+}
+
+void
+gsk_gl_command_queue_set_uniform4fv (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ gsize count,
+ const float *value)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ gsk_gl_uniform_state_set4fv (self->uniforms, program, location, count, value);
+}
+
+void
+gsk_gl_command_queue_set_uniform_matrix (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ const graphene_matrix_t *matrix)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ gsk_gl_uniform_state_set_matrix (self->uniforms, program, location, matrix);
+}
+
+void
+gsk_gl_command_queue_set_uniform_color (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ const GdkRGBA *color)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ 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.
+ */
+void
+gsk_gl_command_queue_set_uniform_texture (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ GLenum texture_target,
+ GLenum texture_slot,
+ guint texture_id)
+{
+ g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+ g_assert (program > 0);
+ g_assert (texture_target == GL_TEXTURE_1D ||
+ texture_target == GL_TEXTURE_2D ||
+ texture_target == GL_TEXTURE_3D);
+ g_assert (texture_slot >= GL_TEXTURE0);
+ g_assert (texture_slot < GL_TEXTURE16);
+
+ 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.
+ */
+void
+gsk_gl_command_queue_set_uniform_rounded_rect (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ const GskRoundedRect *rounded_rect)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+ g_return_if_fail (program > 0);
+ g_return_if_fail (rounded_rect != NULL);
+
+ gsk_gl_uniform_state_set_rounded_rect (self->uniforms,
+ program,
+ location,
+ rounded_rect);
+}
+
+/**
+ * gsk_gl_command_queue_execute:
+ * @self: a #GskGLCommandQueue
+ *
+ * Executes all of the batches in the command queue.
+ */
+static void
+gsk_gl_command_queue_execute (GskGLCommandQueue *self)
+{
+ GskGLCommandBatch *last_batch;
+ GLuint vao_id;
+
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ if (self->all_batches.length == 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);
+
+ glGenVertexArrays (1, &vao_id);
+ glBindVertexArray (vao_id);
+
+ gsk_gl_buffer_submit (self->vertices);
+
+ /* 0 = position location */
+ glEnableVertexAttribArray (0);
+ glVertexAttribPointer (0, 2, GL_FLOAT, GL_FALSE,
+ sizeof (GskGLDrawVertex),
+ (void *) G_STRUCT_OFFSET (GskGLDrawVertex, position));
+
+ /* 1 = texture coord location */
+ glEnableVertexAttribArray (1);
+ glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE,
+ sizeof (GskGLDrawVertex),
+ (void *) G_STRUCT_OFFSET (GskGLDrawVertex, uv));
+
+ for (const GList *iter = self->all_batches.head; iter != NULL; iter = iter->next)
+ {
+ GskGLCommandBatch *batch = self->all_batches.head->data;
+
+ if (batch->n_draws > 0)
+ gsk_gl_command_batch_execute (batch, self->uniforms);
+ }
+
+ glDeleteVertexArrays (1, &vao_id);
+}
+
+void
+gsk_gl_command_queue_begin_frame (GskGLCommandQueue *self)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ glBindFramebuffer (GL_FRAMEBUFFER, 0);
+
+ for (guint i = 0; i < 8; i++)
+ {
+ glActiveTexture (GL_TEXTURE0 + i);
+ glBindTexture (GL_TEXTURE_2D, 0);
+ }
+
+ glBindVertexArray (0);
+ glUseProgram (0);
+}
+
+/**
+ * gsk_gl_command_queue_end_frame:
+ * @self: a #GskGLCommandQueue
+ *
+ * This function performs cleanup steps that need to be done after
+ * a frame has finished. This is not performed as part of the command
+ * queue execution to allow for the frame to be submitted as soon
+ * as possible.
+ *
+ * However, it should be executed after the draw contexts end_frame
+ * has been called to swap the OpenGL framebuffers.
+ */
+void
+gsk_gl_command_queue_end_frame (GskGLCommandQueue *self)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+
+ gsk_gl_command_queue_execute (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);
+
+ /* Release autoreleased framebuffers */
+ if (self->autorelease_framebuffers->len > 0)
+ glDeleteFramebuffers (self->autorelease_framebuffers->len,
+ (GLuint *)(gpointer)self->autorelease_framebuffers->data);
+
+ /* Release autoreleased textures */
+ if (self->autorelease_textures->len > 0)
+ glDeleteTextures (self->autorelease_textures->len,
+ (GLuint *)(gpointer)self->autorelease_textures->data);
+}
+
+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);
+}
+
+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
+gsk_gl_command_queue_create_render_target (GskGLCommandQueue *self,
+ int width,
+ int height,
+ guint *out_fbo_id,
+ guint *out_texture_id)
+{
+ GLuint fbo_id = 0;
+ GLint texture_id;
+
+ g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self), FALSE);
+ g_return_val_if_fail (width > 0, FALSE);
+ g_return_val_if_fail (height > 0, FALSE);
+ g_return_val_if_fail (out_fbo_id != NULL, FALSE);
+ g_return_val_if_fail (out_texture_id != NULL, FALSE);
+
+ gsk_gl_command_queue_save (self);
+
+ texture_id = gsk_gl_command_queue_create_texture (self, width, height, GL_NEAREST, GL_NEAREST);
+
+ if (texture_id == -1)
+ {
+ *out_fbo_id = 0;
+ *out_texture_id = 0;
+ gsk_gl_command_queue_restore (self);
+ return FALSE;
+ }
+
+ fbo_id = gsk_gl_command_queue_create_framebuffer (self);
+
+ glBindFramebuffer (GL_FRAMEBUFFER, fbo_id);
+ glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_id, 0);
+ g_assert_cmphex (glCheckFramebufferStatus (GL_FRAMEBUFFER), ==, GL_FRAMEBUFFER_COMPLETE);
+
+ gsk_gl_command_queue_restore (self);
+
+ *out_fbo_id = fbo_id;
+ *out_texture_id = texture_id;
+
+ return TRUE;
+}
+
+void
+gsk_gl_command_queue_autorelease_framebuffer (GskGLCommandQueue *self,
+ guint framebuffer_id)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+ g_return_if_fail (framebuffer_id > 0);
+
+ g_array_append_val (self->autorelease_framebuffers, framebuffer_id);
+}
+
+void
+gsk_gl_command_queue_autorelease_texture (GskGLCommandQueue *self,
+ guint texture_id)
+{
+ g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (self));
+ g_return_if_fail (texture_id > 0);
+
+ g_array_append_val (self->autorelease_textures, texture_id);
+}
diff --git a/gsk/next/gskglcommandqueueprivate.h b/gsk/next/gskglcommandqueueprivate.h
new file mode 100644
index 0000000000..5e1c001bba
--- /dev/null
+++ b/gsk/next/gskglcommandqueueprivate.h
@@ -0,0 +1,148 @@
+/* gskglcommandqueueprivate.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GSK_GL_COMMAND_QUEUE_PRIVATE_H__
+#define __GSK_GL_COMMAND_QUEUE_PRIVATE_H__
+
+#include "gskgltypes.h"
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_GL_COMMAND_QUEUE (gsk_gl_command_queue_get_type())
+
+G_DECLARE_FINAL_TYPE (GskGLCommandQueue, gsk_gl_command_queue, GSK, GL_COMMAND_QUEUE, GObject)
+
+GskGLCommandQueue *gsk_gl_command_queue_new (GdkGLContext *context);
+GdkGLContext *gsk_gl_command_queue_get_context (GskGLCommandQueue *self);
+void gsk_gl_command_queue_make_current (GskGLCommandQueue *self);
+void gsk_gl_command_queue_begin_frame (GskGLCommandQueue *self);
+void gsk_gl_command_queue_end_frame (GskGLCommandQueue *self);
+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);
+int gsk_gl_command_queue_create_texture (GskGLCommandQueue *self,
+ int width,
+ int height,
+ int min_filter,
+ int mag_filter);
+guint gsk_gl_command_queue_create_framebuffer (GskGLCommandQueue *self);
+gboolean gsk_gl_command_queue_create_render_target (GskGLCommandQueue *self,
+ int width,
+ int height,
+ guint *out_fbo_id,
+ 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_set_uniform1i (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ int value0);
+void gsk_gl_command_queue_set_uniform2i (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ int value0,
+ int value1);
+void gsk_gl_command_queue_set_uniform3i (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ int value0,
+ int value1,
+ int value2);
+void gsk_gl_command_queue_set_uniform4i (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ int value0,
+ int value1,
+ int value2,
+ int value3);
+void gsk_gl_command_queue_set_uniform1f (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ float value0);
+void gsk_gl_command_queue_set_uniform2f (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ float value0,
+ float value1);
+void gsk_gl_command_queue_set_uniform3f (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ float value0,
+ float value1,
+ float value2);
+void gsk_gl_command_queue_set_uniform4f (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ float value0,
+ float value1,
+ float value2,
+ float value3);
+void gsk_gl_command_queue_set_uniform2fv (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ gsize count,
+ const float *value);
+void gsk_gl_command_queue_set_uniform1fv (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ gsize count,
+ const float *value);
+void gsk_gl_command_queue_set_uniform4fv (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ gsize count,
+ const float *value);
+void gsk_gl_command_queue_set_uniform_matrix (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ const graphene_matrix_t *matrix);
+void gsk_gl_command_queue_set_uniform_color (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ const GdkRGBA *color);
+void gsk_gl_command_queue_set_uniform_texture (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ GLenum texture_target,
+ GLenum texture_slot,
+ guint texture_id);
+void gsk_gl_command_queue_set_uniform_rounded_rect (GskGLCommandQueue *self,
+ guint program,
+ guint location,
+ const GskRoundedRect *rounded_rect);
+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]);
+
+G_END_DECLS
+
+#endif /* __GSK_GL_COMMAND_QUEUE_PRIVATE_H__ */
diff --git a/gsk/next/gskglcompiler.c b/gsk/next/gskglcompiler.c
new file mode 100644
index 0000000000..d5df9bc3b4
--- /dev/null
+++ b/gsk/next/gskglcompiler.c
@@ -0,0 +1,661 @@
+/* gskglcompiler.c
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <gsk/gskdebugprivate.h>
+#include <gio/gio.h>
+#include <epoxy/gl.h>
+#include <string.h>
+
+#include "gskglcommandqueueprivate.h"
+#include "gskglcompilerprivate.h"
+#include "gskglprogramprivate.h"
+
+#define SHADER_VERSION_GLES 100
+#define SHADER_VERSION_GL2_LEGACY 110
+#define SHADER_VERSION_GL3_LEGACY 130
+#define SHADER_VERSION_GL3 150
+
+struct _GskGLCompiler
+{
+ GObject parent_instance;
+
+ GskGLCommandQueue *command_queue;
+
+ GBytes *all_preamble;
+ GBytes *fragment_preamble;
+ GBytes *vertex_preamble;
+ GBytes *fragment_source;
+ GBytes *fragment_suffix;
+ GBytes *vertex_source;
+ GBytes *vertex_suffix;
+
+ GHashTable *attrib_locations;
+
+ int glsl_version;
+
+ guint gl3 : 1;
+ guint gles : 1;
+ guint legacy : 1;
+ guint debug_shaders : 1;
+};
+
+static GBytes *empty_bytes;
+
+G_DEFINE_TYPE (GskGLCompiler, gsk_gl_compiler, G_TYPE_OBJECT)
+
+static void
+gsk_gl_compiler_finalize (GObject *object)
+{
+ GskGLCompiler *self = (GskGLCompiler *)object;
+
+ g_clear_pointer (&self->all_preamble, g_bytes_unref);
+ g_clear_pointer (&self->fragment_preamble, g_bytes_unref);
+ g_clear_pointer (&self->vertex_preamble, g_bytes_unref);
+ g_clear_pointer (&self->vertex_suffix, g_bytes_unref);
+ g_clear_pointer (&self->fragment_source, g_bytes_unref);
+ g_clear_pointer (&self->fragment_suffix, g_bytes_unref);
+ g_clear_pointer (&self->vertex_source, g_bytes_unref);
+ g_clear_pointer (&self->attrib_locations, g_hash_table_unref);
+ g_clear_object (&self->command_queue);
+
+ G_OBJECT_CLASS (gsk_gl_compiler_parent_class)->finalize (object);
+}
+
+static void
+gsk_gl_compiler_class_init (GskGLCompilerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gsk_gl_compiler_finalize;
+
+ empty_bytes = g_bytes_new (NULL, 0);
+}
+
+static void
+gsk_gl_compiler_init (GskGLCompiler *self)
+{
+ self->glsl_version = 150;
+ self->attrib_locations = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ self->all_preamble = g_bytes_ref (empty_bytes);
+ self->vertex_preamble = g_bytes_ref (empty_bytes);
+ self->fragment_preamble = g_bytes_ref (empty_bytes);
+ self->vertex_source = g_bytes_ref (empty_bytes);
+ self->vertex_suffix = g_bytes_ref (empty_bytes);
+ self->fragment_source = g_bytes_ref (empty_bytes);
+ self->fragment_suffix = g_bytes_ref (empty_bytes);
+}
+
+GskGLCompiler *
+gsk_gl_compiler_new (GskGLCommandQueue *command_queue,
+ gboolean debug_shaders)
+{
+ GskGLCompiler *self;
+ GdkGLContext *context;
+
+ g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (command_queue), NULL);
+
+ self = g_object_new (GSK_TYPE_GL_COMPILER, NULL);
+ self->command_queue = g_object_ref (command_queue);
+ self->debug_shaders = !!debug_shaders;
+
+ context = gsk_gl_command_queue_get_context (command_queue);
+
+ if (gdk_gl_context_get_use_es (context))
+ {
+ self->glsl_version = SHADER_VERSION_GLES;
+ self->gles = TRUE;
+ }
+ else if (gdk_gl_context_is_legacy (context))
+ {
+ int maj, min;
+
+ gdk_gl_context_get_version (context, &maj, &min);
+
+ if (maj == 3)
+ self->glsl_version = SHADER_VERSION_GL3_LEGACY;
+ else
+ self->glsl_version = SHADER_VERSION_GL2_LEGACY;
+
+ self->legacy = TRUE;
+ }
+ else
+ {
+ self->glsl_version = SHADER_VERSION_GL3;
+ self->gl3 = TRUE;
+ }
+
+ gsk_gl_command_queue_make_current (command_queue);
+
+ return g_steal_pointer (&self);
+}
+
+void
+gsk_gl_compiler_bind_attribute (GskGLCompiler *self,
+ const char *name,
+ guint location)
+{
+ g_return_if_fail (GSK_IS_GL_COMPILER (self));
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (location < 32);
+
+ g_hash_table_insert (self->attrib_locations,
+ g_strdup (name),
+ GUINT_TO_POINTER (location));
+}
+
+void
+gsk_gl_compiler_clear_attributes (GskGLCompiler *self)
+{
+ g_return_if_fail (GSK_IS_GL_COMPILER (self));
+
+ g_hash_table_remove_all (self->attrib_locations);
+}
+
+void
+gsk_gl_compiler_set_preamble (GskGLCompiler *self,
+ GskGLCompilerKind kind,
+ GBytes *preamble_bytes)
+{
+ GBytes **loc = NULL;
+
+ g_return_if_fail (GSK_IS_GL_COMPILER (self));
+ g_return_if_fail (preamble_bytes != NULL);
+
+ if (kind == GSK_GL_COMPILER_ALL)
+ loc = &self->all_preamble;
+ else if (kind == GSK_GL_COMPILER_FRAGMENT)
+ loc = &self->fragment_preamble;
+ else if (kind == GSK_GL_COMPILER_VERTEX)
+ loc = &self->vertex_preamble;
+ else
+ g_return_if_reached ();
+
+ g_assert (loc != NULL);
+
+ if (*loc != preamble_bytes)
+ {
+ g_clear_pointer (loc, g_bytes_unref);
+ *loc = preamble_bytes ? g_bytes_ref (preamble_bytes) : NULL;
+ }
+}
+
+void
+gsk_gl_compiler_set_preamble_from_resource (GskGLCompiler *self,
+ GskGLCompilerKind kind,
+ const char *resource_path)
+{
+ GError *error = NULL;
+ GBytes *bytes;
+
+ g_return_if_fail (GSK_IS_GL_COMPILER (self));
+ g_return_if_fail (kind == GSK_GL_COMPILER_ALL ||
+ kind == GSK_GL_COMPILER_VERTEX ||
+ kind == GSK_GL_COMPILER_FRAGMENT);
+ g_return_if_fail (resource_path != NULL);
+
+ bytes = g_resources_lookup_data (resource_path,
+ G_RESOURCE_LOOKUP_FLAGS_NONE,
+ &error);
+
+ if (bytes == NULL)
+ g_warning ("Cannot set shader from resource: %s", error->message);
+ else
+ gsk_gl_compiler_set_preamble (self, kind, bytes);
+
+ g_clear_pointer (&bytes, g_bytes_unref);
+ g_clear_error (&error);
+}
+
+void
+gsk_gl_compiler_set_source (GskGLCompiler *self,
+ GskGLCompilerKind kind,
+ GBytes *source_bytes)
+{
+ GBytes **loc = NULL;
+
+ g_return_if_fail (GSK_IS_GL_COMPILER (self));
+ g_return_if_fail (kind == GSK_GL_COMPILER_ALL ||
+ kind == GSK_GL_COMPILER_VERTEX ||
+ kind == GSK_GL_COMPILER_FRAGMENT);
+
+ if (source_bytes == NULL)
+ source_bytes = empty_bytes;
+
+ /* If kind is ALL, then we need to split the fragment and
+ * vertex shaders from the bytes and assign them individually.
+ * This safely scans for FRAGMENT_SHADER and VERTEX_SHADER as
+ * specified within the GLSL resources. Some care is taken to
+ * use GBytes which reference the original bytes instead of
+ * copying them.
+ */
+ if (kind == GSK_GL_COMPILER_ALL)
+ {
+ gsize len = 0;
+ const char *source;
+ const char *vertex_shader_start;
+ const char *fragment_shader_start;
+ const char *endpos;
+ GBytes *fragment_bytes;
+ GBytes *vertex_bytes;
+
+ source = g_bytes_get_data (source_bytes, &len);
+ endpos = source + len;
+ vertex_shader_start = g_strstr_len (source, len, "VERTEX_SHADER");
+ fragment_shader_start = g_strstr_len (source, len, "FRAGMENT_SHADER");
+
+ if (vertex_shader_start == NULL)
+ {
+ g_warning ("Failed to locate VERTEX_SHADER in shader source");
+ return;
+ }
+
+ if (fragment_shader_start == NULL)
+ {
+ g_warning ("Failed to locate FRAGMENT_SHADER in shader source");
+ return;
+ }
+
+ if (vertex_shader_start > fragment_shader_start)
+ {
+ g_warning ("VERTEX_SHADER must come before FRAGMENT_SHADER");
+ return;
+ }
+
+ /* Locate next newlines */
+ while (vertex_shader_start < endpos && vertex_shader_start[0] != '\n')
+ vertex_shader_start++;
+ while (fragment_shader_start < endpos && fragment_shader_start[0] != '\n')
+ fragment_shader_start++;
+
+ vertex_bytes = g_bytes_new_from_bytes (source_bytes,
+ vertex_shader_start - source,
+ fragment_shader_start - vertex_shader_start);
+ fragment_bytes = g_bytes_new_from_bytes (source_bytes,
+ fragment_shader_start - source,
+ endpos - fragment_shader_start);
+
+ gsk_gl_compiler_set_source (self, GSK_GL_COMPILER_VERTEX, vertex_bytes);
+ gsk_gl_compiler_set_source (self, GSK_GL_COMPILER_FRAGMENT, fragment_bytes);
+
+ g_bytes_unref (fragment_bytes);
+ g_bytes_unref (vertex_bytes);
+
+ return;
+ }
+
+ if (kind == GSK_GL_COMPILER_FRAGMENT)
+ loc = &self->fragment_source;
+ else if (kind == GSK_GL_COMPILER_VERTEX)
+ loc = &self->vertex_source;
+ else
+ g_return_if_reached ();
+
+ if (*loc != source_bytes)
+ {
+ g_clear_pointer (loc, g_bytes_unref);
+ *loc = g_bytes_ref (source_bytes);
+ }
+}
+
+void
+gsk_gl_compiler_set_source_from_resource (GskGLCompiler *self,
+ GskGLCompilerKind kind,
+ const char *resource_path)
+{
+ GError *error = NULL;
+ GBytes *bytes;
+
+ g_return_if_fail (GSK_IS_GL_COMPILER (self));
+ g_return_if_fail (kind == GSK_GL_COMPILER_ALL ||
+ kind == GSK_GL_COMPILER_VERTEX ||
+ kind == GSK_GL_COMPILER_FRAGMENT);
+ g_return_if_fail (resource_path != NULL);
+
+ bytes = g_resources_lookup_data (resource_path,
+ G_RESOURCE_LOOKUP_FLAGS_NONE,
+ &error);
+
+ if (bytes == NULL)
+ g_warning ("Cannot set shader from resource: %s", error->message);
+ else
+ gsk_gl_compiler_set_source (self, kind, bytes);
+
+ g_clear_pointer (&bytes, g_bytes_unref);
+ g_clear_error (&error);
+}
+
+void
+gsk_gl_compiler_set_suffix (GskGLCompiler *self,
+ GskGLCompilerKind kind,
+ GBytes *suffix_bytes)
+{
+ GBytes **loc;
+
+ g_return_if_fail (GSK_IS_GL_COMPILER (self));
+ g_return_if_fail (kind == GSK_GL_COMPILER_VERTEX ||
+ kind == GSK_GL_COMPILER_FRAGMENT);
+ g_return_if_fail (suffix_bytes != NULL);
+
+ if (suffix_bytes == NULL)
+ suffix_bytes = empty_bytes;
+
+ if (kind == GSK_GL_COMPILER_FRAGMENT)
+ loc = &self->fragment_suffix;
+ else if (kind == GSK_GL_COMPILER_VERTEX)
+ loc = &self->vertex_suffix;
+ else
+ g_return_if_reached ();
+
+ if (*loc != suffix_bytes)
+ {
+ g_clear_pointer (loc, g_bytes_unref);
+ *loc = g_bytes_ref (suffix_bytes);
+ }
+}
+
+void
+gsk_gl_compiler_set_suffix_from_resource (GskGLCompiler *self,
+ GskGLCompilerKind kind,
+ const char *resource_path)
+{
+ GError *error = NULL;
+ GBytes *bytes;
+
+ g_return_if_fail (GSK_IS_GL_COMPILER (self));
+ g_return_if_fail (kind == GSK_GL_COMPILER_VERTEX ||
+ kind == GSK_GL_COMPILER_FRAGMENT);
+ g_return_if_fail (resource_path != NULL);
+
+ bytes = g_resources_lookup_data (resource_path,
+ G_RESOURCE_LOOKUP_FLAGS_NONE,
+ &error);
+
+ if (bytes == NULL)
+ g_warning ("Cannot set suffix from resource: %s", error->message);
+ else
+ gsk_gl_compiler_set_suffix (self, kind, bytes);
+
+ g_clear_pointer (&bytes, g_bytes_unref);
+ g_clear_error (&error);
+}
+
+static void
+prepend_line_numbers (char *code,
+ GString *s)
+{
+ char *p;
+ int line;
+
+ p = code;
+ line = 1;
+ while (*p)
+ {
+ char *end = strchr (p, '\n');
+ if (end)
+ end = end + 1; /* Include newline */
+ else
+ end = p + strlen (p);
+
+ g_string_append_printf (s, "%3d| ", line++);
+ g_string_append_len (s, p, end - p);
+
+ p = end;
+ }
+}
+
+static gboolean
+check_shader_error (int shader_id,
+ GError **error)
+{
+ GLint status;
+ GLint log_len;
+ GLint code_len;
+ char *buffer;
+ char *code;
+ GString *s;
+
+ glGetShaderiv (shader_id, GL_COMPILE_STATUS, &status);
+
+ if G_LIKELY (status == GL_TRUE)
+ return TRUE;
+
+ glGetShaderiv (shader_id, GL_INFO_LOG_LENGTH, &log_len);
+ buffer = g_malloc0 (log_len + 1);
+ glGetShaderInfoLog (shader_id, log_len, NULL, buffer);
+
+ glGetShaderiv (shader_id, GL_SHADER_SOURCE_LENGTH, &code_len);
+ code = g_malloc0 (code_len + 1);
+ glGetShaderSource (shader_id, code_len, NULL, code);
+
+ s = g_string_new ("");
+ prepend_line_numbers (code, s);
+
+ g_set_error (error,
+ GDK_GL_ERROR,
+ GDK_GL_ERROR_COMPILATION_FAILED,
+ "Compilation failure in shader.\n"
+ "Source Code: %s\n"
+ "\n"
+ "Error Message:\n"
+ "%s\n"
+ "\n",
+ s->str,
+ buffer);
+
+ g_string_free (s, TRUE);
+ g_free (buffer);
+ g_free (code);
+
+ return FALSE;
+}
+
+static void
+print_shader_info (const char *prefix,
+ int shader_id,
+ const char *name)
+{
+ if (GSK_DEBUG_CHECK(SHADERS))
+ {
+ int code_len;
+
+ glGetShaderiv (shader_id, GL_SHADER_SOURCE_LENGTH, &code_len);
+
+ if (code_len > 0)
+ {
+ char *code;
+ GString *s;
+
+ code = g_malloc0 (code_len + 1);
+ glGetShaderSource (shader_id, code_len, NULL, code);
+
+ s = g_string_new (NULL);
+ prepend_line_numbers (code, s);
+
+ g_message ("%s %d, %s:\n%s",
+ prefix, shader_id,
+ name ? name : "unnamed",
+ s->str);
+ g_string_free (s, TRUE);
+ g_free (code);
+ }
+ }
+}
+
+static const char *
+get_shader_string (GBytes *bytes)
+{
+ /* 0 length bytes will give us NULL back */
+ const char *str = g_bytes_get_data (bytes, NULL);
+ return str ? str : "";
+}
+
+GskGLProgram *
+gsk_gl_compiler_compile (GskGLCompiler *self,
+ const char *name,
+ GError **error)
+{
+ char version[32];
+ const char *debug = "";
+ const char *legacy = "";
+ const char *gl3 = "";
+ const char *gles = "";
+ const char *key;
+ gpointer value;
+ GHashTableIter iter;
+ int program_id;
+ int vertex_id;
+ int fragment_id;
+ int status;
+
+ g_return_val_if_fail (GSK_IS_GL_COMPILER (self), NULL);
+ g_return_val_if_fail (self->all_preamble != NULL, NULL);
+ g_return_val_if_fail (self->fragment_preamble != NULL, NULL);
+ g_return_val_if_fail (self->vertex_preamble != NULL, NULL);
+ g_return_val_if_fail (self->fragment_source != NULL, NULL);
+ g_return_val_if_fail (self->vertex_source != NULL, NULL);
+ g_return_val_if_fail (self->command_queue != NULL, NULL);
+
+ gsk_gl_command_queue_make_current (self->command_queue);
+
+ g_snprintf (version, sizeof version, "#version %d\n", self->glsl_version);
+
+ if (self->debug_shaders)
+ debug = "#define GSK_DEBUG 1\n";
+
+ if (self->legacy)
+ legacy = "#define GSK_LEGACY 1\n";
+
+ if (self->gles)
+ gles = "#define GSK_GLES 1\n";
+
+ if (self->gl3)
+ gl3 = "#define GSK_GL3 1\n";
+
+ vertex_id = glCreateShader (GL_VERTEX_SHADER);
+ glShaderSource (vertex_id,
+ 9,
+ (const char *[]) {
+ version, debug, legacy, gl3, gles,
+ get_shader_string (self->all_preamble),
+ get_shader_string (self->vertex_preamble),
+ get_shader_string (self->vertex_source),
+ get_shader_string (self->vertex_suffix),
+ },
+ (int[]) {
+ strlen (version),
+ strlen (debug),
+ strlen (legacy),
+ strlen (gl3),
+ strlen (gles),
+ g_bytes_get_size (self->all_preamble),
+ g_bytes_get_size (self->vertex_preamble),
+ g_bytes_get_size (self->vertex_source),
+ 0,
+ //g_bytes_get_size (self->vertex_suffix),
+ });
+ glCompileShader (vertex_id);
+
+ if (!check_shader_error (vertex_id, error))
+ {
+ glDeleteShader (vertex_id);
+ return NULL;
+ }
+
+ print_shader_info ("Vertex shader", vertex_id, name);
+
+ fragment_id = glCreateShader (GL_FRAGMENT_SHADER);
+ glShaderSource (fragment_id,
+ 9,
+ (const char *[]) {
+ version, debug, legacy, gl3, gles,
+ get_shader_string (self->all_preamble),
+ get_shader_string (self->fragment_preamble),
+ get_shader_string (self->fragment_source),
+ get_shader_string (self->fragment_suffix),
+ },
+ (int[]) {
+ strlen (version),
+ strlen (debug),
+ strlen (legacy),
+ strlen (gl3),
+ strlen (gles),
+ g_bytes_get_size (self->all_preamble),
+ g_bytes_get_size (self->fragment_preamble),
+ g_bytes_get_size (self->fragment_source),
+ g_bytes_get_size (self->fragment_suffix),
+ });
+ glCompileShader (fragment_id);
+
+ if (!check_shader_error (fragment_id, error))
+ {
+ glDeleteShader (vertex_id);
+ glDeleteShader (fragment_id);
+ return 0;
+ }
+
+ print_shader_info ("Fragment shader", fragment_id, name);
+
+ program_id = glCreateProgram ();
+ glAttachShader (program_id, vertex_id);
+ glAttachShader (program_id, fragment_id);
+ g_hash_table_iter_init (&iter, self->attrib_locations);
+ while (g_hash_table_iter_next (&iter, (gpointer *)&key, &value))
+ glBindAttribLocation (program_id, GPOINTER_TO_UINT (value), key);
+ glLinkProgram (program_id);
+
+ glDeleteShader (fragment_id);
+ glDeleteShader (vertex_id);
+
+ glGetProgramiv (program_id, GL_LINK_STATUS, &status);
+
+ if (status == GL_FALSE)
+ {
+ char *buffer = NULL;
+ int log_len = 0;
+
+ glGetProgramiv (program_id, GL_INFO_LOG_LENGTH, &log_len);
+
+ if (log_len > 0)
+ {
+ /* log_len includes NULL */
+ buffer = g_malloc0 (log_len);
+ glGetProgramInfoLog (program_id, log_len, NULL, buffer);
+ }
+
+ g_warning ("Linking failure in shader:\n%s",
+ buffer ? buffer : "");
+
+ g_set_error (error,
+ GDK_GL_ERROR,
+ GDK_GL_ERROR_LINK_FAILED,
+ "Linking failure in shader: %s",
+ buffer ? buffer : "");
+
+ g_free (buffer);
+
+ glDeleteProgram (program_id);
+
+ return NULL;
+ }
+
+ return gsk_gl_program_new (self->command_queue, name, program_id);
+}
diff --git a/gsk/next/gskglcompilerprivate.h b/gsk/next/gskglcompilerprivate.h
new file mode 100644
index 0000000000..d28c48c0b9
--- /dev/null
+++ b/gsk/next/gskglcompilerprivate.h
@@ -0,0 +1,69 @@
+/* gskglcompilerprivate.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GSK_GL_COMPILER_PRIVATE_H__
+#define __GSK_GL_COMPILER_PRIVATE_H__
+
+#include "gskgltypes.h"
+
+G_BEGIN_DECLS
+
+typedef enum _GskGLCompilerKind
+{
+ GSK_GL_COMPILER_ALL,
+ GSK_GL_COMPILER_FRAGMENT,
+ GSK_GL_COMPILER_VERTEX,
+} GskGLCompilerKind;
+
+#define GSK_TYPE_GL_COMPILER (gsk_gl_compiler_get_type())
+
+G_DECLARE_FINAL_TYPE (GskGLCompiler, gsk_gl_compiler, GSK, GL_COMPILER, GObject)
+
+GskGLCompiler *gsk_gl_compiler_new (GskGLCommandQueue *command_queue,
+ gboolean debug);
+void gsk_gl_compiler_set_preamble (GskGLCompiler *self,
+ GskGLCompilerKind kind,
+ GBytes *preamble_bytes);
+void gsk_gl_compiler_set_preamble_from_resource (GskGLCompiler *self,
+ GskGLCompilerKind kind,
+ const char *resource_path);
+void gsk_gl_compiler_set_source (GskGLCompiler *self,
+ GskGLCompilerKind kind,
+ GBytes *source_bytes);
+void gsk_gl_compiler_set_source_from_resource (GskGLCompiler *self,
+ GskGLCompilerKind kind,
+ const char *resource_path);
+void gsk_gl_compiler_set_suffix (GskGLCompiler *self,
+ GskGLCompilerKind kind,
+ GBytes *suffix_bytes);
+void gsk_gl_compiler_set_suffix_from_resource (GskGLCompiler *self,
+ GskGLCompilerKind kind,
+ const char *resource_path);
+void gsk_gl_compiler_bind_attribute (GskGLCompiler *self,
+ const char *name,
+ guint location);
+void gsk_gl_compiler_clear_attributes (GskGLCompiler *self);
+GskGLProgram *gsk_gl_compiler_compile (GskGLCompiler *self,
+ const char *name,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __GSK_GL_COMPILER_PRIVATE_H__ */
diff --git a/gsk/next/gskgldriver.c b/gsk/next/gskgldriver.c
new file mode 100644
index 0000000000..0fde92af53
--- /dev/null
+++ b/gsk/next/gskgldriver.c
@@ -0,0 +1,219 @@
+/* gskgldriver.c
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <gsk/gskdebugprivate.h>
+#include <gsk/gskrendererprivate.h>
+
+#include "gskglcommandqueueprivate.h"
+#include "gskglcompilerprivate.h"
+#include "gskgldriverprivate.h"
+#include "gskglglyphlibraryprivate.h"
+#include "gskgliconlibraryprivate.h"
+#include "gskglprogramprivate.h"
+#include "gskglshadowlibraryprivate.h"
+
+G_DEFINE_TYPE (GskNextDriver, gsk_next_driver, G_TYPE_OBJECT)
+
+static void
+gsk_next_driver_dispose (GObject *object)
+{
+ GskNextDriver *self = (GskNextDriver *)object;
+
+#define GSK_GL_NO_UNIFORMS
+#define GSK_GL_ADD_UNIFORM(pos, KEY, name)
+#define GSK_GL_DEFINE_PROGRAM(name, resource, uniforms) \
+ G_STMT_START { \
+ if (self->name) \
+ gsk_gl_program_delete (self->name); \
+ g_clear_object (&self->name); \
+ } G_STMT_END;
+# include "gskglprograms.defs"
+#undef GSK_GL_NO_UNIFORMS
+#undef GSK_GL_ADD_UNIFORM
+#undef GSK_GL_DEFINE_PROGRAM
+
+ g_clear_object (&self->command_queue);
+
+ G_OBJECT_CLASS (gsk_next_driver_parent_class)->dispose (object);
+}
+
+static void
+gsk_next_driver_class_init (GskNextDriverClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gsk_next_driver_dispose;
+}
+
+static void
+gsk_next_driver_init (GskNextDriver *self)
+{
+}
+
+static gboolean
+gsk_next_driver_load_programs (GskNextDriver *self,
+ GError **error)
+{
+ GskGLCompiler *compiler;
+ gboolean ret = FALSE;
+
+ g_assert (GSK_IS_NEXT_DRIVER (self));
+ g_assert (GSK_IS_GL_COMMAND_QUEUE (self->command_queue));
+
+ compiler = gsk_gl_compiler_new (self->command_queue, self->debug);
+
+ /* Setup preambles that are shared by all shaders */
+ gsk_gl_compiler_set_preamble_from_resource (compiler,
+ GSK_GL_COMPILER_ALL,
+ "/org/gtk/libgsk/glsl/preamble.glsl");
+ gsk_gl_compiler_set_preamble_from_resource (compiler,
+ GSK_GL_COMPILER_VERTEX,
+ "/org/gtk/libgsk/glsl/preamble.vs.glsl");
+ gsk_gl_compiler_set_preamble_from_resource (compiler,
+ GSK_GL_COMPILER_FRAGMENT,
+ "/org/gtk/libgsk/glsl/preamble.fs.glsl");
+
+ /* Setup attributes that are provided via VBO */
+ gsk_gl_compiler_bind_attribute (compiler, "aPosition", 0);
+ gsk_gl_compiler_bind_attribute (compiler, "vUv", 1);
+
+ /* Use XMacros to register all of our programs and their uniforms */
+#define GSK_GL_NO_UNIFORMS
+#define GSK_GL_ADD_UNIFORM(pos, KEY, name) \
+ gsk_gl_program_add_uniform (program, #name, UNIFORM_##KEY);
+#define GSK_GL_DEFINE_PROGRAM(name, resource, uniforms) \
+ G_STMT_START { \
+ GskGLProgram *program; \
+ gboolean have_alpha; \
+ \
+ gsk_gl_compiler_set_source_from_resource (compiler, GSK_GL_COMPILER_ALL, resource); \
+ \
+ if (!(program = gsk_gl_compiler_compile (compiler, #name, error))) \
+ goto failure; \
+ \
+ have_alpha = gsk_gl_program_add_uniform (program, "u_alpha", UNIFORM_SHARED_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); \
+ \
+ uniforms \
+ \
+ if (have_alpha) \
+ gsk_gl_program_set_uniform1f (program, UNIFORM_SHARED_ALPHA, 1.0f); \
+ \
+ *(GskGLProgram **)(((guint8 *)self) + G_STRUCT_OFFSET (GskNextDriver, name)) = \
+ g_steal_pointer (&program); \
+ } G_STMT_END;
+# include "gskglprograms.defs"
+#undef GSK_GL_DEFINE_PROGRAM
+#undef GSK_GL_ADD_UNIFORM
+
+ ret = TRUE;
+
+failure:
+ g_clear_object (&compiler);
+
+ return ret;
+}
+
+GskNextDriver *
+gsk_next_driver_new (GskGLCommandQueue *command_queue,
+ gboolean debug,
+ GError **error)
+{
+ GskNextDriver *self;
+ GdkGLContext *context;
+
+ g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (command_queue), NULL);
+
+ context = gsk_gl_command_queue_get_context (command_queue);
+
+ gdk_gl_context_make_current (context);
+
+ self = g_object_new (GSK_TYPE_NEXT_DRIVER, NULL);
+ self->command_queue = g_object_ref (command_queue);
+ self->debug = !!debug;
+
+ if (!gsk_next_driver_load_programs (self, error))
+ {
+ g_object_unref (self);
+ return NULL;
+ }
+
+ self->glyphs = gsk_gl_glyph_library_new (context);
+ self->icons = gsk_gl_icon_library_new (context);
+ self->shadows = gsk_gl_shadow_library_new (context);
+
+ return g_steal_pointer (&self);
+}
+
+void
+gsk_next_driver_begin_frame (GskNextDriver *self)
+{
+ g_return_if_fail (GSK_IS_NEXT_DRIVER (self));
+ g_return_if_fail (self->in_frame == FALSE);
+
+ self->in_frame = TRUE;
+
+ gsk_gl_command_queue_begin_frame (self->command_queue);
+}
+
+void
+gsk_next_driver_end_frame (GskNextDriver *self)
+{
+ g_return_if_fail (GSK_IS_NEXT_DRIVER (self));
+ g_return_if_fail (self->in_frame == TRUE);
+
+ gsk_gl_command_queue_end_frame (self->command_queue);
+
+ self->in_frame = FALSE;
+}
+
+GdkGLContext *
+gsk_next_driver_get_context (GskNextDriver *self)
+{
+ g_return_val_if_fail (GSK_IS_NEXT_DRIVER (self), NULL);
+ g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self->command_queue), NULL);
+
+ return gsk_gl_command_queue_get_context (self->command_queue);
+}
+
+gboolean
+gsk_next_driver_create_render_target (GskNextDriver *self,
+ int width,
+ int height,
+ guint *out_fbo_id,
+ guint *out_texture_id)
+{
+ g_return_val_if_fail (GSK_IS_NEXT_DRIVER (self), FALSE);
+
+ if (self->command_queue == NULL)
+ return FALSE;
+
+ return gsk_gl_command_queue_create_render_target (self->command_queue,
+ width,
+ height,
+ out_fbo_id,
+ out_texture_id);
+}
diff --git a/gsk/next/gskgldriverprivate.h b/gsk/next/gskgldriverprivate.h
new file mode 100644
index 0000000000..b7c5a852b5
--- /dev/null
+++ b/gsk/next/gskgldriverprivate.h
@@ -0,0 +1,87 @@
+/* gskgldriverprivate.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GSK_GL_DRIVER_PRIVATE_H__
+#define __GSK_GL_DRIVER_PRIVATE_H__
+
+#include "gskgltypes.h"
+
+G_BEGIN_DECLS
+
+enum {
+ UNIFORM_SHARED_ALPHA,
+ UNIFORM_SHARED_SOURCE,
+ UNIFORM_SHARED_CLIP_RECT,
+ UNIFORM_SHARED_VIEWPORT,
+ UNIFORM_SHARED_PROJECTION,
+ UNIFORM_SHARED_MODELVIEW,
+
+ UNIFORM_SHARED_LAST
+};
+
+#define GSL_GK_NO_UNIFORMS UNIFORM_INVALID_##__COUNTER__
+#define GSK_GL_ADD_UNIFORM(pos, KEY, name) UNIFORM_##KEY = UNIFORM_SHARED_LAST + pos,
+#define GSK_GL_DEFINE_PROGRAM(name, resource, uniforms) enum { uniforms };
+# include "gskglprograms.defs"
+#undef GSK_GL_DEFINE_PROGRAM
+#undef GSK_GL_ADD_UNIFORM
+#undef GSL_GK_NO_UNIFORMS
+
+#define GSK_TYPE_NEXT_DRIVER (gsk_next_driver_get_type())
+
+G_DECLARE_FINAL_TYPE (GskNextDriver, gsk_next_driver, GSK, NEXT_DRIVER, GObject)
+
+struct _GskNextDriver
+{
+ GObject parent_instance;
+
+ GskGLCommandQueue *command_queue;
+
+ GskGLGlyphLibrary *glyphs;
+ GskGLIconLibrary *icons;
+ GskGLShadowLibrary *shadows;
+
+#define GSK_GL_NO_UNIFORMS
+#define GSK_GL_ADD_UNIFORM(pos, KEY, name)
+#define GSK_GL_DEFINE_PROGRAM(name, resource, uniforms) GskGLProgram *name;
+# include "gskglprograms.defs"
+#undef GSK_GL_NO_UNIFORMS
+#undef GSK_GL_ADD_UNIFORM
+#undef GSK_GL_DEFINE_PROGRAM
+
+ guint debug : 1;
+ guint in_frame : 1;
+};
+
+GskNextDriver *gsk_next_driver_new (GskGLCommandQueue *command_queue,
+ gboolean debug,
+ GError **error);
+GdkGLContext *gsk_next_driver_get_context (GskNextDriver *self);
+gboolean gsk_next_driver_create_render_target (GskNextDriver *self,
+ int width,
+ int height,
+ guint *out_fbo_id,
+ guint *out_texture_id);
+void gsk_next_driver_begin_frame (GskNextDriver *driver);
+void gsk_next_driver_end_frame (GskNextDriver *driver);
+
+G_END_DECLS
+
+#endif /* __GSK_GL_DRIVER_PRIVATE_H__ */
diff --git a/gsk/next/gskglglyphlibrary.c b/gsk/next/gskglglyphlibrary.c
new file mode 100644
index 0000000000..ed40f1af7f
--- /dev/null
+++ b/gsk/next/gskglglyphlibrary.c
@@ -0,0 +1,50 @@
+/* gskglglyphlibrary.c
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include "gskglglyphlibraryprivate.h"
+
+struct _GskGLGlyphLibrary
+{
+ GskGLTextureLibrary parent_instance;
+};
+
+G_DEFINE_TYPE (GskGLGlyphLibrary, gsk_gl_glyph_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
+
+GskGLGlyphLibrary *
+gsk_gl_glyph_library_new (GdkGLContext *context)
+{
+ g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+
+ return g_object_new (GSK_TYPE_GL_GLYPH_LIBRARY,
+ "context", context,
+ NULL);
+}
+
+static void
+gsk_gl_glyph_library_class_init (GskGLGlyphLibraryClass *klass)
+{
+}
+
+static void
+gsk_gl_glyph_library_init (GskGLGlyphLibrary *self)
+{
+}
diff --git a/gsk/next/gskglglyphlibraryprivate.h b/gsk/next/gskglglyphlibraryprivate.h
new file mode 100644
index 0000000000..1929bc9ad4
--- /dev/null
+++ b/gsk/next/gskglglyphlibraryprivate.h
@@ -0,0 +1,53 @@
+/* gskglglyphlibraryprivate.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GSK_GL_GLYPH_LIBRARY_PRIVATE_H__
+#define __GSK_GL_GLYPH_LIBRARY_PRIVATE_H__
+
+#include <pango/pango.h>
+
+#include "gskgltexturelibraryprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskGLGlyphKey
+{
+ PangoFont *font;
+ PangoGlyph glyph;
+ guint xshift : 3;
+ guint yshift : 3;
+ guint scale : 26; /* times 1024 */
+} GskGLGlyphKey;
+
+#if GLIB_SIZEOF_VOID_P == 8
+G_STATIC_ASSERT (sizeof (GskGLGlyphKey) == 16);
+#elif GLIB_SIZEOF_VOID_P == 4
+G_STATIC_ASSERT (sizeof (GskGLGlyphKey) == 12);
+#endif
+
+#define GSK_TYPE_GL_GLYPH_LIBRARY (gsk_gl_glyph_library_get_type())
+
+G_DECLARE_FINAL_TYPE (GskGLGlyphLibrary, gsk_gl_glyph_library, GSK, GL_GLYPH_LIBRARY, GskGLTextureLibrary)
+
+GskGLGlyphLibrary *gsk_gl_glyph_library_new (GdkGLContext *context);
+
+G_END_DECLS
+
+#endif /* __GSK_GL_GLYPH_LIBRARY_PRIVATE_H__ */
diff --git a/gsk/next/gskgliconlibrary.c b/gsk/next/gskgliconlibrary.c
new file mode 100644
index 0000000000..64e800d115
--- /dev/null
+++ b/gsk/next/gskgliconlibrary.c
@@ -0,0 +1,50 @@
+/* gskgliconlibrary.c
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include "gskgliconlibraryprivate.h"
+
+struct _GskGLIconLibrary
+{
+ GskGLTextureLibrary parent_instance;
+};
+
+G_DEFINE_TYPE (GskGLIconLibrary, gsk_gl_icon_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
+
+GskGLIconLibrary *
+gsk_gl_icon_library_new (GdkGLContext *context)
+{
+ g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+
+ return g_object_new (GSK_TYPE_GL_ICON_LIBRARY,
+ "context", context,
+ NULL);
+}
+
+static void
+gsk_gl_icon_library_class_init (GskGLIconLibraryClass *klass)
+{
+}
+
+static void
+gsk_gl_icon_library_init (GskGLIconLibrary *self)
+{
+}
diff --git a/gsk/next/gskgliconlibraryprivate.h b/gsk/next/gskgliconlibraryprivate.h
new file mode 100644
index 0000000000..c6b4363e33
--- /dev/null
+++ b/gsk/next/gskgliconlibraryprivate.h
@@ -0,0 +1,38 @@
+/* gskgliconlibraryprivate.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GSK_GL_ICON_LIBRARY_PRIVATE_H__
+#define __GSK_GL_ICON_LIBRARY_PRIVATE_H__
+
+#include <pango/pango.h>
+
+#include "gskgltexturelibraryprivate.h"
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_GL_ICON_LIBRARY (gsk_gl_icon_library_get_type())
+
+G_DECLARE_FINAL_TYPE (GskGLIconLibrary, gsk_gl_icon_library, GSK, GL_ICON_LIBRARY, GskGLTextureLibrary)
+
+GskGLIconLibrary *gsk_gl_icon_library_new (GdkGLContext *context);
+
+G_END_DECLS
+
+#endif /* __GSK_GL_ICON_LIBRARY_PRIVATE_H__ */
diff --git a/gsk/next/gskglprogram.c b/gsk/next/gskglprogram.c
new file mode 100644
index 0000000000..2c13bb0b65
--- /dev/null
+++ b/gsk/next/gskglprogram.c
@@ -0,0 +1,361 @@
+/* gskglprogram.c
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include "gskglcommandqueueprivate.h"
+#include "gskglprogramprivate.h"
+#include "gskgluniformstateprivate.h"
+
+struct _GskGLProgram
+{
+ GObject parent_instance;
+ int id;
+ char *name;
+ GArray *uniform_locations;
+ GskGLCommandQueue *command_queue;
+};
+
+G_DEFINE_TYPE (GskGLProgram, gsk_gl_program, G_TYPE_OBJECT)
+
+GskGLProgram *
+gsk_gl_program_new (GskGLCommandQueue *command_queue,
+ const char *name,
+ int program_id)
+{
+ GskGLProgram *self;
+
+ g_return_val_if_fail (program_id >= 0, NULL);
+
+ self = g_object_new (GSK_TYPE_GL_PROGRAM, NULL);
+ self->id = program_id;
+ self->name = g_strdup (name);
+ self->command_queue = g_object_ref (command_queue);
+
+ return self;
+}
+
+static void
+gsk_gl_program_finalize (GObject *object)
+{
+ GskGLProgram *self = (GskGLProgram *)object;
+
+ if (self->id >= 0)
+ g_warning ("Leaking GLSL program %d (%s)",
+ self->id,
+ self->name ? self->name : "");
+
+ g_clear_pointer (&self->name, g_free);
+ g_clear_pointer (&self->uniform_locations, g_array_unref);
+ g_clear_object (&self->command_queue);
+
+ G_OBJECT_CLASS (gsk_gl_program_parent_class)->finalize (object);
+}
+
+static void
+gsk_gl_program_class_init (GskGLProgramClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gsk_gl_program_finalize;
+}
+
+static void
+gsk_gl_program_init (GskGLProgram *self)
+{
+ self->id = -1;
+ 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
+ * @name: the name of the uniform such as "u_source"
+ * @key: the identifier to use for the uniform
+ *
+ * This method will create a mapping between @key and the location
+ * of the uniform on the GPU. This simplifies calling code to not
+ * need to know where the uniform location is and only register it
+ * when creating the program.
+ *
+ * You might use this with an enum of all your uniforms for the
+ * program and then register each of them like:
+ *
+ * ```
+ * gsk_gl_program_add_uniform (program, "u_source", UNIFORM_SOURCE);
+ * ```
+ *
+ * That allows you to set values for the program with something
+ * like the following:
+ *
+ * ```
+ * gsk_gl_program_set_uniform1i (program, UNIFORM_SOURCE, 1);
+ * ```
+ *
+ * Returns: %TRUE if the uniform was found; otherwise %FALSE
+ */
+gboolean
+gsk_gl_program_add_uniform (GskGLProgram *self,
+ const char *name,
+ guint key)
+{
+ const GLint invalid = -1;
+ GLint location;
+
+ g_return_val_if_fail (GSK_IS_GL_PROGRAM (self), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+ g_return_val_if_fail (key < 1024, FALSE);
+
+ if (-1 == (location = glGetUniformLocation (self->id, name)))
+ return FALSE;
+
+ while (key >= self->uniform_locations->len)
+ g_array_append_val (self->uniform_locations, invalid);
+ g_array_index (self->uniform_locations, GLint, key) = location;
+
+ return TRUE;
+}
+
+static inline GLint
+get_uniform_location (GskGLProgram *self,
+ guint key)
+{
+ if G_LIKELY (key < self->uniform_locations->len)
+ return g_array_index (self->uniform_locations, GLint, key);
+ else
+ return -1;
+}
+
+/**
+ * gsk_gl_program_delete:
+ * @self: a #GskGLProgram
+ *
+ * Deletes the GLSL program.
+ *
+ * You must call gsk_gl_program_use() before and
+ * gsk_gl_program_unuse() after this function.
+ */
+void
+gsk_gl_program_delete (GskGLProgram *self)
+{
+ g_return_if_fail (GSK_IS_GL_PROGRAM (self));
+ g_return_if_fail (self->command_queue != NULL);
+
+ gsk_gl_command_queue_delete_program (self->command_queue, self->id);
+ self->id = -1;
+}
+
+void
+gsk_gl_program_set_uniform1i (GskGLProgram *self,
+ guint key,
+ int value0)
+{
+ gsk_gl_command_queue_set_uniform1i (self->command_queue,
+ self->id,
+ get_uniform_location (self, key),
+ value0);
+}
+
+void
+gsk_gl_program_set_uniform2i (GskGLProgram *self,
+ guint key,
+ int value0,
+ int value1)
+{
+ gsk_gl_command_queue_set_uniform2i (self->command_queue,
+ self->id,
+ get_uniform_location (self, key),
+ value0,
+ value1);
+}
+
+void
+gsk_gl_program_set_uniform3i (GskGLProgram *self,
+ guint key,
+ int value0,
+ int value1,
+ int value2)
+{
+ gsk_gl_command_queue_set_uniform3i (self->command_queue,
+ self->id,
+ get_uniform_location (self, key),
+ value0,
+ value1,
+ value2);
+}
+
+void
+gsk_gl_program_set_uniform4i (GskGLProgram *self,
+ guint key,
+ int value0,
+ int value1,
+ int value2,
+ int value3)
+{
+ gsk_gl_command_queue_set_uniform4i (self->command_queue,
+ self->id,
+ get_uniform_location (self, key),
+ value0,
+ value1,
+ value2,
+ value3);
+}
+
+void
+gsk_gl_program_set_uniform1f (GskGLProgram *self,
+ guint key,
+ float value0)
+{
+ gsk_gl_command_queue_set_uniform1f (self->command_queue,
+ self->id,
+ get_uniform_location (self, key),
+ value0);
+}
+
+void
+gsk_gl_program_set_uniform2f (GskGLProgram *self,
+ guint key,
+ float value0,
+ float value1)
+{
+ gsk_gl_command_queue_set_uniform2f (self->command_queue,
+ self->id,
+ get_uniform_location (self, key),
+ value0,
+ value1);
+}
+
+void
+gsk_gl_program_set_uniform3f (GskGLProgram *self,
+ guint key,
+ float value0,
+ float value1,
+ float value2)
+{
+ gsk_gl_command_queue_set_uniform3f (self->command_queue,
+ self->id,
+ get_uniform_location (self, key),
+ value0,
+ value1,
+ value2);
+}
+
+void
+gsk_gl_program_set_uniform4f (GskGLProgram *self,
+ guint key,
+ float value0,
+ float value1,
+ float value2,
+ float value3)
+{
+ gsk_gl_command_queue_set_uniform4f (self->command_queue,
+ self->id,
+ get_uniform_location (self, key),
+ value0,
+ value1,
+ value2,
+ value3);
+}
+
+void
+gsk_gl_program_set_uniform_color (GskGLProgram *self,
+ guint key,
+ const GdkRGBA *color)
+{
+ g_assert (GSK_IS_GL_PROGRAM (self));
+
+ gsk_gl_command_queue_set_uniform_color (self->command_queue,
+ self->id,
+ get_uniform_location (self, key),
+ color);
+}
+
+void
+gsk_gl_program_set_uniform_texture (GskGLProgram *self,
+ guint key,
+ GLenum texture_target,
+ GLenum texture_slot,
+ guint texture_id)
+{
+ g_assert (GSK_IS_GL_PROGRAM (self));
+ g_assert (texture_target == GL_TEXTURE_1D ||
+ texture_target == GL_TEXTURE_2D ||
+ texture_target == GL_TEXTURE_3D);
+ g_assert (texture_slot >= GL_TEXTURE0);
+ g_assert (texture_slot <= GL_TEXTURE16);
+
+ gsk_gl_command_queue_set_uniform_texture (self->command_queue,
+ self->id,
+ get_uniform_location (self, key),
+ texture_target,
+ texture_slot,
+ texture_id);
+}
+
+void
+gsk_gl_program_set_uniform_rounded_rect (GskGLProgram *self,
+ guint key,
+ const GskRoundedRect *rounded_rect)
+{
+ g_assert (GSK_IS_GL_PROGRAM (self));
+
+ gsk_gl_command_queue_set_uniform_rounded_rect (self->command_queue,
+ self->id,
+ get_uniform_location (self, key),
+ rounded_rect);
+}
+
+GskGLDrawVertex *
+gsk_gl_program_draw (GskGLProgram *self,
+ const GskGLDrawVertex vertices[6])
+{
+ g_assert (GSK_IS_GL_PROGRAM (self));
+
+ return gsk_gl_command_queue_draw (self->command_queue, vertices);
+}
diff --git a/gsk/next/gskglprogramprivate.h b/gsk/next/gskglprogramprivate.h
new file mode 100644
index 0000000000..e223a5263a
--- /dev/null
+++ b/gsk/next/gskglprogramprivate.h
@@ -0,0 +1,93 @@
+/* gskglprogramprivate.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GSK_GL_PROGRAM_H__
+#define __GSK_GL_PROGRAM_H__
+
+#include "gskgltypes.h"
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_GL_PROGRAM (gsk_gl_program_get_type())
+
+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]);
+
+G_END_DECLS
+
+#endif /* __GSK_GL_PROGRAM_H__ */
diff --git a/gsk/next/gskglprograms.defs b/gsk/next/gskglprograms.defs
new file mode 100644
index 0000000000..1e075bea81
--- /dev/null
+++ b/gsk/next/gskglprograms.defs
@@ -0,0 +1,85 @@
+GSK_GL_DEFINE_PROGRAM (blend,
+ "/org/gtk/libgsk/glsl/blend.glsl",
+ GSK_GL_ADD_UNIFORM (1, BLEND_SOURCE2, u_source2)
+ GSK_GL_ADD_UNIFORM (2, BLEND_MODE, u_mode))
+
+GSK_GL_DEFINE_PROGRAM (blit,
+ "/org/gtk/libgsk/glsl/blit.glsl",
+ GSK_GL_NO_UNIFORMS)
+
+GSK_GL_DEFINE_PROGRAM (blur,
+ "/org/gtk/libgsk/glsl/blur.glsl",
+ GSK_GL_ADD_UNIFORM (1, BLUR_RADIUS, u_blur_radius)
+ GSK_GL_ADD_UNIFORM (2, BLUR_SIZE, u_blur_size)
+ GSK_GL_ADD_UNIFORM (3, BLUR_DIR, u_blur_dir))
+
+GSK_GL_DEFINE_PROGRAM (border,
+ "/org/gtk/libgsk/glsl/border.glsl",
+ GSK_GL_ADD_UNIFORM (1, BORDER_COLOR, u_color)
+ GSK_GL_ADD_UNIFORM (2, BORDER_WIDTHS, u_widths)
+ GSK_GL_ADD_UNIFORM (3, BORDER_OUTLINE_RECT, u_outline_rect))
+
+GSK_GL_DEFINE_PROGRAM (color,
+ "/org/gtk/libgsk/glsl/color.glsl",
+ GSK_GL_ADD_UNIFORM (1, COLOR_COLOR, u_color))
+
+GSK_GL_DEFINE_PROGRAM (coloring,
+ "/org/gtk/libgsk/glsl/coloring.glsl",
+ GSK_GL_ADD_UNIFORM (1, COLORING_COLOR, u_color))
+
+GSK_GL_DEFINE_PROGRAM (color_matrix,
+ "/org/gtk/libgsk/glsl/color_matrix.glsl",
+ GSK_GL_ADD_UNIFORM (1, COLOR_MATRIX_COLOR_MATRIX, u_color_matrix)
+ GSK_GL_ADD_UNIFORM (2, COLOR_MATRIX_COLOR_OFFSET, u_color_offset))
+
+GSK_GL_DEFINE_PROGRAM (conic_gradient,
+ "/org/gtk/libgsk/glsl/conic_gradient.glsl",
+ GSK_GL_ADD_UNIFORM (1, CONIC_GRADIENT_COLOR_STOPS, u_color_stops)
+ GSK_GL_ADD_UNIFORM (2, CONIC_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops)
+ GSK_GL_ADD_UNIFORM (3, CONIC_GRADIENT_CENTER, u_center)
+ GSK_GL_ADD_UNIFORM (4, CONIC_GRADIENT_ROTATION, u_rotation))
+
+GSK_GL_DEFINE_PROGRAM (cross_fade,
+ "/org/gtk/libgsk/glsl/cross_fade.glsl",
+ GSK_GL_ADD_UNIFORM (1, CROSS_FADE_PROGRESS, u_progress)
+ GSK_GL_ADD_UNIFORM (2, CROSS_FADE_SOURCE2, u_source2))
+
+GSK_GL_DEFINE_PROGRAM (inset_shadow,
+ "/org/gtk/libgsk/glsl/inset_shadow.glsl",
+ GSK_GL_ADD_UNIFORM (1, INSET_SHADOW_COLOR, u_color)
+ GSK_GL_ADD_UNIFORM (2, INSET_SHADOW_SPREAD, u_spread)
+ GSK_GL_ADD_UNIFORM (3, INSET_SHADOW_OFFSET, u_offset)
+ GSK_GL_ADD_UNIFORM (4, INSET_SHADOW_OUTLINE_RECT, u_outline_rect))
+
+GSK_GL_DEFINE_PROGRAM (linear_gradient,
+ "/org/gtk/libgsk/glsl/linear_gradient.glsl",
+ GSK_GL_ADD_UNIFORM (1, LINEAR_GRADIENT_COLOR_STOPS, u_color_stops)
+ GSK_GL_ADD_UNIFORM (2, LINEAR_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops)
+ GSK_GL_ADD_UNIFORM (3, LINEAR_GRADIENT_START_POINT, u_start_point)
+ GSK_GL_ADD_UNIFORM (4, LINEAR_GRADIENT_END_POINT, u_end_point))
+
+GSK_GL_DEFINE_PROGRAM (outset_shadow,
+ "/org/gtk/libgsk/glsl/outset_shadow.glsl",
+ GSK_GL_ADD_UNIFORM (1, OUTSET_SHADOW_COLOR, u_color)
+ GSK_GL_ADD_UNIFORM (2, OUTSET_SHADOW_OUTLINE_RECT, u_outline_rect))
+
+GSK_GL_DEFINE_PROGRAM (radial_gradient,
+ "/org/gtk/libgsk/glsl/radial_gradient.glsl",
+ GSK_GL_ADD_UNIFORM (1, RADIAL_GRADIENT_COLOR_STOPS, u_color_stops)
+ GSK_GL_ADD_UNIFORM (2, RADIAL_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops)
+ GSK_GL_ADD_UNIFORM (3, RADIAL_GRADIENT_CENTER, u_center)
+ GSK_GL_ADD_UNIFORM (4, RADIAL_GRADIENT_START, u_start)
+ GSK_GL_ADD_UNIFORM (5, RADIAL_GRADIENT_END, u_end)
+ GSK_GL_ADD_UNIFORM (6, RADIAL_GRADIENT_RADIUS, u_radius))
+
+GSK_GL_DEFINE_PROGRAM (repeat,
+ "/org/gtk/libgsk/glsl/repeat.glsl",
+ GSK_GL_ADD_UNIFORM (1, REPEAT_CHILD_BOUNDS, u_child_bounds)
+ GSK_GL_ADD_UNIFORM (2, REPEAT_EXTURE_RECT, u_texture_rect))
+
+GSK_GL_DEFINE_PROGRAM (unblurred_outset_shadow,
+ "/org/gtk/libgsk/glsl/unblurred_outset_shadow.glsl",
+ GSK_GL_ADD_UNIFORM (1, UNBLURRED_OUTSET_SHADOW_COLOR, u_color)
+ GSK_GL_ADD_UNIFORM (1, UNBLURRED_OUTSET_SHADOW_SPREAD, u_spread)
+ GSK_GL_ADD_UNIFORM (1, UNBLURRED_OUTSET_SHADOW_OFFSET, u_offset)
+ GSK_GL_ADD_UNIFORM (2, UNBLURRED_OUTSET_SHADOW_OUTLINE_RECT, u_outline_rect))
diff --git a/gsk/next/gskglrenderer.c b/gsk/next/gskglrenderer.c
new file mode 100644
index 0000000000..19c67e671e
--- /dev/null
+++ b/gsk/next/gskglrenderer.c
@@ -0,0 +1,310 @@
+/* gskglrenderer.c
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <gsk/gskdebugprivate.h>
+#include <gsk/gskrendererprivate.h>
+
+#include "gskglcommandqueueprivate.h"
+#include "gskgldriverprivate.h"
+#include "gskglprogramprivate.h"
+#include "gskglrenderjobprivate.h"
+#include "gskglrenderer.h"
+
+struct _GskNextRendererClass
+{
+ GskRendererClass parent_class;
+};
+
+struct _GskNextRenderer
+{
+ GskRenderer parent_instance;
+
+ /* The GskGLCommandQueue manages how we send all drawing operations,
+ * uniform changes, and other texture related operations to the GPU.
+ * It maintains a cache of state to help reduce the number of state
+ * changes we send to the GPU. Furthermore, it can reorder batches so
+ * that we switch programs and uniforms less frequently by verifying
+ * what batches can be reordered based on clipping and stacking.
+ */
+ GskGLCommandQueue *command_queue;
+
+ /* The driver manages our program state and command queues. It gives
+ * us a single place to manage loading all the programs as well which
+ * unfortunately cannot be shared across all renderers for a display.
+ * (Context sharing explicitly does not name program/uniform state as
+ * shareable even though on some implementations it is).
+ */
+ GskNextDriver *driver;
+};
+
+G_DEFINE_TYPE (GskNextRenderer, gsk_next_renderer, GSK_TYPE_RENDERER)
+
+GskRenderer *
+gsk_next_renderer_new (void)
+{
+ return g_object_new (GSK_TYPE_NEXT_RENDERER, NULL);
+}
+
+static gboolean
+gsk_next_renderer_realize (GskRenderer *renderer,
+ GdkSurface *surface,
+ GError **error)
+{
+ GskNextRenderer *self = (GskNextRenderer *)renderer;
+ GskGLCommandQueue *command_queue = NULL;
+ GdkGLContext *context = NULL;
+ GskNextDriver *driver = NULL;
+ gboolean ret = FALSE;
+ gboolean debug = FALSE;
+
+ g_assert (GSK_IS_NEXT_RENDERER (self));
+ g_assert (GDK_IS_SURFACE (surface));
+
+ if (!(context = gdk_surface_create_gl_context (surface, error)) ||
+ !gdk_gl_context_realize (context, error))
+ goto failure;
+
+ gdk_gl_context_make_current (context);
+
+ command_queue = gsk_gl_command_queue_new (context);
+
+#ifdef G_ENABLE_DEBUG
+ if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), SHADERS))
+ debug = TRUE;
+#endif
+
+ if (!(driver = gsk_next_driver_new (command_queue, debug, error)))
+ goto failure;
+
+ self->command_queue = g_steal_pointer (&command_queue);
+ self->driver = g_steal_pointer (&driver);
+
+ ret = TRUE;
+
+failure:
+ g_clear_object (&driver);
+ g_clear_object (&context);
+ g_clear_object (&command_queue);
+
+ return ret;
+}
+
+static void
+gsk_next_renderer_unrealize (GskRenderer *renderer)
+{
+ GskNextRenderer *self = (GskNextRenderer *)renderer;
+
+ g_assert (GSK_IS_NEXT_RENDERER (renderer));
+
+ g_clear_object (&self->driver);
+ g_clear_object (&self->command_queue);
+}
+
+typedef struct _GskGLTextureState
+{
+ GdkGLContext *context;
+ GLuint texture_id;
+} GskGLTextureState;
+
+static void
+create_texture_from_texture_destroy (gpointer data)
+{
+ GskGLTextureState *state = data;
+
+ g_assert (state != NULL);
+ g_assert (GDK_IS_GL_CONTEXT (state->context));
+
+ gdk_gl_context_make_current (state->context);
+ glDeleteTextures (1, &state->texture_id);
+ g_clear_object (&state->context);
+ g_slice_free (GskGLTextureState, state);
+}
+
+static GdkTexture *
+create_texture_from_texture (GdkGLContext *context,
+ GLuint texture_id,
+ int width,
+ int height)
+{
+ GskGLTextureState *state;
+
+ g_assert (GDK_IS_GL_CONTEXT (context));
+
+ state = g_slice_new0 (GskGLTextureState);
+ state->texture_id = texture_id;
+ state->context = g_object_ref (context);
+
+ return gdk_gl_texture_new (context,
+ texture_id,
+ width,
+ height,
+ create_texture_from_texture_destroy,
+ state);
+}
+
+static cairo_region_t *
+get_render_region (GdkSurface *surface,
+ GdkGLContext *context)
+{
+ const cairo_region_t *damage;
+ GdkRectangle whole_surface;
+ GdkRectangle extents;
+ float scale_factor;
+
+ g_assert (GDK_IS_SURFACE (surface));
+ g_assert (GDK_IS_GL_CONTEXT (context));
+
+ scale_factor = gdk_surface_get_scale_factor (surface);
+
+ whole_surface.x = 0;
+ whole_surface.y = 0;
+ whole_surface.width = gdk_surface_get_width (surface) * scale_factor;
+ whole_surface.height = gdk_surface_get_height (surface) * scale_factor;
+
+ damage = gdk_draw_context_get_frame_region (GDK_DRAW_CONTEXT (context));
+
+ /* NULL means everything in this case, and ensures that we
+ * don't setup any complicated clips for full scene redraw.
+ */
+ if (damage == NULL ||
+ cairo_region_contains_rectangle (damage, &whole_surface) == CAIRO_REGION_OVERLAP_IN)
+ return NULL;
+
+ /* If the extents match the full-scene, do the same as above */
+ cairo_region_get_extents (damage, &extents);
+ if (gdk_rectangle_equal (&extents, &whole_surface))
+ return NULL;
+
+ /* Draw clipped to the bounding-box of the region */
+ return cairo_region_create_rectangle (&extents);
+}
+
+static void
+gsk_gl_renderer_render (GskRenderer *renderer,
+ GskRenderNode *root,
+ const cairo_region_t *update_area)
+{
+ GskNextRenderer *self = (GskNextRenderer *)renderer;
+ cairo_region_t *render_region;
+ graphene_rect_t viewport;
+ GskGLRenderJob *job;
+ GdkGLContext *context;
+ GdkSurface *surface;
+ float scale_factor;
+
+ g_assert (GSK_IS_NEXT_RENDERER (renderer));
+ g_assert (root != NULL);
+
+ context = gsk_gl_command_queue_get_context (self->command_queue);
+ surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context));
+ scale_factor = gdk_surface_get_scale_factor (surface);
+ render_region = get_render_region (surface, context);
+
+ viewport.origin.x = 0;
+ viewport.origin.y = 0;
+ viewport.size.width = gdk_surface_get_width (surface) * scale_factor;
+ viewport.size.height = gdk_surface_get_height (surface) * scale_factor;
+
+ job = gsk_gl_render_job_new (self->driver,
+ root,
+ &viewport,
+ scale_factor,
+ render_region,
+ 0,
+ FALSE);
+
+ gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (context), update_area);
+ gsk_gl_render_job_run (job);
+ gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (context));
+
+ gsk_gl_render_job_free (job);
+
+ cairo_region_destroy (render_region);
+}
+
+static GdkTexture *
+gsk_gl_renderer_render_texture (GskRenderer *renderer,
+ GskRenderNode *root,
+ const graphene_rect_t *viewport)
+{
+ GskNextRenderer *self = (GskNextRenderer *)renderer;
+ GskGLRenderJob *job;
+ GdkGLContext *context;
+ GLuint fbo_id;
+ GLuint texture_id;
+ int width;
+ int height;
+
+ g_assert (GSK_IS_NEXT_RENDERER (renderer));
+ g_assert (root != NULL);
+
+ context = gsk_gl_command_queue_get_context (self->command_queue);
+ width = ceilf (viewport->size.width);
+ height = ceilf (viewport->size.height);
+
+ if (!gsk_next_driver_create_render_target (self->driver,
+ width,
+ height,
+ &fbo_id,
+ &texture_id))
+ return NULL;
+
+ gsk_gl_command_queue_autorelease_framebuffer (self->command_queue, fbo_id);
+
+ job = gsk_gl_render_job_new (self->driver, root, viewport, 1, NULL, fbo_id, TRUE);
+ gsk_gl_render_job_run (job);
+ gsk_gl_render_job_free (job);
+
+ return create_texture_from_texture (context, texture_id, width, height);
+}
+
+static void
+gsk_next_renderer_dispose (GObject *object)
+{
+#ifdef G_ENABLE_DEBUG
+ GskNextRenderer *self = (GskNextRenderer *)object;
+
+ g_assert (self->command_queue == NULL);
+ g_assert (self->driver == NULL);
+#endif
+
+ G_OBJECT_CLASS (gsk_next_renderer_parent_class)->dispose (object);
+}
+
+static void
+gsk_next_renderer_class_init (GskNextRendererClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GskRendererClass *renderer_class = GSK_RENDERER_CLASS (klass);
+
+ object_class->dispose = gsk_next_renderer_dispose;
+
+ renderer_class->realize = gsk_next_renderer_realize;
+ renderer_class->unrealize = gsk_next_renderer_unrealize;
+ renderer_class->render = gsk_gl_renderer_render;
+ renderer_class->render_texture = gsk_gl_renderer_render_texture;
+}
+
+static void
+gsk_next_renderer_init (GskNextRenderer *self)
+{
+}
diff --git a/gsk/next/gskglrenderer.h b/gsk/next/gskglrenderer.h
new file mode 100644
index 0000000000..c2e2354f06
--- /dev/null
+++ b/gsk/next/gskglrenderer.h
@@ -0,0 +1,47 @@
+/* gskglrendererprivate.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GSK_NEXT_RENDERER_PRIVATE_H__
+#define __GSK_NEXT_RENDERER_PRIVATE_H__
+
+#include <gsk/gskrenderer.h>
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_NEXT_RENDERER (gsk_next_renderer_get_type ())
+
+#define GSK_NEXT_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GSK_TYPE_NEXT_RENDERER, GskNextRenderer))
+#define GSK_IS_NEXT_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
GSK_TYPE_NEXT_RENDERER))
+#define GSK_NEXT_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_NEXT_RENDERER,
GskNextRendererClass))
+#define GSK_IS_NEXT_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_NEXT_RENDERER))
+#define GSK_NEXT_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_NEXT_RENDERER,
GskNextRendererClass))
+
+typedef struct _GskNextRenderer GskNextRenderer;
+typedef struct _GskNextRendererClass GskNextRendererClass;
+
+GDK_AVAILABLE_IN_ALL
+GType gsk_next_renderer_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_ALL
+GskRenderer * gsk_next_renderer_new (void);
+
+G_END_DECLS
+
+#endif /* __GSK_NEXT_RENDERER_PRIVATE_H__ */
diff --git a/gsk/next/gskglrenderjob.c b/gsk/next/gskglrenderjob.c
new file mode 100644
index 0000000000..e0d8393cfa
--- /dev/null
+++ b/gsk/next/gskglrenderjob.c
@@ -0,0 +1,124 @@
+/* gskglrenderjob.c
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gskglcommandqueueprivate.h"
+#include "gskgldriverprivate.h"
+#include "gskglrenderjobprivate.h"
+
+#define ORTHO_NEAR_PLANE -10000
+#define ORTHO_FAR_PLANE 10000
+
+struct _GskGLRenderJob
+{
+ GskNextDriver *driver;
+ GskRenderNode *root;
+ cairo_region_t *region;
+ guint framebuffer;
+ float scale_factor;
+ graphene_rect_t viewport;
+ graphene_matrix_t projection;
+ GArray *modelview;
+ GArray *clip;
+ guint flip_y : 1;
+};
+
+typedef struct _GskGLRenderClip
+{
+ GskRoundedRect rect;
+ bool is_rectilinear;
+} GskGLRenderClip;
+
+typedef struct _GskGLRenderModelview
+{
+ GskTransform *transform;
+ guint n_repeated;
+} GskGLRenderModelview;
+
+static void
+init_projection_matrix (graphene_matrix_t *projection,
+ const graphene_rect_t *viewport,
+ gboolean flip_y)
+{
+ graphene_matrix_init_ortho (projection,
+ viewport->origin.x,
+ viewport->origin.x + viewport->size.width,
+ viewport->origin.y,
+ viewport->origin.y + viewport->size.height,
+ ORTHO_NEAR_PLANE,
+ ORTHO_FAR_PLANE);
+
+ if (!flip_y)
+ graphene_matrix_scale (projection, 1, -1, 1);
+}
+
+GskGLRenderJob *
+gsk_gl_render_job_new (GskNextDriver *driver,
+ GskRenderNode *root,
+ const graphene_rect_t *viewport,
+ float scale_factor,
+ const cairo_region_t *region,
+ guint framebuffer,
+ gboolean flip_y)
+{
+ GskGLRenderJob *job;
+
+ g_return_val_if_fail (GSK_IS_NEXT_DRIVER (driver), NULL);
+ g_return_val_if_fail (root != NULL, NULL);
+ g_return_val_if_fail (viewport != NULL, NULL);
+ g_return_val_if_fail (scale_factor > 0, NULL);
+
+ job = g_slice_new0 (GskGLRenderJob);
+ job->driver = g_object_ref (driver);
+ job->root = gsk_render_node_ref (root);
+ job->modelview = g_array_new (FALSE, FALSE, sizeof (GskGLRenderModelview));
+ job->clip = g_array_new (FALSE, FALSE, sizeof (GskGLRenderClip));
+ job->framebuffer = framebuffer;
+ job->scale_factor = scale_factor;
+ job->viewport = *viewport;
+ job->region = region ? cairo_region_copy (region) : NULL;
+ job->flip_y = !!flip_y;
+
+ return job;
+}
+
+void
+gsk_gl_render_job_free (GskGLRenderJob *job)
+{
+ g_clear_object (&job->driver);
+ g_clear_pointer (&job->root, gsk_render_node_unref);
+ g_clear_pointer (&job->region, cairo_region_destroy);
+ g_clear_pointer (&job->modelview, g_array_unref);
+ g_clear_pointer (&job->clip, g_array_unref);
+ g_slice_free (GskGLRenderJob, job);
+}
+
+void
+gsk_gl_render_job_run (GskGLRenderJob *job)
+{
+ g_return_if_fail (job != NULL);
+ g_return_if_fail (GSK_IS_NEXT_DRIVER (job->driver));
+
+ gsk_next_driver_begin_frame (job->driver);
+ gsk_next_driver_end_frame (job->driver);
+}
diff --git a/gsk/next/gskglrenderjobprivate.h b/gsk/next/gskglrenderjobprivate.h
new file mode 100644
index 0000000000..3d3440c80c
--- /dev/null
+++ b/gsk/next/gskglrenderjobprivate.h
@@ -0,0 +1,36 @@
+/* gskglrenderjobprivate.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GSK_GL_RENDER_JOB_H__
+#define __GSK_GL_RENDER_JOB_H__
+
+#include "gskgltypes.h"
+
+GskGLRenderJob *gsk_gl_render_job_new (GskNextDriver *driver,
+ GskRenderNode *root,
+ const graphene_rect_t *viewport,
+ float scale_factor,
+ const cairo_region_t *region,
+ guint framebuffer,
+ gboolean flip_y);
+void gsk_gl_render_job_free (GskGLRenderJob *job);
+void gsk_gl_render_job_run (GskGLRenderJob *job);
+
+#endif /* __GSK_GL_RENDER_JOB_H__ */
diff --git a/gsk/next/gskglshadowlibrary.c b/gsk/next/gskglshadowlibrary.c
new file mode 100644
index 0000000000..af611adba7
--- /dev/null
+++ b/gsk/next/gskglshadowlibrary.c
@@ -0,0 +1,50 @@
+/* gskglshadowlibrary.c
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include "gskglshadowlibraryprivate.h"
+
+struct _GskGLShadowLibrary
+{
+ GskGLTextureLibrary parent_instance;
+};
+
+G_DEFINE_TYPE (GskGLShadowLibrary, gsk_gl_shadow_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
+
+GskGLShadowLibrary *
+gsk_gl_shadow_library_new (GdkGLContext *context)
+{
+ g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+
+ return g_object_new (GSK_TYPE_GL_SHADOW_LIBRARY,
+ "context", context,
+ NULL);
+}
+
+static void
+gsk_gl_shadow_library_class_init (GskGLShadowLibraryClass *klass)
+{
+}
+
+static void
+gsk_gl_shadow_library_init (GskGLShadowLibrary *self)
+{
+}
diff --git a/gsk/next/gskglshadowlibraryprivate.h b/gsk/next/gskglshadowlibraryprivate.h
new file mode 100644
index 0000000000..3493e6d93d
--- /dev/null
+++ b/gsk/next/gskglshadowlibraryprivate.h
@@ -0,0 +1,42 @@
+/* gskglshadowlibraryprivate.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GSK_GL_SHADOW_LIBRARY_PRIVATE_H__
+#define __GSK_GL_SHADOW_LIBRARY_PRIVATE_H__
+
+#include "gskgltexturelibraryprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskGLShadowKey
+{
+ GskRoundedRect rounded_rect;
+ float blur_radius;
+} GskGLShadowKey;
+
+#define GSK_TYPE_GL_SHADOW_LIBRARY (gsk_gl_shadow_library_get_type())
+
+G_DECLARE_FINAL_TYPE (GskGLShadowLibrary, gsk_gl_shadow_library, GSK, GL_SHADOW_LIBRARY, GskGLTextureLibrary)
+
+GskGLShadowLibrary *gsk_gl_shadow_library_new (GdkGLContext *context);
+
+G_END_DECLS
+
+#endif /* __GSK_GL_SHADOW_LIBRARY_PRIVATE_H__ */
diff --git a/gsk/next/gskgltextureatlas.c b/gsk/next/gskgltextureatlas.c
new file mode 100644
index 0000000000..d0b98e89bc
--- /dev/null
+++ b/gsk/next/gskgltextureatlas.c
@@ -0,0 +1,46 @@
+/* gskgltextureatlas.c
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include "gskgltextureatlasprivate.h"
+
+struct _GskGLTextureAtlas
+{
+ GObject parent_instance;
+};
+
+G_DEFINE_TYPE (GskGLTextureAtlas, gsk_gl_texture_atlas, G_TYPE_OBJECT)
+
+GskGLTextureAtlas *
+gsk_gl_texture_atlas_new (void)
+{
+ return g_object_new (GSK_TYPE_GL_TEXTURE_ATLAS, NULL);
+}
+
+static void
+gsk_gl_texture_atlas_class_init (GskGLTextureAtlasClass *klass)
+{
+}
+
+static void
+gsk_gl_texture_atlas_init (GskGLTextureAtlas *self)
+{
+}
diff --git a/gsk/next/gskgltextureatlasprivate.h b/gsk/next/gskgltextureatlasprivate.h
new file mode 100644
index 0000000000..0350d1fe91
--- /dev/null
+++ b/gsk/next/gskgltextureatlasprivate.h
@@ -0,0 +1,36 @@
+/* gskgltextureatlasprivate.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GSK_GL_TEXTURE_ATLAS_PRIVATE_H__
+#define __GSK_GL_TEXTURE_ATLAS_PRIVATE_H__
+
+#include "gskgltypes.h"
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_GL_TEXTURE_ATLAS (gsk_gl_texture_atlas_get_type())
+
+G_DECLARE_FINAL_TYPE (GskGLTextureAtlas, gsk_gl_texture_atlas, GSK, GL_TEXTURE_ATLAS, GObject)
+
+GskGLTextureAtlas *gsk_gl_texture_atlas_new (void);
+
+G_END_DECLS
+
+#endif /* __GSK_GL_TEXTURE_ATLAS_PRIVATE_H__ */
diff --git a/gsk/next/gskgltexturelibrary.c b/gsk/next/gskgltexturelibrary.c
new file mode 100644
index 0000000000..25ff5599ac
--- /dev/null
+++ b/gsk/next/gskgltexturelibrary.c
@@ -0,0 +1,156 @@
+/* gskgltexturelibrary.c
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include "gskgltextureatlasprivate.h"
+#include "gskgltexturelibraryprivate.h"
+
+typedef struct
+{
+ guint hash;
+ guint len;
+ guint8 key[0];
+} Entry;
+
+typedef struct
+{
+ GdkGLContext *context;
+ GHashFunc hash_func;
+ GEqualFunc equal_func;
+} GskGLTextureLibraryPrivate;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GskGLTextureLibrary, gsk_gl_texture_library, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_CONTEXT,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+gsk_gl_texture_library_dispose (GObject *object)
+{
+ GskGLTextureLibrary *self = (GskGLTextureLibrary *)object;
+ GskGLTextureLibraryPrivate *priv = gsk_gl_texture_library_get_instance_private (self);
+
+ g_clear_object (&priv->context);
+
+ G_OBJECT_CLASS (gsk_gl_texture_library_parent_class)->dispose (object);
+}
+
+static void
+gsk_gl_texture_library_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GskGLTextureLibrary *self = GSK_GL_TEXTURE_LIBRARY (object);
+ GskGLTextureLibraryPrivate *priv = gsk_gl_texture_library_get_instance_private (self);
+
+ switch (prop_id)
+ {
+ case PROP_CONTEXT:
+ g_value_set_object (value, priv->context);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gsk_gl_texture_library_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GskGLTextureLibrary *self = GSK_GL_TEXTURE_LIBRARY (object);
+ GskGLTextureLibraryPrivate *priv = gsk_gl_texture_library_get_instance_private (self);
+
+ switch (prop_id)
+ {
+ case PROP_CONTEXT:
+ priv->context = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gsk_gl_texture_library_class_init (GskGLTextureLibraryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gsk_gl_texture_library_dispose;
+ object_class->get_property = gsk_gl_texture_library_get_property;
+ object_class->set_property = gsk_gl_texture_library_set_property;
+
+ properties [PROP_CONTEXT] =
+ g_param_spec_object ("context",
+ "Context",
+ "Context",
+ GDK_TYPE_GL_CONTEXT,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gsk_gl_texture_library_init (GskGLTextureLibrary *self)
+{
+ GskGLTextureLibraryPrivate *priv = gsk_gl_texture_library_get_instance_private (self);
+
+ priv->hash_func = g_direct_hash;
+ priv->equal_func = g_direct_equal;
+}
+
+GdkGLContext *
+gsk_gl_texture_library_get_context (GskGLTextureLibrary *self)
+{
+ GskGLTextureLibraryPrivate *priv = gsk_gl_texture_library_get_instance_private (self);
+
+ g_return_val_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self), NULL);
+ g_return_val_if_fail (GDK_IS_GL_CONTEXT (priv->context), NULL);
+
+ return priv->context;
+}
+
+void
+gsk_gl_texture_library_begin_frame (GskGLTextureLibrary *self)
+{
+ g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
+
+ if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame)
+ GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame (self);
+}
+
+void
+gsk_gl_texture_library_end_frame (GskGLTextureLibrary *self)
+{
+ g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self));
+
+ if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->end_frame)
+ GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->end_frame (self);
+}
diff --git a/gsk/next/gskgltexturelibraryprivate.h b/gsk/next/gskgltexturelibraryprivate.h
new file mode 100644
index 0000000000..9f8d002e58
--- /dev/null
+++ b/gsk/next/gskgltexturelibraryprivate.h
@@ -0,0 +1,61 @@
+/* gskgltexturelibraryprivate.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GSK_GL_TEXTURE_LIBRARY_PRIVATE_H__
+#define __GSK_GL_TEXTURE_LIBRARY_PRIVATE_H__
+
+#include "gskgltypes.h"
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_GL_TEXTURE_LIBRARY (gsk_gl_texture_library_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (GskGLTextureLibrary, gsk_gl_texture_library, GSK, GL_TEXTURE_LIBRARY, GObject)
+
+struct _GskGLTextureLibraryClass
+{
+ GObjectClass parent_class;
+
+ void (*begin_frame) (GskGLTextureLibrary *library);
+ void (*end_frame) (GskGLTextureLibrary *library);
+};
+
+GdkGLContext *gsk_gl_texture_library_get_context (GskGLTextureLibrary *self);
+void gsk_gl_texture_library_set_funcs (GHashFunc hash_func,
+ GEqualFunc equal_func);
+void gsk_gl_texture_library_begin_frame (GskGLTextureLibrary *self);
+void gsk_gl_texture_library_end_frame (GskGLTextureLibrary *self);
+gboolean gsk_gl_texture_library_pack (GskGLTextureLibrary *self,
+ gconstpointer key,
+ gsize keylen,
+ int width,
+ int height,
+ GskGLTextureAtlas **atlas,
+ int *atlas_x,
+ int *atlas_y);
+gboolean gsk_gl_texture_library_lookup (GskGLTextureLibrary *library,
+ gconstpointer key,
+ GskGLTextureAtlas **atlas,
+ int *atlas_x,
+ int *atlas_y);
+
+G_END_DECLS
+
+#endif /* __GSK_GL_TEXTURE_LIBRARY_PRIVATE_H__ */
diff --git a/gsk/next/gskgltypes.h b/gsk/next/gskgltypes.h
new file mode 100644
index 0000000000..232786438e
--- /dev/null
+++ b/gsk/next/gskgltypes.h
@@ -0,0 +1,53 @@
+/* gskgltypes.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef GSK_GL_TYPES_H
+#define GSK_GL_TYPES_H
+
+#include <graphene.h>
+#include <epoxy/gl.h>
+#include <gdk/gdk.h>
+#include <gsk/gsk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GskGLAttachmentState GskGLAttachmentState;
+typedef struct _GskGLCommandQueue GskGLCommandQueue;
+typedef struct _GskGLCompiler GskGLCompiler;
+typedef struct _GskGLDrawVertex GskGLDrawVertex;
+typedef struct _GskGLGlyphLibrary GskGLGlyphLibrary;
+typedef struct _GskGLIconLibrary GskGLIconLibrary;
+typedef struct _GskGLProgram GskGLProgram;
+typedef struct _GskGLShadowLibrary GskGLShadowLibrary;
+typedef struct _GskGLTextureAtlas GskGLTextureAtlas;
+typedef struct _GskGLTextureLibrary GskGLTextureLibrary;
+typedef struct _GskGLUniformState GskGLUniformState;
+typedef struct _GskNextDriver GskNextDriver;
+typedef struct _GskGLRenderJob GskGLRenderJob;
+
+struct _GskGLDrawVertex
+{
+ float position[2];
+ float uv[2];
+};
+
+G_END_DECLS
+
+#endif /* GSK_GL_TYPES_H */
diff --git a/gsk/next/gskgluniformstate.c b/gsk/next/gskgluniformstate.c
new file mode 100644
index 0000000000..c079b2ac1e
--- /dev/null
+++ b/gsk/next/gskgluniformstate.c
@@ -0,0 +1,746 @@
+/* gskgluniformstate.c
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <gsk/gskroundedrectprivate.h>
+#include <epoxy/gl.h>
+#include <string.h>
+
+#include "gskgluniformstateprivate.h"
+
+typedef struct { float v0; } Uniform1f;
+typedef struct { float v0; float v1; } Uniform2f;
+typedef struct { float v0; float v1; float v2; } Uniform3f;
+typedef struct { float v0; float v1; float v2; float v3; } Uniform4f;
+
+typedef struct { int v0; } Uniform1i;
+typedef struct { int v0; int v1; } Uniform2i;
+typedef struct { int v0; int v1; int v2; } Uniform3i;
+typedef struct { int v0; int v1; int v2; int v3; } Uniform4i;
+
+static guint8 uniform_sizes[] = {
+ sizeof (Uniform1f),
+ sizeof (Uniform2f),
+ sizeof (Uniform3f),
+ sizeof (Uniform4f),
+
+ sizeof (Uniform1f),
+ sizeof (Uniform2f),
+ sizeof (Uniform3f),
+ sizeof (Uniform4f),
+
+ sizeof (Uniform1i),
+ sizeof (Uniform2i),
+ sizeof (Uniform3i),
+ sizeof (Uniform4i),
+
+ sizeof (guint),
+
+ sizeof (graphene_matrix_t),
+ sizeof (GskRoundedRect),
+ sizeof (GdkRGBA),
+
+ 0,
+};
+
+#define REPLACE_UNIFORM(info, u, format, count) \
+ G_STMT_START { \
+ guint offset; \
+ u = alloc_uniform_data(state->uniform_data, uniform_sizes[format] * count, &offset); \
+ (info)->offset = offset; \
+ } G_STMT_END
+
+typedef struct
+{
+ GArray *uniform_info;
+ guint n_changed;
+} ProgramInfo;
+
+GskGLUniformState *
+gsk_gl_uniform_state_new (void)
+{
+ GskGLUniformState *state;
+
+ state = g_new0 (GskGLUniformState, 1);
+ state->program_info = g_array_new (FALSE, TRUE, sizeof (ProgramInfo));
+ state->uniform_data = g_byte_array_new ();
+
+ return g_steal_pointer (&state);
+}
+
+void
+gsk_gl_uniform_state_free (GskGLUniformState *state)
+{
+ g_clear_pointer (&state->program_info, g_array_unref);
+ g_clear_pointer (&state->uniform_data, g_byte_array_unref);
+ g_free (state);
+}
+
+static inline void
+program_changed (GskGLUniformState *state,
+ GskGLUniformInfo *info,
+ guint program)
+{
+ if (!info->changed)
+ {
+ info->changed = TRUE;
+ g_array_index (state->program_info, ProgramInfo, program).n_changed++;
+ }
+}
+
+void
+gsk_gl_uniform_state_clear_program (GskGLUniformState *state,
+ guint program)
+{
+ ProgramInfo *program_info;
+
+ g_assert (state != NULL);
+
+ if (program == 0 || program >= state->program_info->len)
+ return;
+
+ program_info = &g_array_index (state->program_info, ProgramInfo, program);
+ program_info->n_changed = 0;
+ g_clear_pointer (&program_info->uniform_info, g_array_unref);
+}
+
+static gpointer
+alloc_uniform_data (GByteArray *buffer,
+ guint size,
+ guint *offset)
+{
+ guint align = size > 4 ? GLIB_SIZEOF_VOID_P : 4;
+ guint masked = buffer->len & (align - 1);
+
+ /* Try to give a more natural alignment based on the size
+ * of the uniform. In case it's greater than 4 try to at least
+ * give us an 8-byte alignment to be sure we can dereference
+ * without a memcpy().
+ */
+ if (masked != 0)
+ {
+ guint prefix_align = align - masked;
+ g_byte_array_set_size (buffer, buffer->len + prefix_align);
+ }
+
+ *offset = buffer->len;
+ g_byte_array_set_size (buffer, buffer->len + size);
+
+ g_assert ((*offset & (align - 1)) == 0);
+
+ return (gpointer)&buffer->data[*offset];
+}
+
+static gpointer
+get_uniform (GskGLUniformState *state,
+ guint program,
+ GskGLUniformFormat format,
+ guint array_count,
+ guint location,
+ GskGLUniformInfo **infoptr)
+{
+ ProgramInfo *program_info;
+ GskGLUniformInfo *info;
+
+ 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 (location < GL_MAX_UNIFORM_LOCATIONS);
+
+ /* Fast path for common case (state already initialized) */
+ if G_LIKELY (program < state->program_info->len &&
+ (program_info = &g_array_index (state->program_info, ProgramInfo, program)) &&
+ program_info->uniform_info != NULL &&
+ location < program_info->uniform_info->len)
+ {
+ info = &g_array_index (program_info->uniform_info, GskGLUniformInfo, location);
+
+ if G_LIKELY (format == info->format && array_count <= info->array_count)
+ {
+ *infoptr = info;
+ return state->uniform_data->data + info->offset;
+ }
+ else
+ {
+ g_critical ("Attempt to access uniform with different type of value "
+ "than it was initialized with. Program %u Location %u.",
+ program, location);
+ *infoptr = NULL;
+ return NULL;
+ }
+ }
+ else
+ {
+ guint offset;
+
+ if (program >= state->program_info->len ||
+ g_array_index (state->program_info, ProgramInfo, program).uniform_info == NULL)
+ {
+ if (program >= state->program_info->len)
+ g_array_set_size (state->program_info, program + 1);
+
+ program_info = &g_array_index (state->program_info, ProgramInfo, program);
+ program_info->uniform_info = g_array_new (FALSE, TRUE, sizeof (GskGLUniformInfo));
+ program_info->n_changed = 0;
+ }
+
+ g_assert (program_info != NULL);
+ g_assert (program_info->uniform_info != NULL);
+
+ if (location >= program_info->uniform_info->len)
+ g_array_set_size (program_info->uniform_info, location + 1);
+
+ alloc_uniform_data (state->uniform_data, uniform_sizes[format] * array_count, &offset);
+
+ info = &g_array_index (program_info->uniform_info, GskGLUniformInfo, location);
+ info->changed = TRUE;
+ info->format = format;
+ info->offset = offset;
+ info->array_count = 0;
+
+ *infoptr = info;
+
+ return state->uniform_data->data + offset;
+ }
+}
+
+void
+gsk_gl_uniform_state_set1f (GskGLUniformState *state,
+ guint program,
+ guint location,
+ float value0)
+{
+ Uniform1f *u;
+ GskGLUniformInfo *info;
+
+ g_assert (state != NULL);
+ g_assert (program > 0);
+
+ if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_1F, 1, location, &info)))
+ {
+ if (u->v0 != value0)
+ {
+ REPLACE_UNIFORM (info, u, GSK_GL_UNIFORM_FORMAT_1F, 1);
+ u->v0 = value0;
+ program_changed (state, info, program);
+ }
+ }
+}
+
+void
+gsk_gl_uniform_state_set2f (GskGLUniformState *state,
+ guint program,
+ guint location,
+ float value0,
+ float value1)
+{
+ Uniform2f *u;
+ GskGLUniformInfo *info;
+
+ g_assert (state != NULL);
+ g_assert (program > 0);
+
+ if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_2F, 1, location, &info)))
+ {
+ if (u->v0 != value0 || u->v1 != value1)
+ {
+ REPLACE_UNIFORM (info, u, GSK_GL_UNIFORM_FORMAT_2F, 1);
+ u->v0 = value0;
+ u->v1 = value1;
+ program_changed (state, info, program);
+ }
+ }
+}
+
+void
+gsk_gl_uniform_state_set3f (GskGLUniformState *state,
+ guint program,
+ guint location,
+ float value0,
+ float value1,
+ float value2)
+{
+ Uniform3f *u;
+ GskGLUniformInfo *info;
+
+ g_assert (state != NULL);
+ g_assert (program > 0);
+
+ if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_3F, 1, location, &info)))
+ {
+ if (u->v0 != value0 || u->v1 != value1 || u->v2 != value2)
+ {
+ REPLACE_UNIFORM (info, u, GSK_GL_UNIFORM_FORMAT_3F, 1);
+ u->v0 = value0;
+ u->v1 = value1;
+ u->v2 = value2;
+ program_changed (state, info, program);
+ }
+ }
+}
+
+void
+gsk_gl_uniform_state_set4f (GskGLUniformState *state,
+ guint program,
+ guint location,
+ float value0,
+ float value1,
+ float value2,
+ float value3)
+{
+ Uniform4f *u;
+ GskGLUniformInfo *info;
+
+ g_assert (state != NULL);
+ g_assert (program > 0);
+
+ if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_4F, 1, location, &info)))
+ {
+ if (u->v0 != value0 || u->v1 != value1 || u->v2 != value2 || u->v3 != value3)
+ {
+ REPLACE_UNIFORM (info, u, GSK_GL_UNIFORM_FORMAT_4F, 1);
+ u->v0 = value0;
+ u->v1 = value1;
+ u->v2 = value2;
+ u->v3 = value3;
+ program_changed (state, info, program);
+ }
+ }
+}
+
+void
+gsk_gl_uniform_state_set1i (GskGLUniformState *state,
+ guint program,
+ guint location,
+ int value0)
+{
+ Uniform1i *u;
+ GskGLUniformInfo *info;
+
+ g_assert (state != NULL);
+ g_assert (program > 0);
+
+ if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_1I, 1, location, &info)))
+ {
+ if (u->v0 != value0)
+ {
+ REPLACE_UNIFORM (info, u, GSK_GL_UNIFORM_FORMAT_1I, 1);
+ u->v0 = value0;
+ program_changed (state, info, program);
+ }
+ }
+}
+
+void
+gsk_gl_uniform_state_set2i (GskGLUniformState *state,
+ guint program,
+ guint location,
+ int value0,
+ int value1)
+{
+ Uniform2i *u;
+ GskGLUniformInfo *info;
+
+ g_assert (state != NULL);
+ g_assert (program > 0);
+
+ if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_2I, 1, location, &info)))
+ {
+ if (u->v0 != value0 || u->v1 != value1)
+ {
+ REPLACE_UNIFORM (info, u, GSK_GL_UNIFORM_FORMAT_2I, 1);
+ u->v0 = value0;
+ u->v1 = value1;
+ program_changed (state, info, program);
+ }
+ }
+}
+
+void
+gsk_gl_uniform_state_set3i (GskGLUniformState *state,
+ guint program,
+ guint location,
+ int value0,
+ int value1,
+ int value2)
+{
+ Uniform3i *u;
+ GskGLUniformInfo *info;
+
+ g_assert (state != NULL);
+ g_assert (program > 0);
+
+ if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_3I, 1, location, &info)))
+ {
+ if (u->v0 != value0 || u->v1 != value1 || u->v2 != value2)
+ {
+ REPLACE_UNIFORM (info, u, GSK_GL_UNIFORM_FORMAT_3I, 1);
+ u->v0 = value0;
+ u->v1 = value1;
+ u->v2 = value2;
+ program_changed (state, info, program);
+ }
+ }
+}
+
+void
+gsk_gl_uniform_state_set4i (GskGLUniformState *state,
+ guint program,
+ guint location,
+ int value0,
+ int value1,
+ int value2,
+ int value3)
+{
+ Uniform4i *u;
+ GskGLUniformInfo *info;
+
+ g_assert (state != NULL);
+ g_assert (program > 0);
+
+ if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_4I, 1, location, &info)))
+ {
+ if (u->v0 != value0 || u->v1 != value1 || u->v2 != value2 || u->v3 != value3)
+ {
+ REPLACE_UNIFORM (info, u, GSK_GL_UNIFORM_FORMAT_4I, 1);
+ u->v0 = value0;
+ u->v1 = value1;
+ u->v2 = value2;
+ u->v3 = value3;
+ program_changed (state, info, program);
+ }
+ }
+}
+
+void
+gsk_gl_uniform_state_set_rounded_rect (GskGLUniformState *state,
+ guint program,
+ guint location,
+ const GskRoundedRect *rounded_rect)
+{
+ GskRoundedRect *u;
+ GskGLUniformInfo *info;
+
+ g_assert (state != NULL);
+ g_assert (program > 0);
+ g_assert (rounded_rect != NULL);
+
+ if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_ROUNDED_RECT, 1, location, &info)))
+ {
+ if (!gsk_rounded_rect_equal (rounded_rect, u))
+ {
+ REPLACE_UNIFORM (info, u, GSK_GL_UNIFORM_FORMAT_ROUNDED_RECT, 1);
+
+ if (!(info->flags & GSK_GL_UNIFORM_FLAGS_SEND_CORNERS))
+ {
+ if (!graphene_size_equal (&u->corner[0], &rounded_rect->corner[0]) ||
+ !graphene_size_equal (&u->corner[1], &rounded_rect->corner[1]) ||
+ !graphene_size_equal (&u->corner[2], &rounded_rect->corner[2]) ||
+ !graphene_size_equal (&u->corner[3], &rounded_rect->corner[3]))
+ info->flags |= GSK_GL_UNIFORM_FLAGS_SEND_CORNERS;
+ }
+
+ memcpy (u, rounded_rect, sizeof *rounded_rect);
+ program_changed (state, info, program);
+ }
+ }
+}
+
+void
+gsk_gl_uniform_state_set_matrix (GskGLUniformState *state,
+ guint program,
+ guint location,
+ const graphene_matrix_t *matrix)
+{
+ graphene_matrix_t *u;
+ GskGLUniformInfo *info;
+
+ g_assert (state != NULL);
+ g_assert (program > 0);
+ g_assert (matrix != NULL);
+
+ if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_MATRIX, 1, location, &info)))
+ {
+ if (graphene_matrix_equal_fast (u, matrix))
+ return;
+
+ if (!graphene_matrix_equal (u, matrix))
+ {
+ REPLACE_UNIFORM (info, u, GSK_GL_UNIFORM_FORMAT_MATRIX, 1);
+ memcpy (u, matrix, sizeof *matrix);
+ program_changed (state, info, program);
+ }
+ }
+}
+
+/**
+ * gsk_gl_uniform_state_set_texture:
+ * @state: a #GskGLUniformState
+ * @program: the program id
+ * @location: the location of the texture
+ * @texture_slot: a texturing slot such as GL_TEXTURE0
+ *
+ * Sets the uniform expecting a texture to @texture_slot. This API
+ * expects a texture slot such as GL_TEXTURE0 to reduce chances of
+ * miss-use by the caller.
+ *
+ * The value stored to the uniform is in the form of 0 for GL_TEXTURE0,
+ * 1 for GL_TEXTURE1, and so on.
+ */
+void
+gsk_gl_uniform_state_set_texture (GskGLUniformState *state,
+ guint program,
+ guint location,
+ guint texture_slot)
+{
+ GskGLUniformInfo *info;
+ guint *u;
+
+ g_assert (texture_slot >= GL_TEXTURE0);
+ g_assert (texture_slot < GL_TEXTURE16);
+
+ texture_slot -= GL_TEXTURE0;
+
+ if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_TEXTURE, 1, location, &info)))
+ {
+ if (*u != texture_slot)
+ {
+ REPLACE_UNIFORM (info, u, GSK_GL_UNIFORM_FORMAT_TEXTURE, 1);
+ *u = texture_slot;
+ program_changed (state, info, program);
+ }
+ }
+}
+
+/**
+ * gsk_gl_uniform_state_set_color:
+ * @state: a #GskGLUniformState
+ * @program: a program id > 0
+ * @location: the uniform location
+ * @color: a color to set or %NULL for transparent
+ *
+ * Sets a uniform to the color described by @color. This is a convenience
+ * function to allow callers to avoid having to translate colors to floats
+ * in other portions of the renderer.
+ */
+void
+gsk_gl_uniform_state_set_color (GskGLUniformState *state,
+ guint program,
+ guint location,
+ const GdkRGBA *color)
+{
+ static const GdkRGBA transparent = {0};
+ GskGLUniformInfo *info;
+ GdkRGBA *u;
+
+ g_assert (state != NULL);
+ g_assert (program > 0);
+
+ if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_COLOR, 1, location, &info)))
+ {
+ if (color == NULL)
+ color = &transparent;
+
+ if (!gdk_rgba_equal (u, color))
+ {
+ REPLACE_UNIFORM (info, u, GSK_GL_UNIFORM_FORMAT_COLOR, 1);
+ memcpy (u, color, sizeof *color);
+ program_changed (state, info, program);
+ }
+ }
+}
+
+void
+gsk_gl_uniform_state_set1fv (GskGLUniformState *state,
+ guint program,
+ guint location,
+ guint count,
+ const float *value)
+{
+ Uniform1f *u;
+ GskGLUniformInfo *info;
+
+ g_assert (state != NULL);
+ g_assert (program > 0);
+
+ if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_1F, count, location, &info)))
+ {
+ gboolean changed = memcmp (u, value, sizeof (Uniform1f) * count) != 0;
+
+ if (changed)
+ {
+ REPLACE_UNIFORM (info, u, GSK_GL_UNIFORM_FORMAT_1F, count);
+ memcpy (u, value, sizeof (Uniform1f) * count);
+ program_changed (state, info, program);
+ }
+ }
+}
+
+void
+gsk_gl_uniform_state_set2fv (GskGLUniformState *state,
+ guint program,
+ guint location,
+ guint count,
+ const float *value)
+{
+ Uniform2f *u;
+ GskGLUniformInfo *info;
+
+ g_assert (state != NULL);
+ g_assert (program > 0);
+
+ if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_2F, count, location, &info)))
+ {
+ gboolean changed = memcmp (u, value, sizeof (Uniform2f) * count) != 0;
+
+ if (changed)
+ {
+ REPLACE_UNIFORM (info, u, GSK_GL_UNIFORM_FORMAT_2F, count);
+ memcpy (u, value, sizeof (Uniform2f) * count);
+ program_changed (state, info, program);
+ }
+ }
+}
+
+void
+gsk_gl_uniform_state_set3fv (GskGLUniformState *state,
+ guint program,
+ guint location,
+ guint count,
+ const float *value)
+{
+ Uniform3f *u;
+ GskGLUniformInfo *info;
+
+ g_assert (state != NULL);
+ g_assert (program > 0);
+
+ if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_3F, count, location, &info)))
+ {
+ gboolean changed = memcmp (u, value, sizeof (Uniform3f) * count) != 0;
+
+ if (changed)
+ {
+ REPLACE_UNIFORM (info, u, GSK_GL_UNIFORM_FORMAT_3F, count);
+ memcpy (u, value, sizeof (Uniform3f) * count);
+ program_changed (state, info, program);
+ }
+ }
+}
+
+void
+gsk_gl_uniform_state_set4fv (GskGLUniformState *state,
+ guint program,
+ guint location,
+ guint count,
+ const float *value)
+{
+ Uniform4f *u;
+ GskGLUniformInfo *info;
+
+ g_assert (state != NULL);
+ g_assert (program > 0);
+
+ if ((u = get_uniform (state, program, GSK_GL_UNIFORM_FORMAT_4F, count, location, &info)))
+ {
+ gboolean changed = memcmp (u, value, sizeof (Uniform4f) * count) != 0;
+
+ if (changed)
+ {
+ REPLACE_UNIFORM (info, u, GSK_GL_UNIFORM_FORMAT_4F, count);
+ memcpy (u, value, sizeof (Uniform4f) * count);
+ program_changed (state, info, program);
+ }
+ }
+}
+
+void
+gsk_gl_uniform_state_snapshot (GskGLUniformState *state,
+ guint program_id,
+ GskGLUniformStateCallback callback,
+ gpointer user_data)
+{
+ ProgramInfo *program_info;
+
+ g_assert (state != NULL);
+ g_assert (program_id > 0);
+
+ if (program_id >= state->program_info->len)
+ return;
+
+ program_info = &g_array_index (state->program_info, ProgramInfo, program_id);
+ if (program_info->n_changed == 0 || program_info->uniform_info == NULL)
+ return;
+
+ for (guint i = 0; i < program_info->uniform_info->len; i++)
+ {
+ GskGLUniformInfo *info = &g_array_index (program_info->uniform_info, GskGLUniformInfo, i);
+
+ if (!info->changed)
+ continue;
+
+ callback (info, i, user_data);
+
+ info->changed = FALSE;
+ info->flags = 0;
+ }
+
+ program_info->n_changed = 0;
+}
+
+void
+gsk_gl_uniform_state_end_frame (GskGLUniformState *state)
+{
+ GByteArray *buffer;
+
+ g_return_if_fail (state != NULL);
+
+ /* After a frame finishes, we want to remove all our copies of uniform
+ * data that isn't needed any longer. We just create a new byte array
+ * that contains the new data with the gaps removed.
+ */
+
+ buffer = g_byte_array_sized_new (4096);
+
+ for (guint i = 0; i < state->program_info->len; i++)
+ {
+ ProgramInfo *program_info = &g_array_index (state->program_info, ProgramInfo, i);
+
+ if (program_info->uniform_info == NULL)
+ continue;
+
+ for (guint j = 0; j < program_info->uniform_info->len; j++)
+ {
+ GskGLUniformInfo *info = &g_array_index (program_info->uniform_info, GskGLUniformInfo, j);
+ guint size = uniform_sizes[info->format] * info->array_count;
+ guint offset;
+
+ alloc_uniform_data (buffer, size, &offset);
+ memcpy (&buffer->data[offset], &state->uniform_data->data[info->offset], size);
+ info->changed = FALSE;
+ info->offset = offset;
+ }
+ }
+
+ g_clear_pointer (&state->uniform_data, g_byte_array_unref);
+ state->uniform_data = buffer;
+}
diff --git a/gsk/next/gskgluniformstateprivate.h b/gsk/next/gskgluniformstateprivate.h
new file mode 100644
index 0000000000..2fea9dbd3e
--- /dev/null
+++ b/gsk/next/gskgluniformstateprivate.h
@@ -0,0 +1,189 @@
+/* gskgluniformstateprivate.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef GSK_GL_UNIFORM_STATE_PRIVATE_H
+#define GSK_GL_UNIFORM_STATE_PRIVATE_H
+
+#include "gskgltypes.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskGLUniformState
+{
+ GArray *program_info;
+ GByteArray *uniform_data;
+} GskGLUniformState;
+
+typedef struct _GskGLUniformInfo
+{
+ guint changed : 1;
+ guint format : 5;
+ guint array_count : 6;
+ guint flags : 4;
+ guint offset : 16;
+} GskGLUniformInfo;
+
+G_STATIC_ASSERT (sizeof (GskGLUniformInfo) == 4);
+
+/**
+ * GskGLUniformStateCallback:
+ * @info: a pointer to the information about the uniform
+ * @location: the location of the uniform within the GPU program.
+ * @user_data: closure data for the callback
+ *
+ * This callback can be used to snapshot state of a program which
+ * is useful when batching commands so that the state may be compared
+ * with future evocations of the program.
+ */
+typedef void (*GskGLUniformStateCallback) (const GskGLUniformInfo *info,
+ guint location,
+ gpointer user_data);
+
+typedef enum _GskGLUniformFlags
+{
+ GSK_GL_UNIFORM_FLAGS_SEND_CORNERS = 1 << 0,
+} GskGLUniformFlags;
+
+typedef enum _GskGLUniformKind
+{
+ GSK_GL_UNIFORM_FORMAT_1F = 1,
+ GSK_GL_UNIFORM_FORMAT_2F,
+ GSK_GL_UNIFORM_FORMAT_3F,
+ GSK_GL_UNIFORM_FORMAT_4F,
+
+ GSK_GL_UNIFORM_FORMAT_1FV,
+ GSK_GL_UNIFORM_FORMAT_2FV,
+ GSK_GL_UNIFORM_FORMAT_3FV,
+ GSK_GL_UNIFORM_FORMAT_4FV,
+
+ GSK_GL_UNIFORM_FORMAT_1I,
+ GSK_GL_UNIFORM_FORMAT_2I,
+ GSK_GL_UNIFORM_FORMAT_3I,
+ GSK_GL_UNIFORM_FORMAT_4I,
+
+ GSK_GL_UNIFORM_FORMAT_TEXTURE,
+
+ GSK_GL_UNIFORM_FORMAT_MATRIX,
+ GSK_GL_UNIFORM_FORMAT_ROUNDED_RECT,
+ GSK_GL_UNIFORM_FORMAT_COLOR,
+
+ GSK_GL_UNIFORM_FORMAT_LAST
+} GskGLUniformFormat;
+
+GskGLUniformState *gsk_gl_uniform_state_new (void);
+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_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);
+void gsk_gl_uniform_state_free (GskGLUniformState *state);
+
+static inline gconstpointer
+gsk_gl_uniform_state_get_uniform_data (GskGLUniformState *state,
+ guint offset)
+{
+ return (gconstpointer)&state->uniform_data->data[offset];
+}
+
+G_END_DECLS
+
+#endif /* GSK_GL_UNIFORM_STATE_PRIVATE_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]