X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=tests%2Fglsl%2Fglslcompiler.cpp;h=e4b36a398e6b88ca965098b3d3a8a3ed37902e64;hb=12a95b6552d6c67d68ea8aca2561abf3fb2b80e6;hp=8ff630555557e1bf5a3b7f1d7b5e7ece23d33027;hpb=11bbe9f4961d5550de39f890eeac63f45a7d0295;p=libs%2Fgl.git diff --git a/tests/glsl/glslcompiler.cpp b/tests/glsl/glslcompiler.cpp index 8ff63055..e4b36a39 100644 --- a/tests/glsl/glslcompiler.cpp +++ b/tests/glsl/glslcompiler.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -7,55 +8,90 @@ #include #include -#include - -class GlslCompilerTest: public Msp::Test::RegisteredTest +class GlslCompilerHelper { -private: +protected: struct TestCase { std::string name; std::string source; + Msp::GL::SL::Compiler::Mode compile_mode; + std::map spec_values; std::map expected_output; - std::string expected_error; + std::string expected_diagnostic; + bool expect_success; + + TestCase(): expect_success(true) { } }; std::list 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, 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, 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, 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 test_files = FS::list_filtered(tests_dir, "\\.glsl$"); - test_files.sort(); + vector 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()); - try - { - compiler.set_source(test_case->source, ""); - 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 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(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, ""); @@ -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, ""); + 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; jsecond); + } + + 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, ""); + 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(), ""); + 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, ""); + compiler.compile(GL::SL::Compiler::SPIRV); + + vector 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)); +}