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



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

    gsksl: Implement skeleton SPIRV output

 gsk/gskslnode.c               |  398 ++++++++++++++++++++++++++++++++++-------
 gsk/gskslnodeprivate.h        |    7 +
 gsk/gskslpointertype.c        |   52 ++++++
 gsk/gskslpointertypeprivate.h |    9 +
 gsk/gskslpreprocessor.c       |  121 ++++++++++++-
 gsk/gsksltokenizer.c          |   30 +++-
 gsk/gsksltokenizerprivate.h   |    2 +
 gsk/gsksltype.c               |  142 ++++++++++++++-
 gsk/gsksltypeprivate.h        |    7 +
 gsk/gsksltypesprivate.h       |    2 +
 gsk/gskspvwriter.c            |  241 +++++++++++++++++++++++++
 gsk/gskspvwriterprivate.h     |  200 +++++++++++++++++++++
 gsk/meson.build               |    3 +-
 gtk/glsl.c                    |   53 +++++-
 14 files changed, 1172 insertions(+), 95 deletions(-)
---
diff --git a/gsk/gskslnode.c b/gsk/gskslnode.c
index 9e04c9c..89e41ed 100644
--- a/gsk/gskslnode.c
+++ b/gsk/gskslnode.c
@@ -26,6 +26,7 @@
 #include "gskslscopeprivate.h"
 #include "gsksltokenizerprivate.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 FALSE;
 }
 
