<div dir="ltr">On 1 November 2013 02:16, Tapani Pälli <span dir="ltr"><<a href="mailto:tapani.palli@intel.com" target="_blank">tapani.palli@intel.com</a>></span> wrote:<br><div class="gmail_extra"><div class="gmail_quote">
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">These utility functions can be used to (de)serialize gl_shader and<br>
gl_shader_program structures. This makes it possible to implement a<br>
shader compiler cache for individual shaders and functionality required<br>
by OES_get_program_binary extension.<br>
<br>
Signed-off-by: Tapani Pälli <<a href="mailto:tapani.palli@intel.com">tapani.palli@intel.com</a>><br>
---<br>
 src/glsl/Makefile.sources |   1 +<br>
 src/glsl/ir_cache.cpp     | 373 ++++++++++++++++++++++++++++++++++++++++++++++<br>
 src/glsl/ir_cache.h       |  58 +++++++<br>
 3 files changed, 432 insertions(+)<br>
 create mode 100644 src/glsl/ir_cache.cpp<br>
 create mode 100644 src/glsl/ir_cache.h<br>
<br>
diff --git a/src/glsl/Makefile.sources b/src/glsl/Makefile.sources<br>
index 81d5753..99b3c1a 100644<br>
--- a/src/glsl/Makefile.sources<br>
+++ b/src/glsl/Makefile.sources<br>
@@ -30,6 +30,7 @@ LIBGLSL_FILES = \<br>
        $(GLSL_SRCDIR)/hir_field_selection.cpp \<br>
        $(GLSL_SRCDIR)/ir_basic_block.cpp \<br>
        $(GLSL_SRCDIR)/ir_builder.cpp \<br>
+       $(GLSL_SRCDIR)/ir_cache.cpp \<br>
        $(GLSL_SRCDIR)/ir_cache_serializer.cpp \<br>
        $(GLSL_SRCDIR)/ir_cache_deserializer.cpp \<br>
        $(GLSL_SRCDIR)/ir_clone.cpp \<br>
diff --git a/src/glsl/ir_cache.cpp b/src/glsl/ir_cache.cpp<br>
new file mode 100644<br>
index 0000000..24e1c77<br>
--- /dev/null<br>
+++ b/src/glsl/ir_cache.cpp<br>
@@ -0,0 +1,373 @@<br>
+/* -*- c++ -*- */<br>
+/*<br>
+ * Copyright © 2013 Intel Corporation<br>
+ *<br>
+ * Permission is hereby granted, free of charge, to any person obtaining a<br>
+ * copy of this software and associated documentation files (the "Software"),<br>
+ * to deal in the Software without restriction, including without limitation<br>
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,<br>
+ * and/or sell copies of the Software, and to permit persons to whom the<br>
+ * Software is furnished to do so, subject to the following conditions:<br>
+ *<br>
+ * The above copyright notice and this permission notice (including the next<br>
+ * paragraph) shall be included in all copies or substantial portions of the<br>
+ * Software.<br>
+ *<br>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br>
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br>
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL<br>
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br>
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING<br>
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER<br>
+ * DEALINGS IN THE SOFTWARE.<br>
+ */<br>
+<br>
+#include "main/shaderobj.h"<br>
+#include "main/uniforms.h"<br>
+#include "main/macros.h"<br>
+<br>
+#include "ir_cache_serializer.h"<br>
+#include "ir_cache_deserializer.h"<br>
+<br>
+/**<br>
+ * Serialize gl_shader structure<br>
+ */<br>
+extern "C" char *<br>
+_mesa_shader_serialize(struct gl_shader *shader,<br>
+   struct _mesa_glsl_parse_state *state,<br>
+   const char *mesa_sha, size_t *size)<br>
+{<br>
+   ir_serializer s;<br>
+   return s.serialize(shader, state, mesa_sha, size);<br>
+}<br>
+<br>
+<br>
+static void<br>
+calc_item(const void *key, void *data, void *closure)<br></blockquote><div><br></div><div>How about "increment_count" for the name of this function? <br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

