[gtk+/wip/otte/shader: 2/2] gsksl: Implement skeleton SPIRV output



commit c252ce331a802f11b408e764b8a1f00534d0f903
Author: Benjamin Otte <otte redhat com>
Date:   Thu Sep 21 20:33:57 2017 +0200

    gsksl: Implement skeleton SPIRV output

 gsk/gskslnode.c               |  397 ++++++++++++++++++++++++++++++++++-------
 gsk/gskslnodeprivate.h        |    7 +
 gsk/gskslpointertype.c        |   52 ++++++
 gsk/gskslpointertypeprivate.h |    9 +
 gsk/gsksltype.c               |  142 ++++++++++++++-
 gsk/gsksltypeprivate.h        |    7 +
 gsk/gsksltypesprivate.h       |    2 +
 gsk/gskspvwriter.c            |  232 ++++++++++++++++++++++++
 gsk/gskspvwriterprivate.h     |  200 +++++++++++++++++++++
 gsk/meson.build               |    3 +-
 gtk/glsl.c                    |   53 +++++-
 11 files changed, 1024 insertions(+), 80 deletions(-)
---
diff --git a/gsk/gskslnode.c b/gsk/gskslnode.c
index 5cc3986..0ba8851 100644
--- a/gsk/gskslnode.c
+++ b/gsk/gskslnode.c
@@ -26,6 +26,7 @@
 #include "gsksltokenizerprivate.h"
 #include "gsksltokenstreamprivate.h"
 #include "gsksltypeprivate.h"
+#include "gskspvwriterprivate.h"
 
 #include <string.h>
 
@@ -44,67 +45,6 @@ gsk_sl_node_alloc (const GskSlNodeClass *klass,
 }
 #define gsk_sl_node_new(_name, _klass) ((_name *) gsk_sl_node_alloc ((_klass), sizeof (_name)))
 
-/* PROGRAM */
-
-typedef struct _GskSlNodeProgram GskSlNodeProgram;
-
-struct _GskSlNodeProgram {
-  GskSlNode parent;
-
-  GskSlScope *scope;
-  GSList *declarations;
-  GSList *functions;
-};
-
-static void
-gsk_sl_node_program_free (GskSlNode *node)
-{
-  GskSlNodeProgram *program = (GskSlNodeProgram *) node;
-
-  g_slist_free (program->declarations);
-  g_slist_free (program->functions);
-  gsk_sl_scope_unref (program->scope);
-
-  g_slice_free (GskSlNodeProgram, program);
-}
-
-static void
-gsk_sl_node_program_print (GskSlNode *node,
-                           GString   *string)
-{
-  GskSlNodeProgram *program = (GskSlNodeProgram *) node;
-  GSList *l;
-
-  for (l = program->declarations; l; l = l->next)
-    gsk_sl_node_print (l->data, string);
-
-  for (l = program->functions; l; l = l->next)
-    {
-      if (l != program->functions || program->declarations != NULL)
-        g_string_append (string, "\n");
-      gsk_sl_node_print (l->data, string);
-    }
-}
-
-static GskSlType *
-gsk_sl_node_program_get_return_type (GskSlNode *node)
-{
-  return NULL;
-}
-
-static gboolean
-gsk_sl_node_program_is_constant (GskSlNode *node)
-{
-  return TRUE;
-}
-
-static const GskSlNodeClass GSK_SL_NODE_PROGRAM = {
-  gsk_sl_node_program_free,
-  gsk_sl_node_program_print,
-  gsk_sl_node_program_get_return_type,
-  gsk_sl_node_program_is_constant
-};
-
 /* FUNCTION */
 
 typedef struct _GskSlNodeFunction GskSlNodeFunction;
@@ -171,11 +111,142 @@ gsk_sl_node_function_is_constant (GskSlNode *node)
   return TRUE;
 }
 
+static guint32
+gsk_sl_node_function_write_spv (const GskSlNode *node,
+                                GskSpvWriter    *writer)
+{
+  GskSlNodeFunction *function = (GskSlNodeFunction *) node;
+  guint32 return_type_id, function_type_id, function_id, label_id;
+  GSList *l;
+
+  /* declare type of function */
+  return_type_id = gsk_spv_writer_get_id_for_type (writer, function->return_type);
+  function_type_id = gsk_spv_writer_next_id (writer);
+  gsk_spv_writer_add (writer,
+                      GSK_SPV_WRITER_SECTION_DECLARE,
+                      3, GSK_SPV_OP_TYPE_FUNCTION,
+                      (guint32[2]) { function_type_id,
+                                     return_type_id });
+
+  /* add debug info */
+  /* FIXME */
+
+  /* add function body */
+  function_id = gsk_spv_writer_next_id (writer);
+  gsk_spv_writer_add (writer,
+                      GSK_SPV_WRITER_SECTION_CODE,
+                      5, GSK_SPV_OP_FUNCTION,
+                      (guint32[4]) { return_type_id,
+                                     function_id,
+                                     0,
+                                     function_type_id });
+  label_id = gsk_spv_writer_next_id (writer);
+  gsk_spv_writer_add (writer,
+                      GSK_SPV_WRITER_SECTION_CODE,
+                      2, GSK_SPV_OP_LABEL,
+                      (guint32[4]) { label_id });
+
+  for (l = function->statements; l; l = l->next)
+    {
+      gsk_sl_node_write_spv (l->data, writer);
+    }
+
+  gsk_spv_writer_add (writer,
+                      GSK_SPV_WRITER_SECTION_CODE,
+                      1, GSK_SPV_OP_FUNCTION_END,
+                      NULL);
+  return function_id;
+}
+
 static const GskSlNodeClass GSK_SL_NODE_FUNCTION = {
   gsk_sl_node_function_free,
   gsk_sl_node_function_print,
   gsk_sl_node_function_get_return_type,
-  gsk_sl_node_function_is_constant
+  gsk_sl_node_function_is_constant,
+  gsk_sl_node_function_write_spv
+};
+
+/* PROGRAM */
+
+typedef struct _GskSlNodeProgram GskSlNodeProgram;
+
+struct _GskSlNodeProgram {
+  GskSlNode parent;
+
+  GskSlScope *scope;
+  GSList *declarations;
+  GSList *functions;
+};
+
+static void
+gsk_sl_node_program_free (GskSlNode *node)
+{
+  GskSlNodeProgram *program = (GskSlNodeProgram *) node;
+
+  g_slist_free (program->declarations);
+  g_slist_free (program->functions);
+  gsk_sl_scope_unref (program->scope);
+
+  g_slice_free (GskSlNodeProgram, program);
+}
+
+static void
+gsk_sl_node_program_print (GskSlNode *node,
+                           GString   *string)
+{
+  GskSlNodeProgram *program = (GskSlNodeProgram *) node;
+  GSList *l;
+
+  for (l = program->declarations; l; l = l->next)
+    gsk_sl_node_print (l->data, string);
+
+  for (l = program->functions; l; l = l->next)
+    {
+      if (l != program->functions || program->declarations != NULL)
+        g_string_append (string, "\n");
+      gsk_sl_node_print (l->data, string);
+    }
+}
+
+static GskSlType *
+gsk_sl_node_program_get_return_type (GskSlNode *node)
+{
+  return NULL;
+}
+
+static gboolean
+gsk_sl_node_program_is_constant (GskSlNode *node)
+{
+  return TRUE;
+}
+
+static guint32
+gsk_sl_node_program_write_spv (const GskSlNode *node,
+                               GskSpvWriter    *writer)
+{
+  GskSlNodeProgram *program = (GskSlNodeProgram *) node;
+  GSList *l;
+
+  for (l = program->declarations; l; l = l->next)
+    gsk_sl_node_write_spv (l->data, writer);
+
+  for (l = program->functions; l; l = l->next)
+    {
+      guint32 id = gsk_sl_node_write_spv (l->data, writer);
+
+      if (g_str_equal (((GskSlNodeFunction *) l->data)->name, "main"))
+        gsk_spv_writer_set_entry_point (writer, id);
+    }
+
+  return 0;
+}
+
+static const GskSlNodeClass GSK_SL_NODE_PROGRAM = {
+  gsk_sl_node_program_free,
+  gsk_sl_node_program_print,
+  gsk_sl_node_program_get_return_type,
+  gsk_sl_node_program_is_constant,
+  gsk_sl_node_program_write_spv
 };
 
 /* ASSIGNMENT */