+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, (GskSlNode *) node, declaration_id);
+  
+  if (declaration->initial)
+    {
+      gsk_spv_writer_add (writer,
+                          GSK_SPV_WRITER_SECTION_CODE,
+                          3, GSK_SPV_OP_STORE,
+                          (guint32[2]) { 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 */
@@ -1055,11 +1217,85 @@ 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;
+
+    case GSK_SL_VOID:
+    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 */
@@ -2277,6 +2513,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)
 {
@@ -2288,3 +2540,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 07b09f2..42ca9f3 100644
--- a/gsk/gskslpointertype.c
+++ b/gsk/gskslpointertype.c
@@ -23,6 +23,7 @@
 #include "gskslpreprocessorprivate.h"
 #include "gsksltokenizerprivate.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 f0b5614..cb4af80 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/gskslpreprocessor.c b/gsk/gskslpreprocessor.c
index f0fb716..c633a7f 100644
--- a/gsk/gskslpreprocessor.c
+++ b/gsk/gskslpreprocessor.c
@@ -93,29 +93,132 @@ static gboolean
 gsk_sl_token_is_skipped (const GskSlToken *token)
 {
   return gsk_sl_token_is (token, GSK_SL_TOKEN_ERROR)
+      || gsk_sl_token_is (token, GSK_SL_TOKEN_NEWLINE)
       || gsk_sl_token_is (token, GSK_SL_TOKEN_WHITESPACE)
       || gsk_sl_token_is (token, GSK_SL_TOKEN_COMMENT)
       || gsk_sl_token_is (token, GSK_SL_TOKEN_SINGLE_LINE_COMMENT);
 }
 
-static void
-gsk_sl_token_ensure (GskSlPreprocessor *preproc)
+static gboolean
+gsk_sl_preprocessor_next_token (GskSlPreprocessor *preproc)
 {
-  if (!gsk_sl_token_is (&preproc->token, GSK_SL_TOKEN_EOF))
-    return;
-
+  gboolean was_newline;
+  
   do 
     {
       preproc->location = *gsk_sl_tokenizer_get_location (preproc->tokenizer);
+      was_newline = gsk_sl_token_is (&preproc->token, GSK_SL_TOKEN_NEWLINE);
       gsk_sl_tokenizer_read_token (preproc->tokenizer, &preproc->token);
     }
   while (gsk_sl_token_is_skipped (&preproc->token));
+
+  return was_newline;
+}
+
+static void
+gsk_sl_preprocessor_handle_preprocessor_directive (GskSlPreprocessor *preproc)
+{
+  gboolean was_newline = gsk_sl_preprocessor_next_token (preproc);
+
+  /* empty # line */
+  if (was_newline)
+    return;
+
+  if (gsk_sl_token_is (&preproc->token, GSK_SL_TOKEN_IDENTIFIER))
+    {
+      if (g_str_equal (preproc->token.str, "define"))
+        {
+          gsk_sl_preprocessor_error (preproc, "Unknown preprocessor directive #define.");
+        }
+#if 0
+      else if (g_str_equal (preproc->token.str, "else"))
+        {
+        }
+      else if (g_str_equal (preproc->token.str, "elif"))
+        {
+        }
+      else if (g_str_equal (preproc->token.str, "endif"))
+        {
+        }
+      else if (g_str_equal (preproc->token.str, "error"))
+        {
+        }
+      else if (g_str_equal (preproc->token.str, "extension"))
+        {
+        }
+      else if (g_str_equal (preproc->token.str, "if"))
+        {
+        }
+      else if (g_str_equal (preproc->token.str, "ifdef"))
+        {
+        }
+      else if (g_str_equal (preproc->token.str, "ifndef"))
+        {
+        }
+      else if (g_str_equal (preproc->token.str, "line"))
+        {
+        }
+      else if (g_str_equal (preproc->token.str, "pragma"))
+        {
+        }
+      else if (g_str_equal (preproc->token.str, "version"))
+        {
+        }
+#endif
+      else
+        {
+          gsk_sl_preprocessor_error (preproc, "Unknown preprocessor directive #%s.", preproc->token.str);
+        }
+    }
+  else if (gsk_sl_token_is (&preproc->token, GSK_SL_TOKEN_ELSE))
+    {
+      gsk_sl_preprocessor_error (preproc, "Unknown preprocessor directive #else.");
+    }
+  else
+    {
+      gsk_sl_preprocessor_error (preproc, "Missing identifier for preprocessor directive.");
+    }
+
+  while (!gsk_sl_preprocessor_next_token (preproc));
+}
+
+static void
+gsk_sl_preprocessor_ensure (GskSlPreprocessor *preproc)
+{
+  gboolean was_newline = FALSE;
+
+  if (!gsk_sl_token_is (&preproc->token, GSK_SL_TOKEN_EOF))
+    return;
+
+  was_newline = gsk_sl_preprocessor_next_token (preproc);
+
+  while (TRUE)
+    {
+      if (gsk_sl_token_is (&preproc->token, GSK_SL_TOKEN_HASH))
+        {
+          if (!was_newline &&
+              preproc->location.bytes != 0)
+            {
+              gsk_sl_preprocessor_error (preproc, "Unexpected \"#\" - preprocessor directives must be at 
start of line.");
+              was_newline = gsk_sl_preprocessor_next_token (preproc);
+            }
+          else
+            {
+              gsk_sl_preprocessor_handle_preprocessor_directive (preproc);
+              was_newline = TRUE;
+            }
+        }
+      else
+        {
+          break;
+        }
+    }
 }
 
 const GskSlToken *
 gsk_sl_preprocessor_get (GskSlPreprocessor *preproc)
 {
-  gsk_sl_token_ensure (preproc);
+  gsk_sl_preprocessor_ensure (preproc);
 
   return &preproc->token;
 }
@@ -123,7 +226,7 @@ gsk_sl_preprocessor_get (GskSlPreprocessor *preproc)
 const GskCodeLocation *
 gsk_sl_preprocessor_get_location (GskSlPreprocessor *preproc)
 {
-  gsk_sl_token_ensure (preproc);
+  gsk_sl_preprocessor_ensure (preproc);
 
   return &preproc->location;
 }
@@ -132,7 +235,7 @@ void
 gsk_sl_preprocessor_consume (GskSlPreprocessor *preproc,
                              GskSlNode         *consumer)
 {
-  gsk_sl_token_ensure (preproc);
+  gsk_sl_preprocessor_ensure (preproc);
 
   gsk_sl_token_clear (&preproc->token);
 }
@@ -152,7 +255,7 @@ gsk_sl_preprocessor_error (GskSlPreprocessor *preproc,
                               args);
   va_end (args);
 
-  gsk_sl_token_ensure (preproc);
+  gsk_sl_preprocessor_ensure (preproc);
   gsk_sl_preprocessor_error_func (preproc->tokenizer,
                                   TRUE,
                                   &preproc->location,
diff --git a/gsk/gsksltokenizer.c b/gsk/gsksltokenizer.c
index fa9a071..aa9c343 100644
--- a/gsk/gsksltokenizer.c
+++ b/gsk/gsksltokenizer.c
@@ -90,6 +90,7 @@ gsk_sl_token_clear (GskSlToken *token)
 
     case GSK_SL_TOKEN_EOF:
     case GSK_SL_TOKEN_ERROR:
+    case GSK_SL_TOKEN_NEWLINE:
     case GSK_SL_TOKEN_WHITESPACE:
     case GSK_SL_TOKEN_COMMENT:
     case GSK_SL_TOKEN_SINGLE_LINE_COMMENT:
@@ -296,6 +297,7 @@ gsk_sl_token_clear (GskSlToken *token)
     case GSK_SL_TOKEN_CARET:
     case GSK_SL_TOKEN_AMPERSAND:
     case GSK_SL_TOKEN_QUESTION:
+    case GSK_SL_TOKEN_HASH:
     case GSK_SL_TOKEN_INVARIANT:
     case GSK_SL_TOKEN_PRECISE:
     case GSK_SL_TOKEN_HIGH_PRECISION:
@@ -487,6 +489,7 @@ gsk_sl_token_print (const GskSlToken *token,
     case GSK_SL_TOKEN_SINGLE_LINE_COMMENT:
       break;
 
+    case GSK_SL_TOKEN_NEWLINE:
     case GSK_SL_TOKEN_WHITESPACE:
       g_string_append (string, " ");
       break;
@@ -864,6 +867,10 @@ gsk_sl_token_print (const GskSlToken *token,
       g_string_append_c (string, '?');
       break;
 
+    case GSK_SL_TOKEN_HASH:
+      g_string_append_c (string, '#');
+      break;
+
     default:
       g_assert_not_reached ();
       break;
@@ -979,8 +986,7 @@ is_whitespace (char c)
   return c == ' '
       || c == '\t'
       || c == '\f'
-      || c == '\n'
-      || c == '\r';
+      || is_newline (c);
 }
 
 static inline gsize
@@ -1187,11 +1193,18 @@ static void
 gsk_sl_token_reader_read_whitespace (GskSlTokenReader  *reader,
                                      GskSlToken        *token)
 {
-  do {
-    gsk_sl_token_reader_consume (reader, 1);
-  } while (is_whitespace (gsk_sl_token_reader_get (reader, 0)));
+  gboolean has_newline = FALSE;
+  char c;
 
-  gsk_sl_token_init (token, GSK_SL_TOKEN_WHITESPACE);
+  for (c = gsk_sl_token_reader_get (reader, 0);
+       is_whitespace (c);
+       c = gsk_sl_token_reader_get (reader, 0))
+    {
+      has_newline |= is_newline (c);
+      gsk_sl_token_reader_consume (reader, 1);
+    }
+
+  gsk_sl_token_init (token, has_newline ? GSK_SL_TOKEN_NEWLINE : GSK_SL_TOKEN_WHITESPACE);
 }
 
 static gboolean
@@ -1798,6 +1811,11 @@ gsk_sl_tokenizer_read_token (GskSlTokenizer *tokenizer,
       gsk_sl_token_reader_consume (&reader, 1);
       break;
 
+    case '#':
+      gsk_sl_token_init (token, GSK_SL_TOKEN_HASH);
+      gsk_sl_token_reader_consume (&reader, 1);
+      break;
+
     case ',':
       gsk_sl_token_init (token, GSK_SL_TOKEN_COMMA);
       gsk_sl_token_reader_consume (&reader, 1);
diff --git a/gsk/gsksltokenizerprivate.h b/gsk/gsksltokenizerprivate.h
index ea769fc..ec2fc92 100644
--- a/gsk/gsksltokenizerprivate.h
+++ b/gsk/gsksltokenizerprivate.h
@@ -25,6 +25,7 @@ G_BEGIN_DECLS
 typedef enum {
   GSK_SL_TOKEN_EOF = 0,
   GSK_SL_TOKEN_ERROR,
+  GSK_SL_TOKEN_NEWLINE,
   GSK_SL_TOKEN_WHITESPACE,
   GSK_SL_TOKEN_COMMENT,
   GSK_SL_TOKEN_SINGLE_LINE_COMMENT,
@@ -232,6 +233,7 @@ typedef enum {
   GSK_SL_TOKEN_CARET,
   GSK_SL_TOKEN_AMPERSAND,
   GSK_SL_TOKEN_QUESTION,
+  GSK_SL_TOKEN_HASH,
   GSK_SL_TOKEN_INVARIANT,
   GSK_SL_TOKEN_PRECISE,
   GSK_SL_TOKEN_HIGH_PRECISION,
diff --git a/gsk/gsksltype.c b/gsk/gsksltype.c
index b295872..6259efe 100644
--- a/gsk/gsksltype.c
+++ b/gsk/gsksltype.c
@@ -22,6 +22,7 @@
 
 #include "gsksltokenizerprivate.h"
 #include "gskslpreprocessorprivate.h"
+#include "gskspvwriterprivate.h"
 
 #include <string.h>
 
@@ -45,6 +46,8 @@ struct _GskSlTypeClass {
   guint                 (* get_length)                          (const GskSlType     *type);
   gboolean              (* can_convert)                         (const GskSlType     *target,
                                                                  const GskSlType     *source);
+  guint32               (* write_spv)                           (const GskSlType     *type,
+                                                                 GskSpvWriter        *writer);
 };
 
 /* SCALAR */
@@ -121,13 +124,85 @@ gsk_sl_type_scalar_can_convert (const 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 */
@@ -196,13 +271,33 @@ gsk_sl_type_vector_can_convert (const 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 */
@@ -273,13 +368,33 @@ gsk_sl_type_matrix_can_convert (const 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 *
@@ -634,3 +749,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 3fd84c3..bcf80e7 100644
--- a/gsk/gsksltypeprivate.h
+++ b/gsk/gsksltypeprivate.h
@@ -58,6 +58,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 d160d62..f9d82ea 100644
--- a/gsk/gsksltypesprivate.h
+++ b/gsk/gsksltypesprivate.h
@@ -29,4 +29,6 @@ typedef struct _GskSlScope              GskSlScope;
 typedef struct _GskSlToken              GskSlToken;
 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..dda6402
--- /dev/null
+++ b/gsk/gskspvwriter.c
@@ -0,0 +1,241 @@
+/* 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);
+  /* the ID 1 is reserved for the GLSL instruction set (for now) */
+  writer->last_id = 1;
+
+  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)
+{
+  guchar instruction_set[] = "\0\0\0\0GLSL.std.450\0\0\0\0";
+
+  gsk_spv_writer_add (writer,
+                      GSK_SPV_WRITER_SECTION_HEADER,
+                      2, GSK_SPV_OP_CAPABILITY,
+                      (guint32[1]) { GSK_SPV_CAPABILITY_SHADER });
+  *(guint32 *) instruction_set = 1;
+  gsk_spv_writer_add (writer,
+                      GSK_SPV_WRITER_SECTION_HEADER,
+                      1 + sizeof (instruction_set) / 4, GSK_SPV_OP_EXT_INST_IMPORT,
+                      (guint32 *) instruction_set);
+  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) { 0 });
+  
+  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 5b4f2ae..77fdfd5 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -38,7 +38,8 @@ gsk_private_sources = files([
   'gskslpointertype.c',
   'gskslscope.c',
   'gsksltokenizer.c',
-  'gsksltype.c'
+  'gsksltype.c',
+  'gskspvwriter.c'
 ])
 
 gsk_public_headers = files([
diff --git a/gtk/glsl.c b/gtk/glsl.c
index 4e7f9d1..ec5c0ee 100644
--- a/gtk/glsl.c
+++ b/gtk/glsl.c
@@ -46,7 +46,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)
 {
@@ -64,6 +101,7 @@ dump (GOutputStream *output,
     }
 
   program = gsk_sl_node_new_program (bytes, NULL);
+  g_bytes_unref (bytes);
   if (program == NULL)
     return FALSE;
 
@@ -100,7 +138,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, }
@@ -161,13 +201,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]