]> git.tdb.fi Git - libs/gl.git/blobdiff - tests/glsl/glslcompiler.cpp
Fix a compile error in the GLSL compiler test runner
[libs/gl.git] / tests / glsl / glslcompiler.cpp
index 7c5e646283c25cfcbbbc8e705e4c958b0c22c6f9..e4b36a398e6b88ca965098b3d3a8a3ed37902e64 100644 (file)
@@ -1,3 +1,4 @@
+#include <spirv-tools/libspirv.hpp>
 #include <msp/core/algorithm.h>
 #include <msp/fs/dir.h>
 #include <msp/fs/utils.h>
@@ -7,55 +8,90 @@
 #include <msp/strings/utils.h>
 #include <msp/test/test.h>
 
-#include <msp/io/print.h>
-
-class GlslCompilerTest: public Msp::Test::RegisteredTest<GlslCompilerTest>
+class GlslCompilerHelper
 {
-private:
+protected:
        struct TestCase
        {
                std::string name;
                std::string source;
+               Msp::GL::SL::Compiler::Mode compile_mode;
+               std::map<std::string, int> spec_values;
                std::map<Msp::GL::SL::Stage::Type, std::string> expected_output;
-               std::string expected_error;
+               std::string expected_diagnostic;
+               bool expect_success;
+
+               TestCase(): expect_success(true) { }
        };
 
        std::list<TestCase> test_cases;
 
+       void load_all_test_cases(const Msp::FS::Path &);
+       const TestCase &load_test_case(const std::string &);
+
+       void verify_output(const std::string &, const std::string &);
+       void verify_diagnostic(const std::string &, const std::string &);
+       std::string extract_line(const std::string &, const std::string::const_iterator &);
+       virtual void fail(const std::string &) = 0;
+};
+
+class GlslCompilerTest: public Msp::Test::RegisteredTest<GlslCompilerTest>, private GlslCompilerHelper
+{
 public:
        GlslCompilerTest();
 
        static const char *get_name() { return "GLSL compiler"; }
 
 private:
-       const TestCase &load_test_case(const std::string &);
+       void run_test_case(const TestCase *);
+       virtual void fail(const std::string &m) { Test::fail(m); }
+};
+
+class GlslCompilerIdempotence: public Msp::Test::RegisteredTest<GlslCompilerIdempotence>, private GlslCompilerHelper
+{
+public:
+       GlslCompilerIdempotence();
+
+       static const char *get_name() { return "GLSL compiler idempotence"; }
 
+private:
        void run_test_case(const TestCase *);
-       void verify_output(const std::string &, const std::string &);
-       void verify_error(const std::string &, const std::string &);
-       std::string extract_line(const std::string &, const std::string::const_iterator &);
+       virtual void fail(const std::string &m) { Test::fail(m); }
+};
+
+class GlslCompilerSpirV: public Msp::Test::RegisteredTest<GlslCompilerSpirV>, private GlslCompilerHelper
+{
+private:
+       spvtools::SpirvTools spirv_tools;
+
+public:
+       GlslCompilerSpirV();
+
+       static const char *get_name() { return "GLSL to SPIR-V compilation"; }
+
+private:
+       void run_test_case(const TestCase *);
+       void diagnostic(spv_message_level_t, const char *, const spv_position_t &, const char *);
+       virtual void fail(const std::string &m) { Test::fail(m); }
 };
 
 using namespace std;
 using namespace Msp;
 
-GlslCompilerTest::GlslCompilerTest()
+void GlslCompilerHelper::load_all_test_cases(const FS::Path &tests_dir)
 {
-       FS::Path tests_dir = "glsl";
-       list<string> test_files = FS::list_filtered(tests_dir, "\\.glsl$");
-       test_files.sort();
+       vector<string> test_files = FS::list_filtered(tests_dir, "\\.glsl$");
+       sort(test_files);
        for(const auto &fn: test_files)
                load_test_case((tests_dir/fn).str());
-
-       for(const auto &tc: test_cases)
-               add(&GlslCompilerTest::run_test_case, &tc, tc.name);
 }
 