+{<br>
+   unsigned *sz = (unsigned *) closure;<br>
+   *sz = *sz + 1;<br>
+}<br>
+<br>
+<br>
+static unsigned<br>
+_hash_table_size(struct string_to_uint_map *map)<br></blockquote><div><br></div><div>This function is global, so its name can't start with an underscore--such names are reserved for library functions.  Same goes for many of the other functions in this file. <br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+{<br>
+   unsigned size = 0;<br>
+   map->iterate(calc_item, &size);<br>
+   return size;<br>
+}<br>
+<br>
+<br>
+static void<br>
+serialize_item(const void *key, void *data, void *closure)<br>
+{<br>
+   memory_writer *blob = (memory_writer *) closure;<br>
+   uint32_t value = ((intptr_t)data);<br>
+<br>
+   blob->write_string((char *)key);<br>
+   blob->write_uint32(&value);<br>
+}<br>
+<br>
+<br>
+static void<br>
+_serialize_hash_table(struct string_to_uint_map *map, memory_writer *blob)<br>
+{<br>
+   uint32_t size = _hash_table_size(map);<br>
+   blob->write_uint32(&size);<br>
+   map->iterate(serialize_item, blob);<br></blockquote><div><br></div><div>Rather than take two passes over the hash table (one to compute its size and one to output its contents), why not do the trick that we do with overwrite() in ir_cache_serializer.cpp?  (In other words, reserve space for the size of the hashtable in bytes, then serialize the hashtable, then overwrite the reserved space with the actual size).<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+}<br>
+<br>
+<br>
+static void<br>
+_serialize_uniform_storage(gl_uniform_storage *uni, memory_writer &blob)<br>
+{<br>
+   blob.write_string(uni->name);<br>
+<br>
+   save_glsl_type(blob, uni->type);<br>
+<br>
+   uint8_t initialized = uni->initialized;<br>
+   uint8_t row_major = uni->row_major;<br>
+<br>
+   blob.write_uint32(&uni->array_elements);<br>
+   blob.write_uint8(&initialized);<br>
+   blob.write_int32(&uni->block_index);<br>
+   blob.write_int32(&uni->offset);<br>
+   blob.write_int32(&uni->matrix_stride);<br>
+   blob.write_uint8(&row_major);<br>
+   blob.write_int32(&uni->atomic_buffer_index);<br>
+<br>
+   for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {<br>
+      uint8_t active = uni->sampler[i].active;<br>
+      blob.write_uint8(&uni->sampler[i].index);<br>
+      blob.write_uint8(&active);<br>
+   }<br>
+<br>
+   const unsigned elements = MAX2(1, uni->array_elements);<br>
+   const unsigned data_components = elements * uni->type->components();<br>
+   uint32_t size = elements * MAX2(1, data_components);<br>
+<br>
+   CACHE_DEBUG("%s: size %ld\n", __func__,<br>
+               size * sizeof(union gl_constant_value));<br>
+<br>
+   blob.write_uint32(&size);<br>
+   blob.write(uni->storage, size * sizeof(union gl_constant_value));<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * Serialize gl_shader_program structure<br>
+ */<br>
+extern "C" char *<br>
+_mesa_program_serialize(struct gl_shader_program *prog, size_t *size,<br>
+   const char *mesa_sha)<br>
+{<br>
+   memory_writer blob;<br>
+<br>
+   blob.write_uint32(&prog->Type);<br>
+   blob.write_uint32(&prog->NumShaders);<br>
+   blob.write_uint8(&prog->LinkStatus);<br>
+   blob.write_uint32(&prog->Version);<br>
+   blob.write_uint8(&prog->IsES);<br>
+   blob.write_uint32(&prog->NumUserUniformStorage);<br>
+   blob.write_uint32(&prog->UniformLocationBaseScale);<br>
+<br>
+   /* hash tables */<br>
+   _serialize_hash_table(prog->AttributeBindings, &blob);<br>
+   _serialize_hash_table(prog->FragDataBindings, &blob);<br>
+   _serialize_hash_table(prog->FragDataIndexBindings, &blob);<br>
+   _serialize_hash_table(prog->UniformHash, &blob);<br>
+<br>
+   /* uniform storage */<br>
+   if (prog->UniformStorage) {<br>
+      for (unsigned i = 0; i < prog->NumUserUniformStorage; ++i)<br>
+         _serialize_uniform_storage(&prog->UniformStorage[i], blob);<br>
+   }<br>
+<br>
+   /* Shaders IR, to be decided if we want these to be available */<br></blockquote><div><br></div><div>Has there been previous discussion on the mesa-dev list about whether or not we want these to be available?  If so, please include a link to previous discussion and a brief summary.  if not, what does the decision hinge on?  At first blush it seems to me that there's no reason to serialize the IR of the individual shaders, since ARB_get_program_binary operates on full programs rather than individual shaders.<br>
<br></div><div>Also, typically we prefer to disable code using "if (false)" rather than ifdefs, because that ensures that the disabled code still compiles.  (Otherwise, it's much more likely to bit rot as the rest of the code base evolves).<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+#if 0<br>
+   for (unsigned i = 0; i < prog->NumShaders; i++) {<br>
+      size_t sha_size = 0;<br>
+      char *data = _mesa_shader_serialize(prog->Shaders[i],<br>
+         NULL, mesa_sha, &sha_size);<br>
+<br>
+      if (data) {<br>
+         blob.write(data, sha_size);<br>
+         free(data);<br>
+      }<br>
+   }<br>
+#endif<br>
+<br>
+   /* _LinkedShaders IR */<br>
+   for (uint32_t i = 0; i < MESA_SHADER_TYPES; i++) {<br>
+      size_t sha_size = 0;<br>
+<br>
+      if (!prog->_LinkedShaders[i])<br>
+         continue;<br>
+<br>
+      char *data = _mesa_shader_serialize(prog->_LinkedShaders[i],<br>
+         NULL, mesa_sha, &sha_size);<br>
+<br>
+      if (!data) {<br>
+         CACHE_DEBUG("error serializing data for index %d\n", i);<br>
+         return NULL;<br>
+      }<br>
+<br>
+      /* index in _LinkedShaders list + shader blob */<br>
+      if (data) {<br>
+         blob.write_uint32(&i);<br>
+         blob.write(data, sha_size);<br>
+         free(data);<br>
+      }<br>
+   }<br></blockquote><div><br></div><div>Are we going to attempt to store the back-end compiled shaders?  It seems like we'd have to do that in order to get a lot of the benefit of ARB_get_program_binary.<br></div><div>
 </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+   *size = blob.position();<br>
+   return blob.release_memory(size);<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * Deserialize gl_shader structure<br>
+ */<br>
+extern "C" struct gl_shader *<br>
+_mesa_shader_deserialize(void *mem_ctx, void *blob,<br>
+   const char *mesa_sha, size_t size)<br>
+{<br>
+   int error = 0;<br>
+   ir_deserializer s;<br>
+   memory_map map;<br>
+<br>
+   map.map(blob, size);<br>
+<br>
+   return s.deserialize(mem_ctx, map, size,<br>
+      NULL,<br>
+      mesa_sha,<br>
+      &error);<br>
+}<br>
+<br>
+<br>
+static void<br>
+_read_hash_table(struct string_to_uint_map *hash, memory_map *map)<br>
+{<br>
+   unsigned size;<br>
+   map->read(&size);<br>
+<br>
+   for (unsigned i = 0; i < size; i++) {<br>
+      unsigned value;<br>
+<br>
+      char *key = map->read_string();<br>
+      map->read(&value);<br>
+<br>
+      hash->put(value-1, key);<br>
+   }<br>
+}<br>
+<br>
+<br>
+static void<br>
+_read_uniform_storage(void *mem_ctx, gl_uniform_storage *uni,<br>
+   memory_map &map, struct _mesa_glsl_parse_state *state)<br>
+{<br>
+   ir_deserializer s;<br>
+<br>
+   char *name = map.read_string();<br>
+   uni->name = strdup(name);<br>
+<br>
+   uni->type = s.read_glsl_type(map, NULL);<br>
+<br>
+   map.read(&uni->array_elements);<br>
+   map.read(&uni->initialized);<br>
+   map.read(&uni->block_index);<br>
+   map.read(&uni->offset);<br>
+   map.read(&uni->matrix_stride);<br>
+   map.read(&uni->row_major);<br>
+   map.read(&uni->atomic_buffer_index);<br>
+<br>
+   for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {<br>
+      map.read(&uni->sampler[i].index);<br>
+      map.read(&uni->sampler[i].active);<br>
+   }<br>
+<br>
+   uint32_t size;<br>
+   map.read(&size);<br>
+<br>
+   CACHE_DEBUG("read uniform storage size %ld\n",<br>
+               size * sizeof(union gl_constant_value));<br>
+<br>
+   uni->storage =<br>
+      rzalloc_array(mem_ctx, union gl_constant_value, size);<br>
+<br>
+   map.read(uni->storage, size * sizeof(union gl_constant_value));<br>
+<br>
+   /* driver uniform storage gets generated and propagated later */<br>
+   uni->driver_storage = NULL;<br>
+   uni->num_driver_storage = 0;<br>
+}<br>
+<br>
+<br>
+/**<br>
+ * Deserialize gl_shader_program structure<br>
+ */<br>
+extern "C" int<br>
+_mesa_program_deserialize(struct gl_shader_program *prog,<br>
+   const GLvoid *blob, size_t size, const char *mesa_sha)<br>
+{<br>
+   memory_map map;<br>
+   map.map((const void*)blob, size);<br>
+<br>
+   map.read(&prog->Type);<br>
+   map.read(&prog->NumShaders);<br>
+   map.read(&prog->LinkStatus);<br>
+   map.read(&prog->Version);<br>
+   map.read(&prog->IsES);<br>
+<br>
+   prog->NumUserUniformStorage = 0;<br>
+   prog->UniformStorage = NULL;<br>
+   prog->Label = NULL;<br>
+<br>
+   map.read(&prog->NumUserUniformStorage);<br>
+   map.read(&prog->UniformLocationBaseScale);<br>
+<br>
+   /* these already allocated by _mesa_init_shader_program */<br>
+   _read_hash_table(prog->AttributeBindings, &map);<br>
+   _read_hash_table(prog->FragDataBindings, &map);<br>
+   _read_hash_table(prog->FragDataIndexBindings, &map);<br>
+<br>
+   prog->UniformHash = new string_to_uint_map;<br>
+   _read_hash_table(prog->UniformHash, &map);<br>
+<br>
+   /* just zero for now */<br>
+   prog->LinkedTransformFeedback.Outputs = NULL;<br>
+   prog->LinkedTransformFeedback.Varyings = NULL;<br>
+   prog->LinkedTransformFeedback.NumVarying = 0;<br>
+   prog->LinkedTransformFeedback.NumOutputs = 0;<br>
+<br>
+   /* uniform storage */<br>
+   prog->UniformStorage = rzalloc_array(prog, struct gl_uniform_storage,<br>
+      prog->NumUserUniformStorage);<br>
+<br>
+   for (unsigned i = 0; i < prog->NumUserUniformStorage; i++)<br>
+      _read_uniform_storage(prog, &prog->UniformStorage[i], map, NULL);<br>
+<br>
+   GET_CURRENT_CONTEXT(ctx);<br>
+<br>
+   /**<br>
+    * prog->Shaders is not strictly required, however we might want to be<br>
+    * able to recompile and relink these programs? One disadvantage is that<br>
+    * it makes the binary blobs a lot bigger<br>
+    */<br></blockquote><div><br></div><div>As I commented above, I don't see a reason to store the unlinked shaders.  However, if we do decide we want to keep that option open, I'd recommend putting something like this near the top of the file:<br>
<br></div><div>const bool STORE_UNLINKED_SHADERS = false;<br><br></div><div>along with a comment explaining the pros and cons of storing the unlinked shaders.  Then, here in the code, use<br><br></div><div>if (STORE_UNLINKED_SHADERS)<br>
<br>rather than<br><br>#if 0<br><br>That way, if we decide to enable storage of unlinked shaders, there's no danger that we'll accidentally enable it in one place but not the other.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

+#if 0<br>
+   /* Shaders array (unlinked */<br>
+   prog->Shaders = (struct gl_shader **)<br>
+      _mesa_realloc(prog->Shaders, 0,<br>
+                    (prog->NumShaders) * sizeof(struct gl_shader *));<br>
+<br>
+   for (unsigned i = 0; i < prog->NumShaders; i++) {<br>
+<br>
+      struct gl_shader *sha = map.read_shader(prog, mesa_sha);<br>
+<br>
+      if (sha) {<br>
+         prog->Shaders[i] = NULL; /* alloc did not initialize */<br>
+         _mesa_reference_shader(ctx, &prog->Shaders[i], sha);<br>
+         CACHE_DEBUG("%s: read unlinked shader, index %d (%p) size %d\n",<br>
+                     __func__, i, sha, shader_size);<br>
+      }<br>
+   }<br>
+#else<br>
+   prog->Shaders = NULL;<br>
+   prog->NumShaders = 0;<br>
+#endif<br>
+<br>
+   /* init list, cache can contain only some shader types */<br>
+   for (unsigned i = 0; i < MESA_SHADER_TYPES; i++)<br>
+      prog->_LinkedShaders[i] = NULL;<br>
+<br>
+   /* read _LinkedShaders */<br>
+   while(map.position() < size) {<br>
+      unsigned index;<br>
+      map.read(&index);<br>
+<br>
+      struct gl_shader *sha = map.read_shader(prog, mesa_sha);<br>
+<br>
+      if (!sha) {<br>
+         CACHE_DEBUG("failed to read shader (index %d)\n", index);<br>
+         return -1;<br>
+      }<br>
+<br>
+#if 0<br></blockquote><div><br></div><div>This should also use "if (false)"<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+      {<br>
+         GET_CURRENT_CONTEXT(ctx);<br>
+         _mesa_glsl_parse_state *state =<br>
+            new(sha) _mesa_glsl_parse_state(ctx, sha->Type, sha);<br>
+         printf("\n");<br>
+         _mesa_print_ir(sha->ir, state);<br>
+         printf("\n");<br>
+      }<br>
+#endif<br>
+<br>
+      _mesa_reference_shader(ctx, &prog->_LinkedShaders[index], sha);<br>
+      CACHE_DEBUG("%s: read a linked shader, index %d (%p)\n",<br>
+                  __func__, index, sha);<br>
+   }<br>
+<br>
+   return 0;<br>
+}<br>
diff --git a/src/glsl/ir_cache.h b/src/glsl/ir_cache.h<br>
new file mode 100644<br>
index 0000000..0146b48<br>
--- /dev/null<br>
+++ b/src/glsl/ir_cache.h<br>
@@ -0,0 +1,58 @@<br>
+/* -*- c++ -*- */<br>
+/*<br>
+ * Copyright © 2013 Intel Corporation<br>
+ *<br>
+ * Permission is hereby granted, free of charge, to any person obtaining a<br>
+ * copy of this software and associated documentation files (the "Software"),<br>
+ * to deal in the Software without restriction, including without limitation<br>
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,<br>
+ * and/or sell copies of the Software, and to permit persons to whom the<br>
+ * Software is furnished to do so, subject to the following conditions:<br>
+ *<br>
+ * The above copyright notice and this permission notice (including the next<br>
+ * paragraph) shall be included in all copies or substantial portions of the<br>
+ * Software.<br>
+ *<br>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br>
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br>
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL<br>
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br>
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING<br>
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER<br>
+ * DEALINGS IN THE SOFTWARE.<br>
+ */<br>
+<br>
+#pragma once<br>
+#ifndef IR_CACHE_H<br>
+#define IR_CACHE_H<br>
+<br>
+/* cache specific debug output */<br>
+#ifdef SHADER_CACHE_DEBUG<br>
+#define CACHE_DEBUG(fmt, ...) printf(fmt, ## __VA_ARGS__)<br>
+#else<br>
+#define CACHE_DEBUG(fmt, ...) do {} while (0)<br>
+#endif<br>
+<br>
+/* C API for the cache */<br>
+#ifdef __cplusplus<br>
+extern "C" {<br>
+#endif<br>
+<br>
+char *_mesa_shader_serialize(struct gl_shader *shader,<br>
+   struct _mesa_glsl_parse_state *state,<br>
+   const char *mesa_sha, size_t *size);<br>
+<br>
+struct gl_shader *_mesa_shader_deserialize(void *mem_ctx,<br>
+   void *blob, const char *mesa_sha, size_t size);<br>
+<br>
+char *_mesa_program_serialize(struct gl_shader_program *prog,<br>
+   size_t *size, const char *mesa_sha);<br>
+<br>
+int _mesa_program_deserialize(struct gl_shader_program *prog,<br>
+   const GLvoid *blob, size_t size, const char *mesa_sha);<br>
+<br>
+#ifdef __cplusplus<br>
+} /* extern "C" */<br>
+#endif<br>
+<br>
+#endif /* IR_CACHE_H */<br>
<span class="HOEnZb"><font color="#888888">--<br>
1.8.1.4<br>
<br>
</font></span></blockquote></div><br></div></div>