@@ -268,11 +339,21 @@ gsk_sl_node_assignment_is_constant (GskSlNode *node)
   return gsk_sl_node_get_return_type (assignment->rvalue);
 }
 
+static guint32
+gsk_sl_node_assignment_write_spv (const GskSlNode *node,
+                                  GskSpvWriter    *writer)
+{
+  g_assert_not_reached ();
+
+  return 0;
+}
+
 static const GskSlNodeClass GSK_SL_NODE_ASSIGNMENT = {
   gsk_sl_node_assignment_free,
   gsk_sl_node_assignment_print,
   gsk_sl_node_assignment_get_return_type,
-  gsk_sl_node_assignment_is_constant
+  gsk_sl_node_assignment_is_constant,
+  gsk_sl_node_assignment_write_spv
 };
 
 /* BINARY */
@@ -710,11 +791,21 @@ gsk_sl_node_operation_is_constant (GskSlNode *node)
       && gsk_sl_node_is_constant (operation->right);
 }
 
+static guint32
+gsk_sl_node_operation_write_spv (const GskSlNode *node,
+                                 GskSpvWriter    *writer)
+{
+  g_assert_not_reached ();
+
+  return 0;
+}
+
 static const GskSlNodeClass GSK_SL_NODE_OPERATION = {
   gsk_sl_node_operation_free,
   gsk_sl_node_operation_print,
   gsk_sl_node_operation_get_return_type,
-  gsk_sl_node_operation_is_constant
+  gsk_sl_node_operation_is_constant,
+  gsk_sl_node_operation_write_spv
 };
 
 /* DECLARATION */
@@ -778,11 +869,41 @@ gsk_sl_node_declaration_is_constant (GskSlNode *node)
   return declaration->constant;
 }
 
+static guint32
+gsk_sl_node_declaration_write_spv (const GskSlNode *node,
+                                   GskSpvWriter    *writer)
+{
+  GskSlNodeDeclaration *declaration = (GskSlNodeDeclaration *) node;
+  guint32 pointer_type_id, declaration_id;
+
+  pointer_type_id = gsk_spv_writer_get_id_for_pointer_type (writer, declaration->type);
+  declaration_id = gsk_spv_writer_next_id (writer);
+  gsk_spv_writer_add (writer,
+                      GSK_SPV_WRITER_SECTION_CODE,
+                      4, GSK_SPV_OP_VARIABLE,
+                      (guint32[3]) { pointer_type_id,
+                                     declaration_id,
+                                     gsk_sl_pointer_type_get_storage_class (declaration->type)});
+  gsk_spv_writer_set_id_for_declaration (writer, node, declaration_id);
+  
+  if (declaration->initial)
+    {
+      gsk_spv_writer_add (writer,
+                          GSK_SPV_WRITER_SECTION_CODE,
+                          4, GSK_SPV_OP_STORE,
+                          (guint32[3]) { declaration_id,
+                                         gsk_sl_node_write_spv (declaration->initial, writer)});
+    }
+
+  return declaration_id;
+}
+
 static const GskSlNodeClass GSK_SL_NODE_DECLARATION = {
   gsk_sl_node_declaration_free,
   gsk_sl_node_declaration_print,
   gsk_sl_node_declaration_get_return_type,
-  gsk_sl_node_declaration_is_constant
+  gsk_sl_node_declaration_is_constant,
+  gsk_sl_node_declaration_write_spv
 };
 
 /* REFERENCE */
@@ -832,11 +953,32 @@ gsk_sl_node_reference_is_constant (GskSlNode *node)
   return gsk_sl_node_is_constant (reference->declaration);
 }
 
+static guint32
+gsk_sl_node_reference_write_spv (const GskSlNode *node,
+                                 GskSpvWriter    *writer)
+{
+  GskSlNodeReference *reference = (GskSlNodeReference *) node;
+  guint32 declaration_id, result_id, type_id;
+
+  type_id = gsk_spv_writer_get_id_for_type (writer, gsk_sl_node_get_return_type (reference->declaration));
+  declaration_id = gsk_spv_writer_get_id_for_declaration (writer, reference->declaration);
+  result_id = gsk_spv_writer_next_id (writer);
+  gsk_spv_writer_add (writer,
+                      GSK_SPV_WRITER_SECTION_CODE,
+                      4, GSK_SPV_OP_LOAD,
+                      (guint32[3]) { type_id,
+                                     result_id,
+                                     declaration_id });
+
+  return result_id;
+}
+
 static const GskSlNodeClass GSK_SL_NODE_REFERENCE = {
   gsk_sl_node_reference_free,
   gsk_sl_node_reference_print,
   gsk_sl_node_reference_get_return_type,
-  gsk_sl_node_reference_is_constant
+  gsk_sl_node_reference_is_constant,
+  gsk_sl_node_reference_write_spv
 };
 
 /* FUNCTION_CALL */
@@ -902,11 +1044,21 @@ gsk_sl_node_function_call_is_constant (GskSlNode *node)
   return FALSE;
 }
 
+static guint32
+gsk_sl_node_function_call_write_spv (const GskSlNode *node,
+                                     GskSpvWriter    *writer)
+{
+  g_assert_not_reached ();
+
+  return 0;
+}
+
 static const GskSlNodeClass GSK_SL_NODE_FUNCTION_CALL = {
   gsk_sl_node_function_call_free,
   gsk_sl_node_function_call_print,
   gsk_sl_node_function_call_get_return_type,
-  gsk_sl_node_function_call_is_constant
+  gsk_sl_node_function_call_is_constant,
+  gsk_sl_node_function_call_write_spv
 };
 
 /* RETURN */