-const GlslCompilerTest::TestCase &GlslCompilerTest::load_test_case(const string &fn)
+const GlslCompilerHelper::TestCase &GlslCompilerHelper::load_test_case(const string &fn)
 {
        IO::BufferedFile file(fn);
        TestCase test_case;
        test_case.name = FS::basename(fn);
+       test_case.compile_mode = GL::SL::Compiler::PROGRAM;
        string *target = &test_case.source;
        while(!file.eof())
        {
@@ -66,10 +102,10 @@ const GlslCompilerTest::TestCase &GlslCompilerTest::load_test_case(const string
                if(line=="*/")
                        continue;
 
-               string::size_type expected = line.find("Expected output:");
-               if(expected!=string::npos)
+               string::size_type pos = line.find("Expected output:");
+               if(pos!=string::npos)
                {
-                       string stage = strip(line.substr(expected+16));
+                       string stage = strip(line.substr(pos+16));
                        if(stage=="vertex")
                                target = &test_case.expected_output[GL::SL::Stage::VERTEX];
                        else if(stage=="geometry")
@@ -81,63 +117,53 @@ const GlslCompilerTest::TestCase &GlslCompilerTest::load_test_case(const string
                        continue;
                }
 
-               expected = line.find("Expected error:");
-               if(expected!=string::npos)
+               pos = line.find("Expected error:");
+               if(pos==string::npos)
+                       pos = line.find("Expected diagnostic:");
+               if(pos!=string::npos)
                {
-                       target = &test_case.expected_error;
+                       target = &test_case.expected_diagnostic;
+                       test_case.expect_success = (line[pos+9]!='e');
                        continue;
                }
 
-               *target += line;
-               *target += '\n';
-       }
-       test_cases.push_back(test_case);
-
-       return test_cases.back();
-}
-
-void GlslCompilerTest::run_test_case(const TestCase *test_case)
-{
-       GL::SL::Compiler compiler(GL::SL::Features::all());
-       compiler.set_source(test_case->source, "<test>");
-       try
-       {
-               compiler.compile(GL::SL::Compiler::PROGRAM);
-       }
-       catch(const GL::SL::invalid_shader_source &exc)
-       {
-               if(!test_case->expected_error.empty())
+               pos = line.find("Compile mode:");
+               if(pos!=string::npos)
                {
-                       debug("Errors from compile:");
-                       debug(exc.what());
-                       verify_error(exc.what(), test_case->expected_error);
-                       return;
+                       string mode = strip(line.substr(pos+13));
+                       if(mode=="module")
+                               test_case.compile_mode = GL::SL::Compiler::MODULE;
+                       else if(mode=="program")
+                               test_case.compile_mode = GL::SL::Compiler::PROGRAM;
+                       else
+                               throw runtime_error("Unknown compile mode "+mode);
+                       continue;
                }
-               throw;
-       }
 
-       auto stages = compiler.get_stages();
-       for(auto s: stages)
-       {
-               auto i = test_case->expected_output.find(s);
-               if(i==test_case->expected_output.end())
-                       fail(format("Compiler produced extra stage %s", GL::SL::Stage::get_stage_name(s)));
-
-               string output = compiler.get_stage_glsl(s);
-               debug(format("Output for stage %s:", GL::SL::Stage::get_stage_name(s)));
-               auto lines = split_fields(output, '\n');
-               for(unsigned j=0; j<lines.size(); ++j)
-                       debug(format("%3d: %s", j+1, lines[j]));
+               pos = line.find("Specialize:");
+               if(pos!=string::npos)
+               {
+                       vector<string> parts = split(line.substr(pos+11));
+                       int value = 0;
+                       if(parts[1]=="true")
+                               value = 1;
+                       else if(parts[1]=="false")
+                               value = 0;
+                       else
+                               value = lexical_cast<int>(parts[1]);
+                       test_case.spec_values[parts[0]] = value;
+                       continue;
+               }
 
-               verify_output(output, i->second);
+               *target += line;
+               *target += '\n';
        }
+       test_cases.push_back(test_case);
 
-       for(const auto &s: test_case->expected_output)
-               if(find(stages, s.first)==stages.end())
-                       fail(format("Compiler didn't produce stage %s", GL::SL::Stage::get_stage_name(s.first)));
+       return test_cases.back();
 }
 
-void GlslCompilerTest::verify_output(const string &output, const string &expected)
+void GlslCompilerHelper::verify_output(const string &output, const string &expected)
 {
        GL::SL::Tokenizer tokenizer;
        tokenizer.begin(output, "<output>");
@@ -163,7 +189,7 @@ void GlslCompilerTest::verify_output(const string &output, const string &expecte
        }
 }
 
-void GlslCompilerTest::verify_error(const string &output, const string &expected)
+void GlslCompilerHelper::verify_diagnostic(const string &output, const string &expected)
 {
        auto i = output.begin();
        auto j = expected.begin();
@@ -189,7 +215,7 @@ void GlslCompilerTest::verify_error(const string &output, const string &expected
                {
                        string out_line = extract_line(output, i);
                        string expect_line = extract_line(expected, j);
-                       fail(format("Incorrect error line:\n%s\nExpected:\n%s", out_line, expect_line));
+                       fail(format("Incorrect diagnostic line:\n%s\nExpected:\n%s", out_line, expect_line));
                }
        }
 
@@ -198,11 +224,13 @@ void GlslCompilerTest::verify_error(const string &output, const string &expected
        while(j!=expected.end() && isspace(*j))
                ++j;
 
+       if(i!=output.end())
+               fail(format("Extra diagnostic line: %s", extract_line(output, i)));
        if(j!=expected.end())
-               fail(format("Missing error line: %s", extract_line(expected, j)));
+               fail(format("Missing diagnostic line: %s", extract_line(expected, j)));
 }
 
-string GlslCompilerTest::extract_line(const string &text, const string::const_iterator &iter)
+string GlslCompilerHelper::extract_line(const string &text, const string::const_iterator &iter)
 {
        string::const_iterator begin = iter;
        for(; (begin!=text.begin() && *begin!='\n'); --begin) ;
@@ -212,3 +240,136 @@ string GlslCompilerTest::extract_line(const string &text, const string::const_it
        for(; (end!=text.end() && *end!='\n'); ++end) ;
        return string(begin, end);
 }
+
+
+GlslCompilerTest::GlslCompilerTest()
+{
+       load_all_test_cases("glsl");
+       for(const auto &tc: test_cases)
+               add(&GlslCompilerTest::run_test_case, &tc, tc.name);
+}
+
+void GlslCompilerTest::run_test_case(const TestCase *test_case)
+{
+       GL::SL::Compiler compiler(GL::SL::Features::latest());
+       try
+       {
+               compiler.set_source(test_case->source, "<test>");
+               if(test_case->compile_mode==GL::SL::Compiler::PROGRAM)
+                       compiler.specialize(test_case->spec_values);
+               compiler.compile(test_case->compile_mode);
+       }
+       catch(const GL::SL::invalid_shader_source &exc)
+       {
+               if(!test_case->expect_success)
+               {
+                       debug("Errors from compile:");
+                       debug(exc.what());
+                       verify_diagnostic(exc.what(), test_case->expected_diagnostic);
+                       return;
+               }
+               throw;
+       }
+
+       if(!test_case->expect_success)
+               fail("Error expected but none thrown");
+
+       verify_diagnostic(compiler.get_diagnostics(), test_case->expected_diagnostic);
+
+       auto stages = compiler.get_stages();
+       for(auto s: stages)
+       {
+               auto i = test_case->expected_output.find(s);
+               if(i==test_case->expected_output.end())
+                       fail(format("Compiler produced extra stage %s", GL::SL::Stage::get_stage_name(s)));
+
+               string output = compiler.get_stage_glsl(s);
+               debug(format("Output for stage %s:", GL::SL::Stage::get_stage_name(s)));
+               auto lines = split_fields(output, '\n');
+               for(unsigned j=0; j<lines.size(); ++j)
+                       debug(format("%3d: %s", j+1, lines[j]));
+
+               verify_output(output, i->second);
+       }
+
+       for(const auto &s: test_case->expected_output)
+               if(find(stages, s.first)==stages.end())
+                       fail(format("Compiler didn't produce stage %s", GL::SL::Stage::get_stage_name(s.first)));
+}
+
+
+GlslCompilerIdempotence::GlslCompilerIdempotence()
+{
+       load_all_test_cases("glsl");
+       for(const auto &tc: test_cases)
+               if(tc.expect_success)
+                       add(&GlslCompilerIdempotence::run_test_case, &tc, tc.name);
+}
+
+void GlslCompilerIdempotence::run_test_case(const TestCase *test_case)
+{
+       GL::SL::Compiler compiler(GL::SL::Features::latest());
+       compiler.set_source(test_case->source, "<test>");
+       if(test_case->compile_mode==GL::SL::Compiler::PROGRAM)
+               compiler.specialize(test_case->spec_values);
+       compiler.compile(test_case->compile_mode);
+
+       GL::SL::Compiler compiler2(GL::SL::Features::latest());
+       compiler2.set_source(compiler.get_combined_glsl(), "<loopback>");
+       compiler2.compile(test_case->compile_mode);
+
+       auto stages = compiler.get_stages();
+       auto stages2 = compiler2.get_stages();
+       auto i = stages.begin();
+       auto j = stages2.begin();
+       for(; (i!=stages.end() && j!=stages2.end() && *i==*j); ++i, ++j)
+       {
+               string output = compiler.get_stage_glsl(*i);
+               string output2 = compiler2.get_stage_glsl(*j);
+
+               verify_output(output2, output);
+       }
+
+       if(i!=stages.end())
+               fail(format("Second pass didn't produce stage %s", GL::SL::Stage::get_stage_name(*i)));
+       if(j!=stages2.end())
+               fail(format("Second pass produced extra stage %s", GL::SL::Stage::get_stage_name(*j)));
+}
+
+
+GlslCompilerSpirV::GlslCompilerSpirV():
+       spirv_tools(SPV_ENV_UNIVERSAL_1_5)
+{
+       load_all_test_cases("glsl");
+       for(const auto &tc: test_cases)
+               if(tc.expect_success)
+                       add(&GlslCompilerSpirV::run_test_case, &tc, tc.name);
+
+       using namespace std::placeholders;
+       spirv_tools.SetMessageConsumer(std::bind(std::mem_fn(&GlslCompilerSpirV::diagnostic), this, _1, _2, _3, _4));
+}
+
+void GlslCompilerSpirV::run_test_case(const TestCase *test_case)
+{
+       GL::SL::Compiler compiler(GL::SL::Features::latest());
+       compiler.set_source(test_case->source, "<test>");
+       compiler.compile(GL::SL::Compiler::SPIRV);
+
+       vector<uint32_t> code = compiler.get_combined_spirv();
+       if(!spirv_tools.Validate(code))
+               fail("Invalid SPIR-V generated");
+}
+
+void GlslCompilerSpirV::diagnostic(spv_message_level_t level, const char *, const spv_position_t &, const char *message)
+{
+       const char *prefix;
+       switch(level)
+       {
+       case SPV_MSG_DEBUG: prefix = "debug: "; break;
+       case SPV_MSG_INFO: prefix = "info: "; break;
+       case SPV_MSG_WARNING: prefix = "warning: "; break;
+       case SPV_MSG_ERROR: prefix = "error: "; break;
+       default: prefix = "";
+       }
+       info(format("%s%s", prefix, message));
+}