(new T(std::forward(args)...));
+ auto ptr = uptr.get();
+ var.set(std::move(uptr), T::type);
+ return *ptr;
+}
+
+struct Meta
+{
+ struct Decoration
+ {
+ std::string alias;
+ std::string qualified_alias;
+ uint64_t decoration_flags = 0;
+ spv::BuiltIn builtin_type;
+ uint32_t location = 0;
+ uint32_t set = 0;
+ uint32_t binding = 0;
+ uint32_t offset = 0;
+ uint32_t array_stride = 0;
+ uint32_t input_attachment = 0;
+ uint32_t spec_id = 0;
+ bool builtin = false;
+ bool per_instance = false;
+ };
+
+ Decoration decoration;
+ std::vector members;
+ uint32_t sampler = 0;
+};
+
+// A user callback that remaps the type of any variable.
+// var_name is the declared name of the variable.
+// name_of_type is the textual name of the type which will be used in the code unless written to by the callback.
+using VariableTypeRemapCallback =
+ std::function;
+}
+
+#endif
diff --git a/deps/SPIRV-Cross/spirv_cpp.cpp b/deps/SPIRV-Cross/spirv_cpp.cpp
new file mode 100644
index 0000000000..223a954131
--- /dev/null
+++ b/deps/SPIRV-Cross/spirv_cpp.cpp
@@ -0,0 +1,511 @@
+/*
+ * Copyright 2015-2016 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "spirv_cpp.hpp"
+
+using namespace spv;
+using namespace spirv_cross;
+using namespace std;
+
+void CompilerCPP::emit_buffer_block(const SPIRVariable &var)
+{
+ add_resource_name(var.self);
+
+ auto &type = get(var.basetype);
+ auto instance_name = to_name(var.self);
+
+ uint32_t descriptor_set = meta[var.self].decoration.set;
+ uint32_t binding = meta[var.self].decoration.binding;
+
+ emit_block_struct(type);
+ auto buffer_name = to_name(type.self);
+
+ statement("internal::Resource<", buffer_name, type_to_array_glsl(type), "> ", instance_name, "__;");
+ statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
+ resource_registrations.push_back(
+ join("s.register_resource(", instance_name, "__", ", ", descriptor_set, ", ", binding, ");"));
+ statement("");
+}
+
+void CompilerCPP::emit_interface_block(const SPIRVariable &var)
+{
+ add_resource_name(var.self);
+
+ auto &type = get(var.basetype);
+
+ const char *qual = var.storage == StorageClassInput ? "StageInput" : "StageOutput";
+ const char *lowerqual = var.storage == StorageClassInput ? "stage_input" : "stage_output";
+ auto instance_name = to_name(var.self);
+ uint32_t location = meta[var.self].decoration.location;
+
+ string buffer_name;
+ auto flags = meta[type.self].decoration.decoration_flags;
+ if (flags & (1ull << DecorationBlock))
+ {
+ emit_block_struct(type);
+ buffer_name = to_name(type.self);
+ }
+ else
+ buffer_name = type_to_glsl(type);
+
+ statement("internal::", qual, "<", buffer_name, type_to_array_glsl(type), "> ", instance_name, "__;");
+ statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
+ resource_registrations.push_back(join("s.register_", lowerqual, "(", instance_name, "__", ", ", location, ");"));
+ statement("");
+}
+
+void CompilerCPP::emit_shared(const SPIRVariable &var)
+{
+ add_resource_name(var.self);
+
+ auto instance_name = to_name(var.self);
+ statement(CompilerGLSL::variable_decl(var), ";");
+ statement_no_indent("#define ", instance_name, " __res->", instance_name);
+}
+
+void CompilerCPP::emit_uniform(const SPIRVariable &var)
+{
+ add_resource_name(var.self);
+
+ auto &type = get(var.basetype);
+ auto instance_name = to_name(var.self);
+
+ uint32_t descriptor_set = meta[var.self].decoration.set;
+ uint32_t binding = meta[var.self].decoration.binding;
+ uint32_t location = meta[var.self].decoration.location;
+
+ string type_name = type_to_glsl(type);
+ remap_variable_type_name(type, instance_name, type_name);
+
+ if (type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage ||
+ type.basetype == SPIRType::AtomicCounter)
+ {
+ statement("internal::Resource<", type_name, type_to_array_glsl(type), "> ", instance_name, "__;");
+ statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
+ resource_registrations.push_back(
+ join("s.register_resource(", instance_name, "__", ", ", descriptor_set, ", ", binding, ");"));
+ }
+ else
+ {
+ statement("internal::UniformConstant<", type_name, type_to_array_glsl(type), "> ", instance_name, "__;");
+ statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
+ resource_registrations.push_back(
+ join("s.register_uniform_constant(", instance_name, "__", ", ", location, ");"));
+ }
+
+ statement("");
+}
+
+void CompilerCPP::emit_push_constant_block(const SPIRVariable &var)
+{
+ add_resource_name(var.self);
+
+ auto &type = get(var.basetype);
+ auto &flags = meta[var.self].decoration.decoration_flags;
+ if ((flags & (1ull << DecorationBinding)) || (flags & (1ull << DecorationDescriptorSet)))
+ SPIRV_CROSS_THROW("Push constant blocks cannot be compiled to GLSL with Binding or Set syntax. "
+ "Remap to location with reflection API first or disable these decorations.");
+
+ emit_block_struct(type);
+ auto buffer_name = to_name(type.self);
+ auto instance_name = to_name(var.self);
+
+ statement("internal::PushConstant<", buffer_name, type_to_array_glsl(type), "> ", instance_name, ";");
+ statement_no_indent("#define ", instance_name, " __res->", instance_name, ".get()");
+ resource_registrations.push_back(join("s.register_push_constant(", instance_name, "__", ");"));
+ statement("");
+}
+
+void CompilerCPP::emit_block_struct(SPIRType &type)
+{
+ // C++ can't do interface blocks, so we fake it by emitting a separate struct.
+ // However, these structs are not allowed to alias anything, so remove it before
+ // emitting the struct.
+ //
+ // The type we have here needs to be resolved to the non-pointer type so we can remove aliases.
+ auto &self = get(type.self);
+ self.type_alias = 0;
+ emit_struct(self);
+}
+
+void CompilerCPP::emit_resources()
+{
+ // Output all basic struct types which are not Block or BufferBlock as these are declared inplace
+ // when such variables are instantiated.
+ for (auto &id : ids)
+ {
+ if (id.get_type() == TypeType)
+ {
+ auto &type = id.get();
+ if (type.basetype == SPIRType::Struct && type.array.empty() && !type.pointer &&
+ (meta[type.self].decoration.decoration_flags &
+ ((1ull << DecorationBlock) | (1ull << DecorationBufferBlock))) == 0)
+ {
+ emit_struct(type);
+ }
+ }
+ }
+
+ statement("struct Resources : ", resource_type);
+ begin_scope();
+
+ // Output UBOs and SSBOs
+ for (auto &id : ids)
+ {
+ if (id.get_type() == TypeVariable)
+ {
+ auto &var = id.get();
+ auto &type = get(var.basetype);
+
+ if (var.storage != StorageClassFunction && type.pointer && type.storage == StorageClassUniform &&
+ !is_hidden_variable(var) && (meta[type.self].decoration.decoration_flags &
+ ((1ull << DecorationBlock) | (1ull << DecorationBufferBlock))))
+ {
+ emit_buffer_block(var);
+ }
+ }
+ }
+
+ // Output push constant blocks
+ for (auto &id : ids)
+ {
+ if (id.get_type() == TypeVariable)
+ {
+ auto &var = id.get();
+ auto &type = get(var.basetype);
+ if (!is_hidden_variable(var) && var.storage != StorageClassFunction && type.pointer &&
+ type.storage == StorageClassPushConstant)
+ {
+ emit_push_constant_block(var);
+ }
+ }
+ }
+
+ // Output in/out interfaces.
+ for (auto &id : ids)
+ {
+ if (id.get_type() == TypeVariable)
+ {
+ auto &var = id.get();
+ auto &type = get(var.basetype);
+
+ if (var.storage != StorageClassFunction && !is_hidden_variable(var) && type.pointer &&
+ (var.storage == StorageClassInput || var.storage == StorageClassOutput) &&
+ interface_variable_exists_in_entry_point(var.self))
+ {
+ emit_interface_block(var);
+ }
+ }
+ }
+
+ // Output Uniform Constants (values, samplers, images, etc).
+ for (auto &id : ids)
+ {
+ if (id.get_type() == TypeVariable)
+ {
+ auto &var = id.get();
+ auto &type = get(var.basetype);
+
+ if (var.storage != StorageClassFunction && !is_hidden_variable(var) && type.pointer &&
+ (type.storage == StorageClassUniformConstant || type.storage == StorageClassAtomicCounter))
+ {
+ emit_uniform(var);
+ }
+ }
+ }
+
+ // Global variables.
+ bool emitted = false;
+ for (auto global : global_variables)
+ {
+ auto &var = get(global);
+ if (var.storage == StorageClassWorkgroup)
+ {
+ emit_shared(var);
+ emitted = true;
+ }
+ }
+
+ if (emitted)
+ statement("");
+
+ statement("inline void init(spirv_cross_shader& s)");
+ begin_scope();
+ statement(resource_type, "::init(s);");
+ for (auto ® : resource_registrations)
+ statement(reg);
+ end_scope();
+ resource_registrations.clear();
+
+ end_scope_decl();
+
+ statement("");
+ statement("Resources* __res;");
+ if (get_entry_point().model == ExecutionModelGLCompute)
+ statement("ComputePrivateResources __priv_res;");
+ statement("");
+
+ // Emit regular globals which are allocated per invocation.
+ emitted = false;
+ for (auto global : global_variables)
+ {
+ auto &var = get(global);
+ if (var.storage == StorageClassPrivate)
+ {
+ if (var.storage == StorageClassWorkgroup)
+ emit_shared(var);
+ else
+ statement(CompilerGLSL::variable_decl(var), ";");
+ emitted = true;
+ }
+ }
+
+ if (emitted)
+ statement("");
+}
+
+string CompilerCPP::compile()
+{
+ // Do not deal with ES-isms like precision, older extensions and such.
+ options.es = false;
+ options.version = 450;
+ backend.float_literal_suffix = true;
+ backend.double_literal_suffix = false;
+ backend.long_long_literal_suffix = true;
+ backend.uint32_t_literal_suffix = true;
+ backend.basic_int_type = "int32_t";
+ backend.basic_uint_type = "uint32_t";
+ backend.swizzle_is_function = true;
+ backend.shared_is_implied = true;
+ backend.flexible_member_array_supported = false;
+ backend.explicit_struct_type = true;
+ backend.use_initializer_list = true;
+
+ uint32_t pass_count = 0;
+ do
+ {
+ if (pass_count >= 3)
+ SPIRV_CROSS_THROW("Over 3 compilation loops detected. Must be a bug!");
+
+ resource_registrations.clear();
+ reset();
+
+ // Move constructor for this type is broken on GCC 4.9 ...
+ buffer = unique_ptr(new ostringstream());
+
+ emit_header();
+ emit_resources();
+
+ emit_function(get(entry_point), 0);
+
+ pass_count++;
+ } while (force_recompile);
+
+ // Match opening scope of emit_header().
+ end_scope_decl();
+ // namespace
+ end_scope();
+
+ // Emit C entry points
+ emit_c_linkage();
+
+ return buffer->str();
+}
+
+void CompilerCPP::emit_c_linkage()
+{
+ statement("");
+
+ statement("spirv_cross_shader_t *spirv_cross_construct(void)");
+ begin_scope();
+ statement("return new ", impl_type, "();");
+ end_scope();
+
+ statement("");
+ statement("void spirv_cross_destruct(spirv_cross_shader_t *shader)");
+ begin_scope();
+ statement("delete static_cast<", impl_type, "*>(shader);");
+ end_scope();
+
+ statement("");
+ statement("void spirv_cross_invoke(spirv_cross_shader_t *shader)");
+ begin_scope();
+ statement("static_cast<", impl_type, "*>(shader)->invoke();");
+ end_scope();
+
+ statement("");
+ statement("static const struct spirv_cross_interface vtable =");
+ begin_scope();
+ statement("spirv_cross_construct,");
+ statement("spirv_cross_destruct,");
+ statement("spirv_cross_invoke,");
+ end_scope_decl();
+
+ statement("");
+ statement("const struct spirv_cross_interface *",
+ interface_name.empty() ? string("spirv_cross_get_interface") : interface_name, "(void)");
+ begin_scope();
+ statement("return &vtable;");
+ end_scope();
+}
+
+void CompilerCPP::emit_function_prototype(SPIRFunction &func, uint64_t)
+{
+ local_variable_names = resource_names;
+ string decl;
+
+ auto &type = get(func.return_type);
+ decl += "inline ";
+ decl += type_to_glsl(type);
+ decl += " ";
+
+ if (func.self == entry_point)
+ {
+ decl += "main";
+ processing_entry_point = true;
+ }
+ else
+ decl += to_name(func.self);
+
+ decl += "(";
+ for (auto &arg : func.arguments)
+ {
+ add_local_variable_name(arg.id);
+
+ decl += argument_decl(arg);
+ if (&arg != &func.arguments.back())
+ decl += ", ";
+
+ // Hold a pointer to the parameter so we can invalidate the readonly field if needed.
+ auto *var = maybe_get(arg.id);
+ if (var)
+ var->parameter = &arg;
+ }
+
+ decl += ")";
+ statement(decl);
+}
+
+string CompilerCPP::argument_decl(const SPIRFunction::Parameter &arg)
+{
+ auto &type = expression_type(arg.id);
+ bool constref = !type.pointer || arg.write_count == 0;
+
+ auto &var = get(arg.id);
+
+ string base = type_to_glsl(type);
+ string variable_name = to_name(var.self);
+ remap_variable_type_name(type, variable_name, base);
+
+ for (uint32_t i = 0; i < type.array.size(); i++)
+ base = join("std::array<", base, ", ", to_array_size(type, i), ">");
+
+ return join(constref ? "const " : "", base, " &", variable_name);
+}
+
+string CompilerCPP::variable_decl(const SPIRType &type, const string &name)
+{
+ string base = type_to_glsl(type);
+ remap_variable_type_name(type, name, base);
+ bool runtime = false;
+
+ for (uint32_t i = 0; i < type.array.size(); i++)
+ {
+ auto &array = type.array[i];
+ if (!array && type.array_size_literal[i])
+ {
+ // Avoid using runtime arrays with std::array since this is undefined.
+ // Runtime arrays cannot be passed around as values, so this is fine.
+ runtime = true;
+ }
+ else
+ base = join("std::array<", base, ", ", to_array_size(type, i), ">");
+ }
+ base += ' ';
+ return base + name + (runtime ? "[1]" : "");
+}
+
+void CompilerCPP::emit_header()
+{
+ auto &execution = get_entry_point();
+
+ statement("// This C++ shader is autogenerated by spirv-cross.");
+ statement("#include \"spirv_cross/internal_interface.hpp\"");
+ statement("#include \"spirv_cross/external_interface.h\"");
+ // Needed to properly implement GLSL-style arrays.
+ statement("#include ");
+ statement("#include ");
+ statement("");
+ statement("using namespace spirv_cross;");
+ statement("using namespace glm;");
+ statement("");
+
+ statement("namespace Impl");
+ begin_scope();
+
+ switch (execution.model)
+ {
+ case ExecutionModelGeometry:
+ case ExecutionModelTessellationControl:
+ case ExecutionModelTessellationEvaluation:
+ case ExecutionModelGLCompute:
+ case ExecutionModelFragment:
+ case ExecutionModelVertex:
+ statement("struct Shader");
+ begin_scope();
+ break;
+
+ default:
+ SPIRV_CROSS_THROW("Unsupported execution model.");
+ }
+
+ switch (execution.model)
+ {
+ case ExecutionModelGeometry:
+ impl_type = "GeometryShader";
+ resource_type = "GeometryResources";
+ break;
+
+ case ExecutionModelVertex:
+ impl_type = "VertexShader";
+ resource_type = "VertexResources";
+ break;
+
+ case ExecutionModelFragment:
+ impl_type = "FragmentShader";
+ resource_type = "FragmentResources";
+ break;
+
+ case ExecutionModelGLCompute:
+ impl_type = join("ComputeShader");
+ resource_type = "ComputeResources";
+ break;
+
+ case ExecutionModelTessellationControl:
+ impl_type = "TessControlShader";
+ resource_type = "TessControlResources";
+ break;
+
+ case ExecutionModelTessellationEvaluation:
+ impl_type = "TessEvaluationShader";
+ resource_type = "TessEvaluationResources";
+ break;
+
+ default:
+ SPIRV_CROSS_THROW("Unsupported execution model.");
+ }
+}
diff --git a/deps/SPIRV-Cross/spirv_cpp.hpp b/deps/SPIRV-Cross/spirv_cpp.hpp
new file mode 100644
index 0000000000..eb77c0b10e
--- /dev/null
+++ b/deps/SPIRV-Cross/spirv_cpp.hpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2015-2016 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SPIRV_CROSS_CPP_HPP
+#define SPIRV_CROSS_CPP_HPP
+
+#include "spirv_glsl.hpp"
+#include
+#include
+
+namespace spirv_cross
+{
+class CompilerCPP : public CompilerGLSL
+{
+public:
+ CompilerCPP(std::vector spirv_)
+ : CompilerGLSL(move(spirv_))
+ {
+ }
+ std::string compile() override;
+
+ // Sets a custom symbol name that can override
+ // spirv_cross_get_interface.
+ //
+ // Useful when several shader interfaces are linked
+ // statically into the same binary.
+ void set_interface_name(std::string name)
+ {
+ interface_name = std::move(name);
+ }
+
+private:
+ void emit_header() override;
+ void emit_c_linkage();
+ void emit_function_prototype(SPIRFunction &func, uint64_t return_flags) override;
+
+ void emit_resources();
+ void emit_buffer_block(const SPIRVariable &type);
+ void emit_push_constant_block(const SPIRVariable &var);
+ void emit_interface_block(const SPIRVariable &type);
+ void emit_block_chain(SPIRBlock &block);
+ void emit_uniform(const SPIRVariable &var);
+ void emit_shared(const SPIRVariable &var);
+ void emit_block_struct(SPIRType &type);
+ std::string variable_decl(const SPIRType &type, const std::string &name) override;
+
+ std::string argument_decl(const SPIRFunction::Parameter &arg);
+
+ std::vector resource_registrations;
+ std::string impl_type;
+ std::string resource_type;
+ uint32_t shared_counter = 0;
+
+ std::string interface_name;
+};
+}
+
+#endif
diff --git a/deps/SPIRV-Cross/spirv_cross.cpp b/deps/SPIRV-Cross/spirv_cross.cpp
new file mode 100644
index 0000000000..7b2d9836a6
--- /dev/null
+++ b/deps/SPIRV-Cross/spirv_cross.cpp
@@ -0,0 +1,3033 @@
+/*
+ * Copyright 2015-2016 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "spirv_cross.hpp"
+#include "GLSL.std.450.h"
+#include "spirv_cfg.hpp"
+#include
+#include
+#include
+
+using namespace std;
+using namespace spv;
+using namespace spirv_cross;
+
+#define log(...) fprintf(stderr, __VA_ARGS__)
+
+Instruction::Instruction(const vector &spirv, uint32_t &index)
+{
+ op = spirv[index] & 0xffff;
+ count = (spirv[index] >> 16) & 0xffff;
+
+ if (count == 0)
+ SPIRV_CROSS_THROW("SPIR-V instructions cannot consume 0 words. Invalid SPIR-V file.");
+
+ offset = index + 1;
+ length = count - 1;
+
+ index += count;
+
+ if (index > spirv.size())
+ SPIRV_CROSS_THROW("SPIR-V instruction goes out of bounds.");
+}
+
+Compiler::Compiler(vector ir)
+ : spirv(move(ir))
+{
+ parse();
+}
+
+string Compiler::compile()
+{
+ return "";
+}
+
+bool Compiler::variable_storage_is_aliased(const SPIRVariable &v)
+{
+ auto &type = get(v.basetype);
+ bool ssbo = (meta[type.self].decoration.decoration_flags & (1ull << DecorationBufferBlock)) != 0;
+ bool image = type.basetype == SPIRType::Image;
+ bool counter = type.basetype == SPIRType::AtomicCounter;
+ bool is_restrict = (meta[v.self].decoration.decoration_flags & (1ull << DecorationRestrict)) != 0;
+ return !is_restrict && (ssbo || image || counter);
+}
+
+bool Compiler::block_is_pure(const SPIRBlock &block)
+{
+ for (auto &i : block.ops)
+ {
+ auto ops = stream(i);
+ auto op = static_cast(i.op);
+
+ switch (op)
+ {
+ case OpFunctionCall:
+ {
+ uint32_t func = ops[2];
+ if (!function_is_pure(get(func)))
+ return false;
+ break;
+ }
+
+ case OpCopyMemory:
+ case OpStore:
+ {
+ auto &type = expression_type(ops[0]);
+ if (type.storage != StorageClassFunction)
+ return false;
+ break;
+ }
+
+ case OpImageWrite:
+ return false;
+
+ // Atomics are impure.
+ case OpAtomicLoad:
+ case OpAtomicStore:
+ case OpAtomicExchange:
+ case OpAtomicCompareExchange:
+ case OpAtomicIIncrement:
+ case OpAtomicIDecrement:
+ case OpAtomicIAdd:
+ case OpAtomicISub:
+ case OpAtomicSMin:
+ case OpAtomicUMin:
+ case OpAtomicSMax:
+ case OpAtomicUMax:
+ case OpAtomicAnd:
+ case OpAtomicOr:
+ case OpAtomicXor:
+ return false;
+
+ // Geometry shader builtins modify global state.
+ case OpEndPrimitive:
+ case OpEmitStreamVertex:
+ case OpEndStreamPrimitive:
+ case OpEmitVertex:
+ return false;
+
+ // Barriers disallow any reordering, so we should treat blocks with barrier as writing.
+ case OpControlBarrier:
+ case OpMemoryBarrier:
+ return false;
+
+ // OpExtInst is potentially impure depending on extension, but GLSL builtins are at least pure.
+
+ default:
+ break;
+ }
+ }
+
+ return true;
+}
+
+string Compiler::to_name(uint32_t id, bool allow_alias)
+{
+ if (allow_alias && ids.at(id).get_type() == TypeType)
+ {
+ // If this type is a simple alias, emit the
+ // name of the original type instead.
+ // We don't want to override the meta alias
+ // as that can be overridden by the reflection APIs after parse.
+ auto &type = get(id);
+ if (type.type_alias)
+ return to_name(type.type_alias);
+ }
+
+ if (meta[id].decoration.alias.empty())
+ return join("_", id);
+ else
+ return meta.at(id).decoration.alias;
+}
+
+bool Compiler::function_is_pure(const SPIRFunction &func)
+{
+ for (auto block : func.blocks)
+ {
+ if (!block_is_pure(get(block)))
+ {
+ //fprintf(stderr, "Function %s is impure!\n", to_name(func.self).c_str());
+ return false;
+ }
+ }
+
+ //fprintf(stderr, "Function %s is pure!\n", to_name(func.self).c_str());
+ return true;
+}
+
+void Compiler::register_global_read_dependencies(const SPIRBlock &block, uint32_t id)
+{
+ for (auto &i : block.ops)
+ {
+ auto ops = stream(i);
+ auto op = static_cast