This seems more appropriate for some SPIR-V stuff I'm going to add.
+++ /dev/null
-#include <msp/core/algorithm.h>
-#include <msp/core/raii.h>
-#include <msp/strings/lexicalcast.h>
-#include "compatibility.h"
-#include "glsl_error.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-namespace SL {
-
-DefaultPrecisionGenerator::DefaultPrecisionGenerator():
- stage(0)
-{ }
-
-void DefaultPrecisionGenerator::apply(Stage &s)
-{
- stage = &s;
- s.content.visit(*this);
-}
-
-void DefaultPrecisionGenerator::visit(Block &block)
-{
- for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
- {
- if(&block==&stage->content)
- insert_point = i;
- (*i)->visit(*this);
- }
-}
-
-void DefaultPrecisionGenerator::visit(Precision &prec)
-{
- have_default.insert(prec.type);
-}
-
-void DefaultPrecisionGenerator::visit(VariableDeclaration &var)
-{
- if(var.type_declaration)
- return;
-
- string type = var.type;
- if(!type.compare(0, 3, "vec") || !type.compare(0, 3, "mat"))
- type = "float";
- else if(!type.compare(0, 3, "ivec") || type=="uint")
- type = "int";
-
- if(!have_default.count(type))
- {
- Precision *prec = new Precision;
- if(!type.compare(0, 7, "sampler"))
- prec->precision = "lowp";
- else if(stage->type==Stage::FRAGMENT)
- prec->precision = "mediump";
- else
- prec->precision = "highp";
- prec->type = type;
- stage->content.body.insert(insert_point, prec);
-
- have_default.insert(type);
- }
-}
-
-
-void PrecisionRemover::apply(Stage &stage)
-{
- stage.content.visit(*this);
- NodeRemover().apply(stage, nodes_to_remove);
-}
-
-void PrecisionRemover::visit(Precision &prec)
-{
- nodes_to_remove.insert(&prec);
-}
-
-void PrecisionRemover::visit(VariableDeclaration &var)
-{
- var.precision.clear();
-}
-
-
-LegacyConverter::LegacyConverter():
- frag_out(0)
-{ }
-
-void LegacyConverter::apply(Stage &s, const Features &feat)
-{
- stage = &s;
- features = feat;
- if(supports_stage(s.type))
- s.content.visit(*this);
- else
- unsupported(format("Stage %s is not supported", Stage::get_stage_name(s.type)));
-}
-
-void LegacyConverter::unsupported(const string &reason)
-{
- Diagnostic diagnostic;
- diagnostic.severity = Diagnostic::ERR;
- diagnostic.source = GENERATED_SOURCE;
- diagnostic.line = 0;
- diagnostic.message = reason;
- stage->diagnostics.push_back(diagnostic);
-}
-
-void LegacyConverter::visit(Block &block)
-{
- for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
- {
- if(&block==&stage->content)
- uniform_insert_point = i;
- (*i)->visit(*this);
- }
-}
-
-bool LegacyConverter::check_version(const Version &feature_version) const
-{
- if(features.glsl_version<feature_version)
- return false;
- else if(stage->required_features.glsl_version<feature_version)
- stage->required_features.glsl_version = feature_version;
-
- return true;
-}
-
-bool LegacyConverter::check_extension(bool Features::*extension) const
-{
- if(!(features.*extension))
- return false;
-
- stage->required_features.*extension = true;
-
- return true;
-}
-
-bool LegacyConverter::supports_stage(Stage::Type st) const
-{
- if(st==Stage::GEOMETRY)
- {
- if(features.gl_api==OPENGL_ES2)
- return check_version(Version(3, 20));
- else
- return check_version(Version(1, 50));
- }
- else
- return true;
-}
-
-bool LegacyConverter::supports_unified_interface_syntax() const
-{
- if(features.gl_api==OPENGL_ES2)
- return check_version(Version(3, 0));
- else
- return check_version(Version(1, 30));
-}
-
-void LegacyConverter::visit(VariableReference &var)
-{
- if(var.declaration==frag_out && !supports_unified_interface_syntax())
- {
- var.name = "gl_FragColor";
- var.declaration = 0;
- r_type = "vec4";
- }
- else if(var.declaration)
- r_type = var.declaration->type;
- else
- r_type.clear();
-}
-
-void LegacyConverter::visit(Assignment &assign)
-{
- TraversingVisitor::visit(assign);
- if(assign.target.declaration==frag_out && !supports_unified_interface_syntax())
- assign.target.declaration = 0;
-}
-
-bool LegacyConverter::supports_unified_sampling_functions() const
-{
- if(features.gl_api==OPENGL_ES2)
- return check_version(Version(3, 0));
- else
- return check_version(Version(1, 30));
-}
-
-void LegacyConverter::visit(FunctionCall &call)
-{
- if(call.name=="texture")
- {
- string sampler_type;
- r_type.clear();
- NodeArray<Expression>::iterator i = call.arguments.begin();
- if(i!=call.arguments.end())
- {
- (*i)->visit(*this);
- sampler_type = r_type;
-
- for(; i!=call.arguments.end(); ++i)
- (*i)->visit(*this);
- }
-
- if(!supports_unified_sampling_functions())
- {
- if(sampler_type=="sampler1D")
- call.name = "texture1D";
- else if(sampler_type=="sampler2D")
- call.name = "texture2D";
- else if(sampler_type=="sampler3D")
- call.name = "texture3D";
- else if(sampler_type=="samplerCube")
- call.name = "textureCube";
- else if(sampler_type=="sampler1DShadow")
- call.name = "shadow1D";
- else if(sampler_type=="sampler2DShadow")
- call.name = "shadow2D";
- else if(sampler_type=="sampler1DArray")
- {
- check_extension(&Features::ext_texture_array);
- call.name = "texture1DArray";
- }
- else if(sampler_type=="sampler2DArray")
- {
- check_extension(&Features::ext_texture_array);
- call.name = "texture2DArray";
- }
- else if(sampler_type=="sampler1DArrayShadow")
- {
- check_extension(&Features::ext_texture_array);
- call.name = "shadow1DArray";
- }
- else if(sampler_type=="sampler2DArrayShadow")
- {
- check_extension(&Features::ext_texture_array);
- call.name = "shadow2DArray";
- }
- }
- }
- else
- TraversingVisitor::visit(call);
-}
-
-bool LegacyConverter::supports_interface_layouts() const
-{
- if(features.gl_api==OPENGL_ES2)
- return check_version(Version(3, 0));
- else if(check_version(Version(3, 30)))
- return true;
- else
- return check_extension(&Features::arb_explicit_attrib_location);
-}
-
-bool LegacyConverter::supports_centroid_sampling() const
-{
- if(features.gl_api==OPENGL_ES2)
- return check_version(Version(3, 0));
- else if(check_version(Version(1, 20)))
- return true;
- else
- return check_extension(&Features::ext_gpu_shader4);
-}
-
-bool LegacyConverter::supports_sample_sampling() const
-{
- if(features.gl_api==OPENGL_ES2)
- return check_version(Version(3, 20));
- else if(check_version(Version(4, 0)))
- return true;
- else
- return check_extension(&Features::arb_gpu_shader5);
-}
-
-void LegacyConverter::visit(VariableDeclaration &var)
-{
- if(var.layout && !supports_interface_layouts())
- {
- vector<Layout::Qualifier>::iterator i;
- for(i=var.layout->qualifiers.begin(); (i!=var.layout->qualifiers.end() && i->name!="location"); ++i) ;
- if(i!=var.layout->qualifiers.end())
- {
- if(stage->type==Stage::VERTEX && var.interface=="in")
- {
- stage->locations[var.name] = i->value;
- var.layout->qualifiers.erase(i);
- }
- else if(stage->type==Stage::FRAGMENT && var.interface=="out")
- {
- if(check_extension(&Features::ext_gpu_shader4))
- stage->locations[var.name] = i->value;
- else if(i->value!=0)
- unsupported("EXT_gpu_shader4 required for multiple fragment shader outputs");
- var.layout->qualifiers.erase(i);
- }
-
- if(var.layout->qualifiers.empty())
- var.layout = 0;
- }
- }
-
- if(var.sampling=="centroid")
- {
- if(!supports_centroid_sampling())
- var.sampling = string();
- }
- else if(var.sampling=="sample")
- {
- if(!supports_sample_sampling())
- var.sampling = string();
- }
-
- if((var.interface=="in" || var.interface=="out") && !supports_unified_interface_syntax())
- {
- if(stage->type==Stage::FRAGMENT && var.interface=="out")
- {
- frag_out = &var;
- nodes_to_remove.insert(&var);
- }
- }
-
- TraversingVisitor::visit(var);
-}
-
-bool LegacyConverter::supports_interface_blocks(const string &iface) const
-{
- if(features.gl_api==OPENGL_ES2)
- {
- if(iface=="uniform")
- return check_version(Version(3, 0));
- else
- return check_version(Version(3, 20));
- }
- else if(check_version(Version(1, 50)))
- return true;
- else if(iface=="uniform")
- return check_extension(&Features::arb_uniform_buffer_object);
- else
- return false;
-}
-
-void LegacyConverter::visit(InterfaceBlock &iface)
-{
- if(!supports_interface_blocks(iface.interface) && iface.type_declaration)
- {
- if(!iface.instance_name.empty())
- unsupported("ARB_uniform_buffer_object required for interface block instances");
- else if(iface.struct_declaration)
- {
- stage->content.body.splice(uniform_insert_point, iface.struct_declaration->members.body);
- nodes_to_remove.insert(&iface);
- nodes_to_remove.insert(iface.struct_declaration);
- }
- else
- /* If the interface block is an array, it should have an instance
- name too, so this should never be reached */
- throw logic_error("Unexpected interface block configuration");
- }
-}
-
-} // namespace SL
-} // namespace GL
-} // namespace Msp
+++ /dev/null
-#ifndef MSP_GL_SL_COMPATIBILITY_H_
-#define MSP_GL_SL_COMPATIBILITY_H_
-
-#include <string>
-#include "visitor.h"
-
-namespace Msp {
-namespace GL {
-namespace SL {
-
-/** Generates default precision declarations if they are missing, to satisfy
-GLSL ES requirements. */
-class DefaultPrecisionGenerator: private TraversingVisitor
-{
-private:
- Stage *stage;
- std::set<std::string> have_default;
- NodeList<Statement>::iterator insert_point;
-
-public:
- DefaultPrecisionGenerator();
-
- void apply(Stage &);
-
-private:
- virtual void visit(Block &);
- virtual void visit(Precision &);
- virtual void visit(VariableDeclaration &);
-};
-
-/** Removes precision qualifiers from variable declarations, as well as
-default precision declarations. */
-class PrecisionRemover: private TraversingVisitor
-{
-private:
- std::set<Node *> nodes_to_remove;
-
-public:
- void apply(Stage &);
-
-private:
- virtual void visit(Precision &);
- virtual void visit(VariableDeclaration &);
-};
-
-/** Converts structures of the syntax tree to match a particular set of
-features. */
-class LegacyConverter: private TraversingVisitor
-{
-private:
- Stage *stage;
- Features features;
- std::string r_type;
- VariableDeclaration *frag_out;
- NodeList<Statement>::iterator uniform_insert_point;
- std::set<Node *> nodes_to_remove;
-
-public:
- LegacyConverter();
-
- virtual void apply(Stage &, const Features &);
-
-private:
- void unsupported(const std::string &);
-
- virtual void visit(Block &);
- bool check_version(const Version &) const;
- bool check_extension(bool Features::*) const;
- bool supports_stage(Stage::Type) const;
- bool supports_unified_interface_syntax() const;
- virtual void visit(VariableReference &);
- virtual void visit(Assignment &);
- bool supports_unified_sampling_functions() const;
- virtual void visit(FunctionCall &);
- bool supports_interface_layouts() const;
- bool supports_centroid_sampling() const;
- bool supports_sample_sampling() const;
- virtual void visit(VariableDeclaration &);
- bool supports_interface_blocks(const std::string &) const;
- virtual void visit(InterfaceBlock &);
-};
-
-} // namespace SL
-} // namespace GL
-} // namespace Msp
-
-#endif
#include <msp/strings/format.h>
#include <msp/strings/utils.h>
#include "builtin.h"
-#include "compatibility.h"
#include "compiler.h"
#include "debug.h"
#include "error.h"
+#include "finalize.h"
#include "generate.h"
#include "glsl_error.h"
#include "optimize.h"
--- /dev/null
+#include <msp/core/algorithm.h>
+#include <msp/core/raii.h>
+#include <msp/strings/lexicalcast.h>
+#include "finalize.h"
+#include "glsl_error.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+namespace SL {
+
+DefaultPrecisionGenerator::DefaultPrecisionGenerator():
+ stage(0)
+{ }
+
+void DefaultPrecisionGenerator::apply(Stage &s)
+{
+ stage = &s;
+ s.content.visit(*this);
+}
+
+void DefaultPrecisionGenerator::visit(Block &block)
+{
+ for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
+ {
+ if(&block==&stage->content)
+ insert_point = i;
+ (*i)->visit(*this);
+ }
+}
+
+void DefaultPrecisionGenerator::visit(Precision &prec)
+{
+ have_default.insert(prec.type);
+}
+
+void DefaultPrecisionGenerator::visit(VariableDeclaration &var)
+{
+ if(var.type_declaration)
+ return;
+
+ string type = var.type;
+ if(!type.compare(0, 3, "vec") || !type.compare(0, 3, "mat"))
+ type = "float";
+ else if(!type.compare(0, 3, "ivec") || type=="uint")
+ type = "int";
+
+ if(!have_default.count(type))
+ {
+ Precision *prec = new Precision;
+ if(!type.compare(0, 7, "sampler"))
+ prec->precision = "lowp";
+ else if(stage->type==Stage::FRAGMENT)
+ prec->precision = "mediump";
+ else
+ prec->precision = "highp";
+ prec->type = type;
+ stage->content.body.insert(insert_point, prec);
+
+ have_default.insert(type);
+ }
+}
+
+
+void PrecisionRemover::apply(Stage &stage)
+{
+ stage.content.visit(*this);
+ NodeRemover().apply(stage, nodes_to_remove);
+}
+
+void PrecisionRemover::visit(Precision &prec)
+{
+ nodes_to_remove.insert(&prec);
+}
+
+void PrecisionRemover::visit(VariableDeclaration &var)
+{
+ var.precision.clear();
+}
+
+
+LegacyConverter::LegacyConverter():
+ frag_out(0)
+{ }
+
+void LegacyConverter::apply(Stage &s, const Features &feat)
+{
+ stage = &s;
+ features = feat;
+ if(supports_stage(s.type))
+ s.content.visit(*this);
+ else
+ unsupported(format("Stage %s is not supported", Stage::get_stage_name(s.type)));
+}
+
+void LegacyConverter::unsupported(const string &reason)
+{
+ Diagnostic diagnostic;
+ diagnostic.severity = Diagnostic::ERR;
+ diagnostic.source = GENERATED_SOURCE;
+ diagnostic.line = 0;
+ diagnostic.message = reason;
+ stage->diagnostics.push_back(diagnostic);
+}
+
+void LegacyConverter::visit(Block &block)
+{
+ for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
+ {
+ if(&block==&stage->content)
+ uniform_insert_point = i;
+ (*i)->visit(*this);
+ }
+}
+
+bool LegacyConverter::check_version(const Version &feature_version) const
+{
+ if(features.glsl_version<feature_version)
+ return false;
+ else if(stage->required_features.glsl_version<feature_version)
+ stage->required_features.glsl_version = feature_version;
+
+ return true;
+}
+
+bool LegacyConverter::check_extension(bool Features::*extension) const
+{
+ if(!(features.*extension))
+ return false;
+
+ stage->required_features.*extension = true;
+
+ return true;
+}
+
+bool LegacyConverter::supports_stage(Stage::Type st) const
+{
+ if(st==Stage::GEOMETRY)
+ {
+ if(features.gl_api==OPENGL_ES2)
+ return check_version(Version(3, 20));
+ else
+ return check_version(Version(1, 50));
+ }
+ else
+ return true;
+}
+
+bool LegacyConverter::supports_unified_interface_syntax() const
+{
+ if(features.gl_api==OPENGL_ES2)
+ return check_version(Version(3, 0));
+ else
+ return check_version(Version(1, 30));
+}
+
+void LegacyConverter::visit(VariableReference &var)
+{
+ if(var.declaration==frag_out && !supports_unified_interface_syntax())
+ {
+ var.name = "gl_FragColor";
+ var.declaration = 0;
+ r_type = "vec4";
+ }
+ else if(var.declaration)
+ r_type = var.declaration->type;
+ else
+ r_type.clear();
+}
+
+void LegacyConverter::visit(Assignment &assign)
+{
+ TraversingVisitor::visit(assign);
+ if(assign.target.declaration==frag_out && !supports_unified_interface_syntax())
+ assign.target.declaration = 0;
+}
+
+bool LegacyConverter::supports_unified_sampling_functions() const
+{
+ if(features.gl_api==OPENGL_ES2)
+ return check_version(Version(3, 0));
+ else
+ return check_version(Version(1, 30));
+}
+
+void LegacyConverter::visit(FunctionCall &call)
+{
+ if(call.name=="texture")
+ {
+ string sampler_type;
+ r_type.clear();
+ NodeArray<Expression>::iterator i = call.arguments.begin();
+ if(i!=call.arguments.end())
+ {
+ (*i)->visit(*this);
+ sampler_type = r_type;
+
+ for(; i!=call.arguments.end(); ++i)
+ (*i)->visit(*this);
+ }
+
+ if(!supports_unified_sampling_functions())
+ {
+ if(sampler_type=="sampler1D")
+ call.name = "texture1D";
+ else if(sampler_type=="sampler2D")
+ call.name = "texture2D";
+ else if(sampler_type=="sampler3D")
+ call.name = "texture3D";
+ else if(sampler_type=="samplerCube")
+ call.name = "textureCube";
+ else if(sampler_type=="sampler1DShadow")
+ call.name = "shadow1D";
+ else if(sampler_type=="sampler2DShadow")
+ call.name = "shadow2D";
+ else if(sampler_type=="sampler1DArray")
+ {
+ check_extension(&Features::ext_texture_array);
+ call.name = "texture1DArray";
+ }
+ else if(sampler_type=="sampler2DArray")
+ {
+ check_extension(&Features::ext_texture_array);
+ call.name = "texture2DArray";
+ }
+ else if(sampler_type=="sampler1DArrayShadow")
+ {
+ check_extension(&Features::ext_texture_array);
+ call.name = "shadow1DArray";
+ }
+ else if(sampler_type=="sampler2DArrayShadow")
+ {
+ check_extension(&Features::ext_texture_array);
+ call.name = "shadow2DArray";
+ }
+ }
+ }
+ else
+ TraversingVisitor::visit(call);
+}
+
+bool LegacyConverter::supports_interface_layouts() const
+{
+ if(features.gl_api==OPENGL_ES2)
+ return check_version(Version(3, 0));
+ else if(check_version(Version(3, 30)))
+ return true;
+ else
+ return check_extension(&Features::arb_explicit_attrib_location);
+}
+
+bool LegacyConverter::supports_centroid_sampling() const
+{
+ if(features.gl_api==OPENGL_ES2)
+ return check_version(Version(3, 0));
+ else if(check_version(Version(1, 20)))
+ return true;
+ else
+ return check_extension(&Features::ext_gpu_shader4);
+}
+
+bool LegacyConverter::supports_sample_sampling() const
+{
+ if(features.gl_api==OPENGL_ES2)
+ return check_version(Version(3, 20));
+ else if(check_version(Version(4, 0)))
+ return true;
+ else
+ return check_extension(&Features::arb_gpu_shader5);
+}
+
+void LegacyConverter::visit(VariableDeclaration &var)
+{
+ if(var.layout && !supports_interface_layouts())
+ {
+ vector<Layout::Qualifier>::iterator i;
+ for(i=var.layout->qualifiers.begin(); (i!=var.layout->qualifiers.end() && i->name!="location"); ++i) ;
+ if(i!=var.layout->qualifiers.end())
+ {
+ if(stage->type==Stage::VERTEX && var.interface=="in")
+ {
+ stage->locations[var.name] = i->value;
+ var.layout->qualifiers.erase(i);
+ }
+ else if(stage->type==Stage::FRAGMENT && var.interface=="out")
+ {
+ if(check_extension(&Features::ext_gpu_shader4))
+ stage->locations[var.name] = i->value;
+ else if(i->value!=0)
+ unsupported("EXT_gpu_shader4 required for multiple fragment shader outputs");
+ var.layout->qualifiers.erase(i);
+ }
+
+ if(var.layout->qualifiers.empty())
+ var.layout = 0;
+ }
+ }
+
+ if(var.sampling=="centroid")
+ {
+ if(!supports_centroid_sampling())
+ var.sampling = string();
+ }
+ else if(var.sampling=="sample")
+ {
+ if(!supports_sample_sampling())
+ var.sampling = string();
+ }
+
+ if((var.interface=="in" || var.interface=="out") && !supports_unified_interface_syntax())
+ {
+ if(stage->type==Stage::FRAGMENT && var.interface=="out")
+ {
+ frag_out = &var;
+ nodes_to_remove.insert(&var);
+ }
+ }
+
+ TraversingVisitor::visit(var);
+}
+
+bool LegacyConverter::supports_interface_blocks(const string &iface) const
+{
+ if(features.gl_api==OPENGL_ES2)
+ {
+ if(iface=="uniform")
+ return check_version(Version(3, 0));
+ else
+ return check_version(Version(3, 20));
+ }
+ else if(check_version(Version(1, 50)))
+ return true;
+ else if(iface=="uniform")
+ return check_extension(&Features::arb_uniform_buffer_object);
+ else
+ return false;
+}
+
+void LegacyConverter::visit(InterfaceBlock &iface)
+{
+ if(!supports_interface_blocks(iface.interface) && iface.type_declaration)
+ {
+ if(!iface.instance_name.empty())
+ unsupported("ARB_uniform_buffer_object required for interface block instances");
+ else if(iface.struct_declaration)
+ {
+ stage->content.body.splice(uniform_insert_point, iface.struct_declaration->members.body);
+ nodes_to_remove.insert(&iface);
+ nodes_to_remove.insert(iface.struct_declaration);
+ }
+ else
+ /* If the interface block is an array, it should have an instance
+ name too, so this should never be reached */
+ throw logic_error("Unexpected interface block configuration");
+ }
+}
+
+} // namespace SL
+} // namespace GL
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GL_SL_COMPATIBILITY_H_
+#define MSP_GL_SL_COMPATIBILITY_H_
+
+#include <string>
+#include "visitor.h"
+
+namespace Msp {
+namespace GL {
+namespace SL {
+
+/** Generates default precision declarations if they are missing, to satisfy
+GLSL ES requirements. */
+class DefaultPrecisionGenerator: private TraversingVisitor
+{
+private:
+ Stage *stage;
+ std::set<std::string> have_default;
+ NodeList<Statement>::iterator insert_point;
+
+public:
+ DefaultPrecisionGenerator();
+
+ void apply(Stage &);
+
+private:
+ virtual void visit(Block &);
+ virtual void visit(Precision &);
+ virtual void visit(VariableDeclaration &);
+};
+
+/** Removes precision qualifiers from variable declarations, as well as
+default precision declarations. */
+class PrecisionRemover: private TraversingVisitor
+{
+private:
+ std::set<Node *> nodes_to_remove;
+
+public:
+ void apply(Stage &);
+
+private:
+ virtual void visit(Precision &);
+ virtual void visit(VariableDeclaration &);
+};
+
+/** Converts structures of the syntax tree to match a particular set of
+features. */
+class LegacyConverter: private TraversingVisitor
+{
+private:
+ Stage *stage;
+ Features features;
+ std::string r_type;
+ VariableDeclaration *frag_out;
+ NodeList<Statement>::iterator uniform_insert_point;
+ std::set<Node *> nodes_to_remove;
+
+public:
+ LegacyConverter();
+
+ virtual void apply(Stage &, const Features &);
+
+private:
+ void unsupported(const std::string &);
+
+ virtual void visit(Block &);
+ bool check_version(const Version &) const;
+ bool check_extension(bool Features::*) const;
+ bool supports_stage(Stage::Type) const;
+ bool supports_unified_interface_syntax() const;
+ virtual void visit(VariableReference &);
+ virtual void visit(Assignment &);
+ bool supports_unified_sampling_functions() const;
+ virtual void visit(FunctionCall &);
+ bool supports_interface_layouts() const;
+ bool supports_centroid_sampling() const;
+ bool supports_sample_sampling() const;
+ virtual void visit(VariableDeclaration &);
+ bool supports_interface_blocks(const std::string &) const;
+ virtual void visit(InterfaceBlock &);
+};
+
+} // namespace SL
+} // namespace GL
+} // namespace Msp
+
+#endif