@@ -966,11 +1118,21 @@ gsk_sl_node_return_is_constant (GskSlNode *node)
     return TRUE;
 }
 
+static guint32
+gsk_sl_node_return_write_spv (const GskSlNode *node,
+                              GskSpvWriter    *writer)
+{
+  g_assert_not_reached ();
+
+  return 0;
+}
+
 static const GskSlNodeClass GSK_SL_NODE_RETURN = {
   gsk_sl_node_return_free,
   gsk_sl_node_return_print,
   gsk_sl_node_return_get_return_type,
-  gsk_sl_node_return_is_constant
+  gsk_sl_node_return_is_constant,
+  gsk_sl_node_return_write_spv
 };
 
 /* CONSTANT */
@@ -1054,11 +1216,84 @@ gsk_sl_node_constant_is_constant (GskSlNode *node)
   return TRUE;
 }
 
+static guint32
+gsk_sl_node_constant_write_spv (const GskSlNode *node,
+                                GskSpvWriter    *writer)
+{
+  GskSlNodeConstant *constant = (GskSlNodeConstant *) node;
+  guint32 type_id, result_id;
+
+  switch (constant->type)
+  {
+    case GSK_SL_FLOAT:
+      type_id = gsk_spv_writer_get_id_for_type (writer, gsk_sl_type_get_scalar (GSK_SL_FLOAT));
+      result_id = gsk_spv_writer_next_id (writer);
+      gsk_spv_writer_add (writer,
+                          GSK_SPV_WRITER_SECTION_DECLARE,
+                          4, GSK_SPV_OP_CONSTANT,
+                          (guint32[3]) { type_id,
+                                         result_id,
+                                         *(guint32 *) &constant->f });
+      break;
+
+    case GSK_SL_DOUBLE:
+      type_id = gsk_spv_writer_get_id_for_type (writer, gsk_sl_type_get_scalar (GSK_SL_DOUBLE));
+      result_id = gsk_spv_writer_next_id (writer);
+      gsk_spv_writer_add (writer,
+                          GSK_SPV_WRITER_SECTION_DECLARE,
+                          5, GSK_SPV_OP_CONSTANT,
+                          (guint32[4]) { type_id,
+                                         result_id,
+                                         *(guint32 *) &constant->d,
+                                         *(((guint32 *) &constant->d) + 1) });
+      break;
+
+    case GSK_SL_INT:
+      type_id = gsk_spv_writer_get_id_for_type (writer, gsk_sl_type_get_scalar (GSK_SL_INT));
+      result_id = gsk_spv_writer_next_id (writer);
+      gsk_spv_writer_add (writer,
+                          GSK_SPV_WRITER_SECTION_DECLARE,
+                          4, GSK_SPV_OP_CONSTANT,
+                          (guint32[3]) { type_id,
+                                         result_id,
+                                         constant->i32 });
+      break;
+
+    case GSK_SL_UINT:
+      type_id = gsk_spv_writer_get_id_for_type (writer, gsk_sl_type_get_scalar (GSK_SL_UINT));
+      result_id = gsk_spv_writer_next_id (writer);
+      gsk_spv_writer_add (writer,
+                          GSK_SPV_WRITER_SECTION_DECLARE,
+                          4, GSK_SPV_OP_CONSTANT,
+                          (guint32[3]) { type_id,
+                                         result_id,
+                                         constant->u32 });
+      break;
+
+    case GSK_SL_BOOL:
+      type_id = gsk_spv_writer_get_id_for_type (writer, gsk_sl_type_get_scalar (GSK_SL_BOOL));
+      result_id = gsk_spv_writer_next_id (writer);
+      gsk_spv_writer_add (writer,
+                          GSK_SPV_WRITER_SECTION_DECLARE,
+                          3, constant->b ? GSK_SPV_OP_CONSTANT_TRUE : GSK_SPV_OP_CONSTANT_FALSE,
+                          (guint32[2]) { type_id,
+                                         result_id });
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
+  }
+
+  return result_id;
+}
+
 static const GskSlNodeClass GSK_SL_NODE_CONSTANT = {
   gsk_sl_node_constant_free,
   gsk_sl_node_constant_print,
   gsk_sl_node_constant_get_return_type,
-  gsk_sl_node_constant_is_constant
+  gsk_sl_node_constant_is_constant,
+  gsk_sl_node_constant_write_spv
 };
 
 /* API */
@@ -2276,6 +2511,22 @@ gsk_sl_node_print (GskSlNode *node,
   node->class->print (node, string);
 }
 
+GBytes *
+gsk_sl_node_compile (GskSlNode *node)
+{
+  GskSpvWriter *writer;
+  GBytes *bytes;
+
+  writer = gsk_spv_writer_new ();
+
+  gsk_sl_node_write_spv (node, writer);
+  bytes = gsk_spv_writer_write (writer);
+
+  gsk_spv_writer_unref (writer);
+
+  return bytes;
+}
+
 GskSlType *
 gsk_sl_node_get_return_type (GskSlNode *node)
 {
@@ -2287,3 +2538,11 @@ gsk_sl_node_is_constant (GskSlNode *node)
 {
   return node->class->is_constant (node);
 }
+
+guint32
+gsk_sl_node_write_spv (const GskSlNode *node,
+                       GskSpvWriter    *writer)
+{
+  return node->class->write_spv (node, writer);
+}
+
diff --git a/gsk/gskslnodeprivate.h b/gsk/gskslnodeprivate.h
index 6df9bb6..43e7d54 100644
--- a/gsk/gskslnodeprivate.h
+++ b/gsk/gskslnodeprivate.h
@@ -39,6 +39,8 @@ struct _GskSlNodeClass {
                                                                  GString             *string);
   GskSlType *           (* get_return_type)                     (GskSlNode           *node);
   gboolean              (* is_constant)                         (GskSlNode           *node);
+  guint32               (* write_spv)                           (const GskSlNode     *node,
+                                                                 GskSpvWriter        *writer);
 };
 
 GDK_AVAILABLE_IN_3_92
@@ -52,9 +54,14 @@ void                    gsk_sl_node_unref                       (GskSlNode
 GDK_AVAILABLE_IN_3_92
 void                    gsk_sl_node_print                       (GskSlNode           *node,
                                                                  GString             *string);
+GDK_AVAILABLE_IN_3_92
+GBytes *                gsk_sl_node_compile                     (GskSlNode           *node);
 GskSlType *             gsk_sl_node_get_return_type             (GskSlNode           *node);
 gboolean                gsk_sl_node_is_constant                 (GskSlNode           *node);
 
+guint32                 gsk_sl_node_write_spv                   (const GskSlNode     *node,
+                                                                 GskSpvWriter        *writer);
+
 G_END_DECLS
 
 #endif /* __GSK_SL_NODE_PRIVATE_H__ */
diff --git a/gsk/gskslpointertype.c b/gsk/gskslpointertype.c
index d4006ec..5f37df9 100644
--- a/gsk/gskslpointertype.c
+++ b/gsk/gskslpointertype.c
@@ -23,6 +23,7 @@
 #include "gsksltokenizerprivate.h"
 #include "gsksltokenstreamprivate.h"
 #include "gsksltypeprivate.h"
+#include "gskspvwriterprivate.h"
 
 struct _GskSlPointerType {
   int ref_count;
@@ -384,3 +385,54 @@ gsk_sl_pointer_type_is_writeonly (const GskSlPointerType *type)
 {
   return type->flags & GSK_SL_POINTER_TYPE_WRITEONLY ? TRUE : FALSE;
 }
+
+GskSpvStorageClass
+gsk_sl_pointer_type_get_storage_class (const GskSlPointerType *type)
+{
+  if (type->flags & GSK_SL_POINTER_TYPE_LOCAL)
+    return GSK_SPV_STORAGE_CLASS_FUNCTION;
+
+  return GSK_SPV_STORAGE_CLASS_PRIVATE;
+}
+
+gboolean
+gsk_sl_pointer_type_equal (gconstpointer a,
+                           gconstpointer b)
+{
+  const GskSlPointerType *typea = a;
+  const GskSlPointerType *typeb = b;
+
+  if (!gsk_sl_type_equal (typea->type, typeb->type))
+    return FALSE;
+
+  return gsk_sl_pointer_type_get_storage_class (typea)
+      == gsk_sl_pointer_type_get_storage_class (typeb);
+}
+
+guint
+gsk_sl_pointer_type_hash (gconstpointer t)
+{
+  const GskSlPointerType *type = t;
+
+  return gsk_sl_type_hash (type->type)
+       ^ gsk_sl_pointer_type_get_storage_class (type);
+}
+
+guint32
+gsk_sl_pointer_type_write_spv (const GskSlPointerType *type,
+                               GskSpvWriter           *writer)
+{
+  guint32 type_id, result_id;
+
+  type_id = gsk_spv_writer_get_id_for_type (writer, type->type);
+  result_id = gsk_spv_writer_next_id (writer);
+
+  gsk_spv_writer_add (writer,
+                      GSK_SPV_WRITER_SECTION_DECLARE,
+                      4, GSK_SPV_OP_TYPE_POINTER,
+                      (guint32[3]) { result_id,
+                                     gsk_sl_pointer_type_get_storage_class (type),
+                                     type_id });
+
+  return result_id;
+}
diff --git a/gsk/gskslpointertypeprivate.h b/gsk/gskslpointertypeprivate.h
index ceff838..493cafd 100644
--- a/gsk/gskslpointertypeprivate.h
+++ b/gsk/gskslpointertypeprivate.h
@@ -22,6 +22,7 @@
 #include <glib.h>
 
 #include "gsksltypesprivate.h"
+#include "gskspvwriterprivate.h"
 
 G_BEGIN_DECLS
 
@@ -71,6 +72,14 @@ gboolean                gsk_sl_pointer_type_is_restrict                 (const G
 gboolean                gsk_sl_pointer_type_is_readonly                 (const GskSlPointerType     *type);
 gboolean                gsk_sl_pointer_type_is_writeonly                (const GskSlPointerType     *type);
 
+gboolean                gsk_sl_pointer_type_equal                       (gconstpointer               a,
+                                                                         gconstpointer               b);
+guint                   gsk_sl_pointer_type_hash                        (gconstpointer               type);
+
+GskSpvStorageClass      gsk_sl_pointer_type_get_storage_class           (const GskSlPointerType     *type);
+guint32                 gsk_sl_pointer_type_write_spv                   (const GskSlPointerType     *type,
+                                                                         GskSpvWriter               *writer);
+
 G_END_DECLS
 
 #endif /* __GSK_SL_POINTER_TYPE_PRIVATE_H__ */
diff --git a/gsk/gsksltype.c b/gsk/gsksltype.c
index 8a3a8f9..748fe0f 100644
--- a/gsk/gsksltype.c
+++ b/gsk/gsksltype.c
@@ -22,6 +22,7 @@
 
 #include "gsksltokenizerprivate.h"
 #include "gsksltokenstreamprivate.h"
+#include "gskspvwriterprivate.h"
 
 #include <string.h>
 
@@ -45,6 +46,8 @@ struct _GskSlTypeClass {
   guint                 (* get_length)                          (GskSlType           *type);
   gboolean              (* can_convert)                         (GskSlType           *target,
                                                                  GskSlType           *source);
+  guint32               (* write_spv)                           (const GskSlType     *type,
+                                                                 GskSpvWriter        *writer);
 };
 
 /* SCALAR */
@@ -121,13 +124,85 @@ gsk_sl_type_scalar_can_convert (GskSlType *target,
   return gsk_sl_scalar_type_can_convert (target_scalar->scalar, source_scalar->scalar);
 }
 
+static guint32
+gsk_sl_type_scalar_write_spv (const GskSlType *type,
+                              GskSpvWriter    *writer)
+{
+  GskSlTypeScalar *scalar = (GskSlTypeScalar *) type;
+  guint32 result;
+
+  switch (scalar->scalar)
+  {
+    case GSK_SL_VOID:
+      result = gsk_spv_writer_next_id (writer);
+      gsk_spv_writer_add (writer,
+                          GSK_SPV_WRITER_SECTION_DECLARE,
+                          2, GSK_SPV_OP_TYPE_VOID,
+                          (guint32[1]) { result });
+      break;
+
+    case GSK_SL_FLOAT:
+      result = gsk_spv_writer_next_id (writer);
+      gsk_spv_writer_add (writer,
+                          GSK_SPV_WRITER_SECTION_DECLARE,
+                          3, GSK_SPV_OP_TYPE_FLOAT,
+                          (guint32[2]) { result,
+                                         32 });
+      break;
+
+    case GSK_SL_DOUBLE:
+      result = gsk_spv_writer_next_id (writer);
+      gsk_spv_writer_add (writer,
+                          GSK_SPV_WRITER_SECTION_DECLARE,
+                          3, GSK_SPV_OP_TYPE_FLOAT,
+                          (guint32[2]) { result,
+                                         64 });
+      break;
+
+    case GSK_SL_INT:
+      result = gsk_spv_writer_next_id (writer);
+      gsk_spv_writer_add (writer,
+                          GSK_SPV_WRITER_SECTION_DECLARE,
+                          4, GSK_SPV_OP_TYPE_INT,
+                          (guint32[3]) { result,
+                                         32,
+                                         1 });
+      break;
+
+    case GSK_SL_UINT:
+      result = gsk_spv_writer_next_id (writer);
+      gsk_spv_writer_add (writer,
+                          GSK_SPV_WRITER_SECTION_DECLARE,
+                          4, GSK_SPV_OP_TYPE_INT,
+                          (guint32[3]) { result,
+                                         32,
+                                         0 });
+      break;
+
+    case GSK_SL_BOOL:
+      result = gsk_spv_writer_next_id (writer);
+      gsk_spv_writer_add (writer,
+                          GSK_SPV_WRITER_SECTION_DECLARE,
+                          2, GSK_SPV_OP_TYPE_BOOL,
+                          (guint32[1]) { result });
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
+  }
+
+  return result;
+}
+
 static const GskSlTypeClass GSK_SL_TYPE_SCALAR = {
   gsk_sl_type_scalar_free,
   gsk_sl_type_scalar_get_name,
   gsk_sl_type_scalar_get_scalar_type,
   gsk_sl_type_scalar_get_index_type,
   gsk_sl_type_scalar_get_length,
-  gsk_sl_type_scalar_can_convert
+  gsk_sl_type_scalar_can_convert,
+  gsk_sl_type_scalar_write_spv
 };
 
 /* VECTOR */
@@ -193,13 +268,33 @@ gsk_sl_type_vector_can_convert (GskSlType *target,
   return gsk_sl_scalar_type_can_convert (target_vector->scalar, source_vector->scalar);
 }
 
+static guint32
+gsk_sl_type_vector_write_spv (const GskSlType *type,
+                              GskSpvWriter    *writer)
+{
+  GskSlTypeVector *vector = (GskSlTypeVector *) type;
+  guint32 result_id, scalar_id;
+
+  scalar_id = gsk_spv_writer_get_id_for_type (writer, gsk_sl_type_get_scalar (vector->scalar));
+  result_id = gsk_spv_writer_next_id (writer);
+  gsk_spv_writer_add (writer,
+                      GSK_SPV_WRITER_SECTION_DECLARE,
+                      4, GSK_SPV_OP_TYPE_VECTOR,
+                      (guint32[3]) { result_id,
+                                     scalar_id,
+                                     vector->length });
+  
+  return result_id;
+}
+
 static const GskSlTypeClass GSK_SL_TYPE_VECTOR = {
   gsk_sl_type_vector_free,
   gsk_sl_type_vector_get_name,
   gsk_sl_type_vector_get_scalar_type,
   gsk_sl_type_vector_get_index_type,
   gsk_sl_type_vector_get_length,
-  gsk_sl_type_vector_can_convert
+  gsk_sl_type_vector_can_convert,
+  gsk_sl_type_vector_write_spv
 };
 
 /* MATRIX */
@@ -266,13 +361,33 @@ gsk_sl_type_matrix_can_convert (GskSlType *target,
   return gsk_sl_scalar_type_can_convert (target_matrix->scalar, source_matrix->scalar);
 }
 
+static guint32
+gsk_sl_type_matrix_write_spv (const GskSlType *type,
+                              GskSpvWriter    *writer)
+{
+  GskSlTypeMatrix *matrix = (GskSlTypeMatrix *) type;
+  guint32 result_id, vector_id;
+
+  vector_id = gsk_spv_writer_get_id_for_type (writer, gsk_sl_type_get_index_type (type));
+  result_id = gsk_spv_writer_next_id (writer);
+  gsk_spv_writer_add (writer,
+                      GSK_SPV_WRITER_SECTION_DECLARE,
+                      4, GSK_SPV_OP_TYPE_MATRIX,
+                      (guint32[3]) { result_id,
+                                     vector_id,
+                                     matrix->columns });
+  
+  return result_id;
+}
+
 static const GskSlTypeClass GSK_SL_TYPE_MATRIX = {
   gsk_sl_type_matrix_free,
   gsk_sl_type_matrix_get_name,
   gsk_sl_type_matrix_get_scalar_type,
   gsk_sl_type_matrix_get_index_type,
   gsk_sl_type_matrix_get_length,
-  gsk_sl_type_matrix_can_convert
+  gsk_sl_type_matrix_can_convert,
+  gsk_sl_type_matrix_write_spv
 };
 
 GskSlType *
@@ -624,3 +739,24 @@ gsk_sl_type_can_convert (const GskSlType *target,
 {
   return target->class->can_convert (target, source);
 }
+
+gboolean
+gsk_sl_type_equal (gconstpointer a,
+                   gconstpointer b)
+{
+  return a == b;
+}
+
+guint
+gsk_sl_type_hash (gconstpointer type)
+{
+  return GPOINTER_TO_UINT (type);
+}
+
+guint32
+gsk_sl_type_write_spv (const GskSlType *type,
+                       GskSpvWriter    *writer)
+{
+  return type->class->write_spv (type, writer);
+}
+
diff --git a/gsk/gsksltypeprivate.h b/gsk/gsksltypeprivate.h
index 29b9583..74cac9c 100644
--- a/gsk/gsksltypeprivate.h
+++ b/gsk/gsksltypeprivate.h
@@ -60,6 +60,13 @@ gboolean                gsk_sl_scalar_type_can_convert          (GskSlScalarType
 gboolean                gsk_sl_type_can_convert                 (const GskSlType     *target,
                                                                  const GskSlType     *source);
 
+gboolean                gsk_sl_type_equal                       (gconstpointer        a,
+                                                                 gconstpointer        b);
+guint                   gsk_sl_type_hash                        (gconstpointer        type);
+
+guint32                 gsk_sl_type_write_spv                   (const GskSlType     *type,
+                                                                 GskSpvWriter        *writer);
+
 G_END_DECLS
 
 #endif /* __GSK_SL_TYPE_PRIVATE_H__ */
diff --git a/gsk/gsksltypesprivate.h b/gsk/gsksltypesprivate.h
index ad387f0..7224c8f 100644
--- a/gsk/gsksltypesprivate.h
+++ b/gsk/gsksltypesprivate.h
@@ -29,4 +29,6 @@ typedef struct _GskSlToken              GskSlToken;
 typedef struct _GskSlTokenStream        GskSlTokenStream;
 typedef struct _GskSlType               GskSlType;
 
+typedef struct _GskSpvWriter            GskSpvWriter;
+
 #endif /* __GSK_SL_TYPES_H__ */
diff --git a/gsk/gskspvwriter.c b/gsk/gskspvwriter.c
new file mode 100644
index 0000000..07b7502
--- /dev/null
+++ b/gsk/gskspvwriter.c
@@ -0,0 +1,232 @@
+/* GTK - The GIMP Toolkit
+ *   
+ * Copyright © 2017 Benjamin Otte <otte gnome org>
+ *
+ * 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 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 library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gskspvwriterprivate.h"
+
+#include "gskslnodeprivate.h"
+#include "gskslpointertypeprivate.h"
+#include "gsksltypeprivate.h"
+
+struct _GskSpvWriter
+{
+  int ref_count;
+
+  guint32 last_id;
+  GArray *code[GSK_SPV_WRITER_N_SECTIONS];
+
+  guint32 entry_point;
+  GHashTable *types;
+  GHashTable *pointer_types;
+  GHashTable *declarations;
+};
+
+GskSpvWriter *
+gsk_spv_writer_new (void)
+{
+  GskSpvWriter *writer;
+  guint i;
+  
+  writer = g_slice_new0 (GskSpvWriter);
+  writer->ref_count = 1;
+
+  for (i = 0; i < GSK_SPV_WRITER_N_SECTIONS; i++)
+    {
+      writer->code[i] = g_array_new (FALSE, FALSE, sizeof (guint32));
+    }
+
+  writer->types = g_hash_table_new_full (gsk_sl_type_hash, gsk_sl_type_equal,
+                                         (GDestroyNotify) gsk_sl_type_unref, NULL);
+  writer->pointer_types = g_hash_table_new_full (gsk_sl_pointer_type_hash, gsk_sl_pointer_type_equal,
+                                                 (GDestroyNotify) gsk_sl_pointer_type_unref, NULL);
+  writer->declarations = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+                                                (GDestroyNotify) gsk_sl_node_unref, NULL);
+
+  return writer;
+}
+
+GskSpvWriter *
+gsk_spv_writer_ref (GskSpvWriter *writer)
+{
+  g_return_val_if_fail (writer != NULL, NULL);
+
+  writer->ref_count += 1;
+
+  return writer;
+}
+
+void
+gsk_spv_writer_unref (GskSpvWriter *writer)
+{
+  guint i;
+
+  if (writer == NULL)
+    return;
+
+  writer->ref_count -= 1;
+  if (writer->ref_count > 0)
+    return;
+
+  for (i = 0; i < GSK_SPV_WRITER_N_SECTIONS; i++)
+    {
+      g_array_free (writer->code[i], TRUE);
+    }
+
+  g_hash_table_destroy (writer->pointer_types);
+  g_hash_table_destroy (writer->types);
+  g_hash_table_destroy (writer->declarations);
+
+  g_slice_free (GskSpvWriter, writer);
+}
+
+#define STRING(s, offset) ((guint32) ((s)[offset + 0] | ((s)[offset + 1] << 8) | ((s)[offset + 2] << 16) | 
((s)[offset + 3] << 24)))
+static void
+gsk_spv_writer_write_header (GskSpvWriter *writer)
+{
+  gsk_spv_writer_add (writer,
+                      GSK_SPV_WRITER_SECTION_HEADER,
+                      2, GSK_SPV_OP_CAPABILITY,
+                      (guint32[1]) { GSK_SPV_CAPABILITY_SHADER });
+  gsk_spv_writer_add (writer,
+                      GSK_SPV_WRITER_SECTION_HEADER,
+                      3, GSK_SPV_OP_MEMORY_MODEL,
+                      (guint32[2]) { GSK_SPV_ADDRESSING_LOGICAL,
+                                     GSK_SPV_MEMORY_GLSL450 });
+  gsk_spv_writer_add (writer,
+                      GSK_SPV_WRITER_SECTION_HEADER,
+                      5, GSK_SPV_OP_ENTRY_POINT,
+                      (guint32[4]) { GSK_SPV_EXECUTION_MODEL_FRAGMENT,
+                                     writer->entry_point,
+                                     STRING ("main", 0),
+                                     0 });
+  gsk_spv_writer_add (writer,
+                      GSK_SPV_WRITER_SECTION_HEADER,
+                      3, GSK_SPV_OP_EXECUTION_MODE,
+                      (guint32[4]) { writer->entry_point,
+                                     GSK_SPV_EXECUTION_MODE_ORIGIN_UPPER_LEFT });
+}
+
+static void
+gsk_spv_writer_clear_header (GskSpvWriter *writer)
+{
+  g_array_set_size (writer->code[GSK_SPV_WRITER_SECTION_HEADER], 0);
+}
+
+GBytes *
+gsk_spv_writer_write (GskSpvWriter *writer)
+{
+  GArray *array;
+  gsize size;
+  guint i;
+
+  gsk_spv_writer_write_header (writer);
+
+  array = g_array_new (FALSE, FALSE, sizeof (guint32));
+
+  g_array_append_val (array, (guint32) { GSK_SPV_MAGIC_NUMBER });
+  g_array_append_val (array, (guint32) { (GSK_SPV_VERSION_MAJOR << 16) | (GSK_SPV_VERSION_MINOR << 8) });
+  g_array_append_val (array, (guint32) { GSK_SPV_GENERATOR });
+  g_array_append_val (array, (guint32) { writer->last_id + 1 });
+  g_array_append_val (array, (guint32) { writer->last_id + 1 });
+  
+  for (i = 0; i < GSK_SPV_WRITER_N_SECTIONS; i++)
+    {
+      g_array_append_vals (array, writer->code[i]->data, writer->code[i]->len);
+    }
+
+  gsk_spv_writer_clear_header (writer);
+
+  size = array->len * sizeof (guint32);
+  return g_bytes_new_take (g_array_free (array, FALSE), size);
+}
+
+guint32
+gsk_spv_writer_get_id_for_type (GskSpvWriter *writer,
+                                GskSlType    *type)
+{
+  guint32 result;
+
+  result = GPOINTER_TO_UINT (g_hash_table_lookup (writer->types, type));
+  if (result != 0)
+    return result;
+
+  result = gsk_sl_type_write_spv (type, writer);
+  g_hash_table_insert (writer->types, gsk_sl_type_ref (type), GUINT_TO_POINTER (result));
+  return result;
+}
+
+guint32
+gsk_spv_writer_get_id_for_pointer_type (GskSpvWriter       *writer,
+                                        GskSlPointerType   *type)
+{
+  guint32 result;
+
+  result = GPOINTER_TO_UINT (g_hash_table_lookup (writer->pointer_types, type));
+  if (result != 0)
+    return result;
+
+  result = gsk_sl_pointer_type_write_spv (type, writer);
+  g_hash_table_insert (writer->pointer_types, gsk_sl_pointer_type_ref (type), GUINT_TO_POINTER (result));
+  return result;
+}
+
+guint32
+gsk_spv_writer_get_id_for_declaration (GskSpvWriter *writer,
+                                       GskSlNode    *node)
+{
+  return GPOINTER_TO_UINT (g_hash_table_lookup (writer->declarations, node));
+}
+
+void
+gsk_spv_writer_set_id_for_declaration (GskSpvWriter *writer,
+                                       GskSlNode    *node,
+                                       guint32       id)
+{
+  g_hash_table_insert (writer->declarations, gsk_sl_node_ref (node), GUINT_TO_POINTER (id));
+}
+
+guint32
+gsk_spv_writer_next_id (GskSpvWriter *writer)
+{
+  writer->last_id++;
+
+  return writer->last_id;
+}
+
+void
+gsk_spv_writer_set_entry_point (GskSpvWriter *writer,
+                                guint32       entry_point)
+{
+  writer->entry_point = entry_point;
+}
+
+void
+gsk_spv_writer_add (GskSpvWriter        *writer,
+                    GskSpvWriterSection  section,
+                    guint16              word_count,
+                    guint16              opcode,
+                    guint32             *words)
+{
+  guint32 word;
+
+  word = word_count << 16 | opcode;
+  g_array_append_val (writer->code[section], word);
+  g_array_append_vals (writer->code[section], words, word_count - 1);
+}
+
diff --git a/gsk/gskspvwriterprivate.h b/gsk/gskspvwriterprivate.h
new file mode 100644
index 0000000..39d561a
--- /dev/null
+++ b/gsk/gskspvwriterprivate.h
@@ -0,0 +1,200 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright © 2017 Benjamin Otte <otte gnome org>
+ *
+ * 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 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 library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GSK_SPV_WRITER_PRIVATE_H__
+#define __GSK_SPV_WRITER_PRIVATE_H__
+
+#include <glib.h>
+
+#include "gsksltypesprivate.h"
+
+G_BEGIN_DECLS
+
+#define GSK_SPV_MAGIC_NUMBER 0x07230203
+#define GSK_SPV_VERSION_MAJOR 1
+#define GSK_SPV_VERSION_MINOR 0
+#define GSK_SPV_GENERATOR 0
+
+typedef enum {
+  GSK_SPV_OP_NOP = 0,
+  GSK_SPV_OP_UNDEF = 1,
+  GSK_SPV_OP_SOURCE_CONTINUED = 2,
+  GSK_SPV_OP_SOURCE = 3,
+  GSK_SPV_OP_SOURCE_EXTENSION = 4,
+  GSK_SPV_OP_NAME = 5,
+  GSK_SPV_OP_MEMBER_NAME = 6,
+  GSK_SPV_OP_STRING = 7,
+  GSK_SPV_OP_LINE = 8,
+  GSK_SPV_OP_EXTENSION = 10,
+  GSK_SPV_OP_EXT_INST_IMPORT = 11,
+  GSK_SPV_OP_EXT_INST = 12,
+  GSK_SPV_OP_MEMORY_MODEL = 14,
+  GSK_SPV_OP_ENTRY_POINT = 15,
+  GSK_SPV_OP_EXECUTION_MODE = 16,
+  GSK_SPV_OP_CAPABILITY = 17,
+  GSK_SPV_OP_TYPE_VOID = 19,
+  GSK_SPV_OP_TYPE_BOOL = 20,
+  GSK_SPV_OP_TYPE_INT = 21,
+  GSK_SPV_OP_TYPE_FLOAT = 22,
+  GSK_SPV_OP_TYPE_VECTOR = 23,
+  GSK_SPV_OP_TYPE_MATRIX = 24,
+  GSK_SPV_OP_TYPE_IMAGE = 25,
+  GSK_SPV_OP_TYPE_SAMPLER = 26,
+  GSK_SPV_OP_TYPE_SAMPLED_IMAGE = 27,
+  GSK_SPV_OP_TYPE_ARRAY = 28,
+  GSK_SPV_OP_TYPE_RUNTIME_ARRAY = 29,
+  GSK_SPV_OP_TYPE_STRUCT = 30,
+  GSK_SPV_OP_TYPE_OPAQUE = 31,
+  GSK_SPV_OP_TYPE_POINTER = 32,
+  GSK_SPV_OP_TYPE_FUNCTION = 33,
+  GSK_SPV_OP_TYPE_EVENT = 34,
+  GSK_SPV_OP_TYPE_DEVICE_EVENT = 35,
+  GSK_SPV_OP_TYPE_RESERVE_ID = 36,
+  GSK_SPV_OP_TYPE_QUEUE = 37,
+  GSK_SPV_OP_TYPE_PIPE = 38,
+  GSK_SPV_OP_TYPE_FORWARD_POINTER = 39,
+  GSK_SPV_OP_CONSTANT_TRUE = 41,
+  GSK_SPV_OP_CONSTANT_FALSE = 42,
+  GSK_SPV_OP_CONSTANT = 43,
+  GSK_SPV_OP_CONSTANT_COMPOSITE = 44,
+  GSK_SPV_OP_CONSTANT_SAMPLER = 45,
+  GSK_SPV_OP_CONSTANT_NULL = 46,
+  GSK_SPV_OP_SPEC_CONSTANT_TRUE = 48,
+  GSK_SPV_OP_SPEC_CONSTANT_FALSE = 49,
+  GSK_SPV_OP_SPEC_CONSTANT = 50,
+  GSK_SPV_OP_SPEC_CONSTANT_COMPOSITE = 51,
+  GSK_SPV_OP_SPEC_CONSTANT_OP = 52,
+  GSK_SPV_OP_FUNCTION = 54,
+  GSK_SPV_OP_FUNCTION_PARAMETER = 55,
+  GSK_SPV_OP_FUNCTION_END = 56,
+  GSK_SPV_OP_FUNCTION_CALL = 57,
+  GSK_SPV_OP_VARIABLE = 59,
+  GSK_SPV_OP_IMAGE_TEXEL_POINTER = 60,
+  GSK_SPV_OP_LOAD = 61,
+  GSK_SPV_OP_STORE = 62,
+  GSK_SPV_OP_COPY_MEMORY = 63,
+  GSK_SPV_OP_COPY_MEMORY_SIZED = 64,
+  GSK_SPV_OP_ACCESS_CHAIN = 65,
+  GSK_SPV_OP_IN_BOUNDS_ACCESS_CHAIN = 66,
+  GSK_SPV_OP_PTR_ACCESS_CHAIN = 67,
+  GSK_SPV_OP_ARRAY_LENGTH = 68,
+  GSK_SPV_OP_GENERIC_PTR_MEM_SEMANTICS = 69,
+  GSK_SPV_OP_IN_BOUNDS_PTR_ACCESS_CHAIN = 70,
+  GSK_SPV_OP_PHI = 245,
+  GSK_SPV_OP_LOOP_MERGE = 246,
+  GSK_SPV_OP_SELECTION_MERGE = 247,
+  GSK_SPV_OP_LABEL = 248,
+  GSK_SPV_OP_BRANCH = 249,
+  GSK_SPV_OP_BRANCH_CONDITIONAL = 250,
+  GSK_SPV_OP_SWITCH = 251,
+  GSK_SPV_OP_KILL = 252,
+  GSK_SPV_OP_RETURN = 253,
+  GSK_SPV_OP_RETURN_VALUE = 254,
+  GSK_SPV_OP_UNREACHABLE = 255,
+  GSK_SPV_OP_LIFETIME_START = 256,
+  GSK_SPV_OP_LIFETIME_STOP = 257,
+  GSK_SPV_OP_SIZE_OF = 321,
+  GSK_SPV_OP_TYPE_PIPE_STORAGE = 322,
+  GSK_SPV_OP_TYPE_NAMED_BARRIER = 327
+} GskSpvOpcode;
+
+typedef enum {
+  GSK_SPV_CAPABILITY_MATRIX = 0,
+  GSK_SPV_CAPABILITY_SHADER = 1
+} GskSpvCapability;
+
+typedef enum {
+  GSK_SPV_ADDRESSING_LOGICAL = 0,
+  GSK_SPV_ADDRESSING_PHYSICAL32 = 1,
+  GSK_SPV_ADDRESSING_PHYSICAL64 = 2,
+} GskSpvAddressingModel;
+
+typedef enum {
+  GSK_SPV_MEMORY_SIMPLE = 0,
+  GSK_SPV_MEMORY_GLSL450 = 1,
+  GSK_SPV_MEMORY_OPEN_CL = 2
+} GskSpvMemoryModel;
+
+typedef enum {
+  GSK_SPV_EXECUTION_MODEL_VERTEX = 0,
+  GSK_SPV_EXECUTION_MODEL_TESSELATION_CONTROL = 1,
+  GSK_SPV_EXECUTION_MODEL_TESSELATION_EVALUATION = 2,
+  GSK_SPV_EXECUTION_MODEL_GEOMETRY = 3,
+  GSK_SPV_EXECUTION_MODEL_FRAGMENT = 4,
+  GSK_SPV_EXECUTION_MODEL_GL_COMPUTE = 5,
+  GSK_SPV_EXECUTION_MODEL_KERNEL = 6,
+} GskSpvExecutionModel;
+
+typedef enum {
+  GSK_SPV_EXECUTION_MODE_ORIGIN_UPPER_LEFT = 7
+} GskSpvExecutionMode;
+
+typedef enum {
+  GSK_SPV_STORAGE_CLASS_UNIFORM_CONSTANT = 0,
+  GSK_SPV_STORAGE_CLASS_INPUT = 1,
+  GSK_SPV_STORAGE_CLASS_UNIFORM = 2,
+  GSK_SPV_STORAGE_CLASS_OUTPUT = 3,
+  GSK_SPV_STORAGE_CLASS_WORKGROUP = 4,
+  GSK_SPV_STORAGE_CLASS_CROSS_WORKGROUP = 5,
+  GSK_SPV_STORAGE_CLASS_PRIVATE = 6,
+  GSK_SPV_STORAGE_CLASS_FUNCTION = 7,
+  GSK_SPV_STORAGE_CLASS_GENERIC = 8,
+  GSK_SPV_STORAGE_CLASS_PUSH_CONSTANT = 9,
+  GSK_SPV_STORAGE_CLASS_ATOMIC_COUNTER = 10,
+  GSK_SPV_STORAGE_CLASS_IMAGE = 11,
+  GSK_SPV_STORAGE_CLASS_STORAGE_BUFFER = 12
+} GskSpvStorageClass;
+
+typedef enum {
+  GSK_SPV_WRITER_SECTION_HEADER,
+  GSK_SPV_WRITER_SECTION_DEBUG,
+  GSK_SPV_WRITER_SECTION_DECLARE,
+  GSK_SPV_WRITER_SECTION_CODE,
+  /* add more */
+  GSK_SPV_WRITER_N_SECTIONS
+} GskSpvWriterSection;
+
+GskSpvWriter *          gsk_spv_writer_new                      (void);
+
+GskSpvWriter *          gsk_spv_writer_ref                      (GskSpvWriter           *writer);
+void                    gsk_spv_writer_unref                    (GskSpvWriter           *writer);
+
+GBytes *                gsk_spv_writer_write                    (GskSpvWriter           *writer);
+void                    gsk_spv_writer_set_entry_point          (GskSpvWriter           *writer,
+                                                                 guint32                 entry_point);
+
+guint32                 gsk_spv_writer_get_id_for_type          (GskSpvWriter           *writer,
+                                                                 GskSlType              *type);
+guint32                 gsk_spv_writer_get_id_for_pointer_type  (GskSpvWriter           *writer,
+                                                                 GskSlPointerType       *type);
+guint32                 gsk_spv_writer_get_id_for_declaration   (GskSpvWriter           *writer,
+                                                                 GskSlNode              *node);
+void                    gsk_spv_writer_set_id_for_declaration   (GskSpvWriter           *writer,
+                                                                 GskSlNode              *node,
+                                                                 guint32                 id);
+
+guint32                 gsk_spv_writer_next_id                  (GskSpvWriter           *writer);
+void                    gsk_spv_writer_add                      (GskSpvWriter           *writer,
+                                                                 GskSpvWriterSection     section,
+                                                                 guint16                 word_count,
+                                                                 guint16                 opcode,
+                                                                 guint32                *words);
+
+G_END_DECLS
+
+#endif /* __GSK_SPV_WRITER_PRIVATE_H__ */
diff --git a/gsk/meson.build b/gsk/meson.build
index 0e04846..1b31042 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -38,7 +38,8 @@ gsk_private_sources = files([
   'gskslscope.c',
   'gsksltokenizer.c',
   'gsksltokenstream.c',
-  'gsksltype.c'
+  'gsksltype.c',
+  'gskspvwriter.c'
 ])
 
 gsk_public_headers = files([
diff --git a/gtk/glsl.c b/gtk/glsl.c
index 8f8d6a0..f78102d 100644
--- a/gtk/glsl.c
+++ b/gtk/glsl.c
@@ -41,7 +41,44 @@ bytes_new_from_file (const char  *filename,
   return g_bytes_new_take (data, length);
 }
 
-gboolean
+static gboolean
+compile (GOutputStream *output,
+         const char    *filename)
+{
+  GBytes *bytes;
+  GskSlNode *program;
+  GError *error = NULL;
+
+  bytes = bytes_new_from_file (filename, &error);
+  if (bytes == NULL)
+    {
+      g_print (error->message);
+      g_error_free (error);
+      return FALSE;
+    }
+
+  program = gsk_sl_node_new_program (bytes, NULL);
+  g_bytes_unref (bytes);
+  if (program == NULL)
+    return FALSE;
+
+  bytes = gsk_sl_node_compile (program);
+  if (!g_output_stream_write_all (output, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), NULL, 
NULL, &error))
+    {
+      g_print (error->message);
+      g_error_free (error);
+      g_bytes_unref (bytes);
+      gsk_sl_node_unref (program);
+      return FALSE;
+    }
+
+  g_bytes_unref (bytes);
+  gsk_sl_node_unref (program);
+
+  return TRUE;
+}
+
+static gboolean
 dump (GOutputStream *output,
       const char    *filename)
 {
@@ -59,6 +96,7 @@ dump (GOutputStream *output,
     }
 
   program = gsk_sl_node_new_program (bytes, NULL);
+  g_bytes_unref (bytes);
   if (program == NULL)
     return FALSE;
 
@@ -95,7 +133,9 @@ main (int argc, char *argv[])
   GOptionContext *ctx;
   char **filenames = NULL;
   char *output_file = NULL;
+  gboolean print = FALSE;
   const GOptionEntry entries[] = {
+    { "print", 'p', 0, G_OPTION_ARG_NONE, &print, "Print instead of compiling", NULL },
     { "output", 'o', 0, G_OPTION_ARG_FILENAME, &output_file, "Output filename", "FILE" },
     { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, "List of input files", "FILE 
[FILE...]" },
     { NULL, }
@@ -145,13 +185,12 @@ main (int argc, char *argv[])
         }
     }
 
-  for (i = 0; filenames[i] != NULL; i++)
+  for (i = 0; success && filenames[i] != NULL; i++)
     {
-      if (!dump (output, filenames[i]))
-        {
-          success = FALSE;
-          break;
-        }
+      if (print)
+        success = dump (output, filenames[i]);
+      else
+        success = compile (output, filenames[i]);
     }
 
  if (!g_output_stream_close (output, NULL, &error))



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