From: Mikko Rasa Date: Wed, 3 Mar 2021 23:44:59 +0000 (+0200) Subject: Add a unit test framework and some tests for the GLSL compiler X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=commitdiff_plain;h=6ba314198dce795408690456fa1f0ef559aa1532 Add a unit test framework and some tests for the GLSL compiler Some of the tests fail due to bugs in the compiler. --- diff --git a/.gitignore b/.gitignore index cff6fec5..676eb92d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,6 @@ temp /mesh2c /mspgl.pc /shaders +/tests/glsltest /texturing /viewer diff --git a/tests/Build b/tests/Build new file mode 100644 index 00000000..85dced18 --- /dev/null +++ b/tests/Build @@ -0,0 +1,16 @@ +package "mspgl-tests" +{ + require "mspcore"; + require "mspgl"; + require "msptest"; + require "sigc++-2.0"; + + program "glsltest" + { + source "glsl"; + build_info + { + standard CXX "c++11"; + }; + }; +}; diff --git a/tests/glsl/constant_condition_removal.glsl b/tests/glsl/constant_condition_removal.glsl new file mode 100644 index 00000000..08a61fde --- /dev/null +++ b/tests/glsl/constant_condition_removal.glsl @@ -0,0 +1,36 @@ +const bool use_color = false; + +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +layout(location=1) in vec4 color; +void main() +{ + gl_Position = position; + passthrough; +} + +#pragma MSP stage(fragment) +layout(location=0) out vec4 frag_color; +void main() +{ + if(use_color) + frag_color = color; + else + frag_color = vec4(1.0); +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +void main() +{ + gl_Position = position; +} +*/ + +/* Expected output: fragment +layout(location=0) out vec4 frag_color; +void main() +{ + frag_color = vec4(1.0); +} +*/ diff --git a/tests/glsl/expression_inline.glsl b/tests/glsl/expression_inline.glsl new file mode 100644 index 00000000..a730e7d5 --- /dev/null +++ b/tests/glsl/expression_inline.glsl @@ -0,0 +1,15 @@ +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +void main() +{ + vec4 p = position*2.0-1.0; + gl_Position = p; +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +void main() +{ + gl_Position = position*2.0-1.0; +} +*/ diff --git a/tests/glsl/expression_inline_iteration.glsl b/tests/glsl/expression_inline_iteration.glsl new file mode 100644 index 00000000..20136ca4 --- /dev/null +++ b/tests/glsl/expression_inline_iteration.glsl @@ -0,0 +1,35 @@ +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +layout(location=1) in vec3 direction; +void main() +{ + vec4 p = position.xyz; + int step = -1; + for(int i=0; i<10; ++i) + { + float scale = 2.0; + p += direction*scale; + if(p.z<0) + break; + step = i; + } + gl_Position = position+vec4(step, 0.0, 0.0, 0.0); +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +layout(location=1) in vec3 direction; +void main() +{ + vec4 p = position.xyz; + int step = -1; + for(int i=0; i<10; ++i) + { + p += direction*2.0; + if(p.z<0) + break; + step = i; + } + gl_Position = position+vec4(step, 0.0, 0.0, 0.0); +} +*/ diff --git a/tests/glsl/expression_inline_precedence.glsl b/tests/glsl/expression_inline_precedence.glsl new file mode 100644 index 00000000..9f37c83b --- /dev/null +++ b/tests/glsl/expression_inline_precedence.glsl @@ -0,0 +1,22 @@ +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +layout(location=1) in float v; +void main() +{ + float a = v.x-v.y; + float b = v.z-v.w; + float c = v.x-v.y; + float d = v.z-v.w; + float e = a-b; + float f = c*d; + gl_Position = position*(e+f); +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +layout(location=1) in float v; +void main() +{ + gl_Position = position*((v.x-v.y-(v.z-v.w))+(v.x-v.y)*(v.z-v.w)); +} +*/ diff --git a/tests/glsl/function_inline.glsl b/tests/glsl/function_inline.glsl new file mode 100644 index 00000000..d1920f27 --- /dev/null +++ b/tests/glsl/function_inline.glsl @@ -0,0 +1,18 @@ +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +vec4 func() +{ + return position; +} +void main() +{ + gl_Position = func(); +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +void main() +{ + gl_Position = position; +} +*/ diff --git a/tests/glsl/function_inline_in_condition.glsl b/tests/glsl/function_inline_in_condition.glsl new file mode 100644 index 00000000..796f8882 --- /dev/null +++ b/tests/glsl/function_inline_in_condition.glsl @@ -0,0 +1,24 @@ +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +layout(location=1) in float scale; +float func() +{ + float s = scale*2.0; + return s*s; +} +void main() +{ + if(func()>1.0) + gl_Position = position; +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +layout(location=1) in float scale; +void main() +{ + float s = scale*2.0; + if(s*s>1.0) + gl_Position = position; +} +*/ diff --git a/tests/glsl/function_inline_in_iteration.glsl b/tests/glsl/function_inline_in_iteration.glsl new file mode 100644 index 00000000..a98a4539 --- /dev/null +++ b/tests/glsl/function_inline_in_iteration.glsl @@ -0,0 +1,28 @@ +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +layout(location=1) in float scale; +float func() +{ + float s = scale*2.0; + return s*s; +} +void main() +{ + float p = 1.0; + for(float i=func(); i<10.0; i+=2.0) + p += i; + gl_Position = position*p; +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +layout(location=1) in float scale; +void main() +{ + float p = 1.0; + float s = scale*2.0; + for(float i=s*s; i<10.0; i+=2.0) + p += i; + gl_Position = position*p; +} +*/ diff --git a/tests/glsl/function_inline_name_conflict.glsl b/tests/glsl/function_inline_name_conflict.glsl new file mode 100644 index 00000000..93c4eca3 --- /dev/null +++ b/tests/glsl/function_inline_name_conflict.glsl @@ -0,0 +1,24 @@ +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +layout(location=1) in float scale; +float func() +{ + float s = scale*2.0; + return s*s; +} +void main() +{ + float s = scale+1.0; + gl_Position = position*func()*s*s; +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +layout(location=1) in float scale; +void main() +{ + float s = scale+1.0; + float _func_s = scale*2.0; + gl_Position = position*_func_s*_func_s*s*s; +} +*/ diff --git a/tests/glsl/function_inline_reorder.glsl b/tests/glsl/function_inline_reorder.glsl new file mode 100644 index 00000000..3e445ce6 --- /dev/null +++ b/tests/glsl/function_inline_reorder.glsl @@ -0,0 +1,19 @@ +#pragma MSP stage(vertex) +vec4 func(); +void main() +{ + gl_Position = func(); +} +layout(location=0) in vec4 position; +vec4 func() +{ + return position; +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +void main() +{ + gl_Position = position; +} +*/ diff --git a/tests/glsl/geometry_interface_block.glsl b/tests/glsl/geometry_interface_block.glsl new file mode 100644 index 00000000..3934dd13 --- /dev/null +++ b/tests/glsl/geometry_interface_block.glsl @@ -0,0 +1,85 @@ +uniform sampler2D tex; + +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +out VertexOut +{ + vec2 texcoord; +} vs_out; +void main() +{ + vs_out.texcoord = position.xy*0.5+0.5; + gl_Position = position; +} + +#pragma MSP stage(geometry) +layout(triangles) in; +layout(triangles, max_vertices=3) out; +out GeometryOut +{ + vec2 texcoord; +} gs_out; +void main() +{ + for(int i=0; i<3; ++i) + { + gs_out.texcoord = vs_out[i].texcoord; + passthrough[i]; + EmitVertex(); + } +} + +#pragma MSP stage(fragment) +layout(location=0) out vec4 frag_color; +void main() +{ + frag_color = texture(tex, gs_out.texcoord); +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +out VertexOut +{ + vec2 texcoord; +} vs_out; +void main() +{ + vs_out.texcoord = position.xy*0.5+0.5; + gl_Position = position; +} +*/ + +/* Expected output: geometry +layout(triangles) in; +layout(triangles, max_vertices=3) out; +out GeometryOut +{ + vec2 texcoord; +} gs_out; +in VertexOut +{ + vec2 texcoord; +} vs_out[]; +void main() +{ + for(int i = 0; i<3; ++i) + { + gs_out.texcoord = vs_out[i].texcoord; + gl_Position = gl_in[i].gl_Position; + EmitVertex(); + } +} +*/ + +/* Expected output: fragment +uniform sampler2D tex; +layout(location=0) out vec4 frag_color; +in GeometryOut +{ + vec2 texcoord; +} gs_out; +void main() +{ + frag_color = texture(tex, gs_out.texcoord); +} +*/ diff --git a/tests/glsl/geometry_passthrough.glsl b/tests/glsl/geometry_passthrough.glsl new file mode 100644 index 00000000..20043589 --- /dev/null +++ b/tests/glsl/geometry_passthrough.glsl @@ -0,0 +1,64 @@ +uniform sampler2D tex; + +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +void main() +{ + out vec2 texcoord = position.xy*0.5+0.5; + gl_Position = position; +} + +#pragma MSP stage(geometry) +layout(triangles) in; +layout(triangles, max_vertices=3) out; +void main() +{ + for(int i=0; i<3; ++i) + { + passthrough[i]; + EmitVertex(); + } +} + +#pragma MSP stage(fragment) +layout(location=0) out vec4 frag_color; +void main() +{ + frag_color = texture(tex, texcoord); +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +out vec2 texcoord; +void main() +{ + texcoord = position.xy*0.5+0.5; + gl_Position = position; +} +*/ + +/* Expected output: geometry +layout(triangles) in; +layout(triangles, max_vertices=3) out; +in vec2 texcoord[]; +out vec2 _gs_out_texcoord; +void main() +{ + for(int i = 0; i<3; ++i) + { + gl_Position = gl_in[i].gl_Position; + _gs_out_texcoord = texcoord[i]; + EmitVertex(); + } +} +*/ + +/* Expected output: fragment +uniform sampler2D tex; +layout(location=0) out vec4 frag_color; +in vec2 _gs_out_texcoord; +void main() +{ + frag_color = texture(tex, _gs_out_texcoord); +} +*/ diff --git a/tests/glsl/glslcompiler.cpp b/tests/glsl/glslcompiler.cpp new file mode 100644 index 00000000..57911cca --- /dev/null +++ b/tests/glsl/glslcompiler.cpp @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class GlslCompilerTest: public Msp::Test::RegisteredTest +{ +private: + struct TestCase + { + std::string name; + std::string source; + std::map expected_output; + }; + + std::list test_cases; + +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 *); +}; + +using namespace std; +using namespace Msp; + +GlslCompilerTest::GlslCompilerTest() +{ + FS::Path tests_dir = "glsl"; + list test_files = FS::list_filtered(tests_dir, "\\.glsl$"); + test_files.sort(); + 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) +{ + IO::BufferedFile file(fn); + TestCase test_case; + test_case.name = FS::basename(fn); + string *target = &test_case.source; + while(!file.eof()) + { + string line; + if(!file.getline(line)) + break; + + if(line=="*/") + continue; + + string::size_type expected = line.find("Expected output:"); + if(expected!=string::npos) + { + string stage = strip(line.substr(expected+16)); + if(stage=="vertex") + target = &test_case.expected_output[GL::SL::Stage::VERTEX]; + else if(stage=="geometry") + target = &test_case.expected_output[GL::SL::Stage::GEOMETRY]; + else if(stage=="fragment") + target = &test_case.expected_output[GL::SL::Stage::FRAGMENT]; + else + throw runtime_error("Unknown stage "+stage); + 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, ""); + compiler.compile(GL::SL::Compiler::PROGRAM); + + 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", output); + + GL::SL::Tokenizer expected_tkn; + expected_tkn.begin("", i->second); + + while(1) + { + string token = expected_tkn.parse_token(); + + try + { + tokenizer.expect(token); + } + catch(const GL::SL::invalid_shader_source &exc) + { + fail(exc.what()); + } + + if(token.empty()) + break; + } + } + + 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))); +} diff --git a/tests/glsl/interface_block.glsl b/tests/glsl/interface_block.glsl new file mode 100644 index 00000000..41d7a63e --- /dev/null +++ b/tests/glsl/interface_block.glsl @@ -0,0 +1,45 @@ +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +layout(location=1) in vec4 color; +out VertexOut +{ + vec4 out_color; +}; +void main() +{ + gl_Position = position; + out_color = color; +} + +#pragma MSP stage(fragment) +layout(location=0) out vec4 frag_color; +void main() +{ + frag_color = out_color; +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +layout(location=1) in vec4 color; +out VertexOut +{ + vec4 out_color; +}; +void main() +{ + gl_Position = position; + out_color = color; +} +*/ + +/* Expected output: fragment +layout(location=0) out vec4 frag_color; +in VertexOut +{ + vec4 out_color; +}; +void main() +{ + frag_color = out_color; +} +*/ diff --git a/tests/glsl/member_assignment.glsl b/tests/glsl/member_assignment.glsl new file mode 100644 index 00000000..8444cfca --- /dev/null +++ b/tests/glsl/member_assignment.glsl @@ -0,0 +1,22 @@ +#pragma MSP stage(vertex) +void main() +{ + vec4 p; + p.x = 1.0; + p.y = 0.0; + p.z = 2.0; + p.w = 1.0; + gl_Position = p; +} + +/* Expected output: vertex +void main() +{ + vec4 p; + p.x = 1.0; + p.y = 0.0; + p.z = 2.0; + p.w = 1.0; + gl_Position = p; +} +*/ diff --git a/tests/glsl/multiline_function_inline.glsl b/tests/glsl/multiline_function_inline.glsl new file mode 100644 index 00000000..e8f05bdb --- /dev/null +++ b/tests/glsl/multiline_function_inline.glsl @@ -0,0 +1,22 @@ +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +layout(location=1) in float scale; +vec4 func() +{ + float s = scale*2.0; + return position*s*s; +} +void main() +{ + gl_Position = func(); +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +layout(location=1) in float scale; +void main() +{ + float s = scale*2.0; + gl_Position = position*s*s; +} +*/ diff --git a/tests/glsl/multiple_emitvertex.glsl b/tests/glsl/multiple_emitvertex.glsl new file mode 100644 index 00000000..06c129d8 --- /dev/null +++ b/tests/glsl/multiple_emitvertex.glsl @@ -0,0 +1,68 @@ +uniform sampler2D tex; + +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +void main() +{ + out vec2 texcoord = position.xy*0.5+0.5; + gl_Position = position; +} + +#pragma MSP stage(geometry) +layout(triangles) in; +layout(triangles, max_vertices=3) out; +void main() +{ + passthrough[0]; + EmitVertex(); + passthrough[1]; + EmitVertex(); + passthrough[2]; + EmitVertex(); +} + +#pragma MSP stage(fragment) +layout(location=0) out vec4 frag_color; +void main() +{ + frag_color = texture(tex, texcoord); +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +out vec2 texcoord; +void main() +{ + texcoord = position.xy*0.5+0.5; + gl_Position = position; +} +*/ + +/* Expected output: geometry +layout(triangles) in; +layout(triangles, max_vertices=3) out; +in vec2 texcoord[]; +out vec2 _gs_out_texcoord; +void main() +{ + gl_Position = gl_in[0].gl_Position; + _gs_out_texcoord = texcoord[0]; + EmitVertex(); + gl_Position = gl_in[1].gl_Position; + _gs_out_texcoord = texcoord[1]; + EmitVertex(); + gl_Position = gl_in[2].gl_Position; + _gs_out_texcoord = texcoord[2]; + EmitVertex(); +} +*/ + +/* Expected output: fragment +uniform sampler2D tex; +layout(location=0) out vec4 frag_color; +in vec2 _gs_out_texcoord; +void main() +{ + frag_color = texture(tex, _gs_out_texcoord); +} +*/ diff --git a/tests/glsl/named_interface_block.glsl b/tests/glsl/named_interface_block.glsl new file mode 100644 index 00000000..9d734bd1 --- /dev/null +++ b/tests/glsl/named_interface_block.glsl @@ -0,0 +1,45 @@ +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +layout(location=1) in vec4 color; +out VertexOut +{ + vec4 color; +} vs_out; +void main() +{ + gl_Position = position; + vs_out.color = color; +} + +#pragma MSP stage(fragment) +layout(location=0) out vec4 frag_color; +void main() +{ + frag_color = vs_out.color; +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +layout(location=1) in vec4 color; +out VertexOut +{ + vec4 color; +} vs_out; +void main() +{ + gl_Position = position; + vs_out.color = color; +} +*/ + +/* Expected output: fragment +layout(location=0) out vec4 frag_color; +in VertexOut +{ + vec4 color; +} vs_out; +void main() +{ + frag_color = vs_out.color; +} +*/ diff --git a/tests/glsl/passthrough.glsl b/tests/glsl/passthrough.glsl new file mode 100644 index 00000000..4017e490 --- /dev/null +++ b/tests/glsl/passthrough.glsl @@ -0,0 +1,35 @@ +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +layout(location=1) in vec4 color; +void main() +{ + gl_Position = position; + passthrough; +} + +#pragma MSP stage(fragment) +layout(location=0) out vec4 frag_color; +void main() +{ + frag_color = color; +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +layout(location=1) in vec4 color; +out vec4 _vs_out_color; +void main() +{ + gl_Position = position; + _vs_out_color = color; +} +*/ + +/* Expected output: fragment +layout(location=0) out vec4 frag_color; +in vec4 _vs_out_color; +void main() +{ + frag_color = _vs_out_color; +} +*/ diff --git a/tests/glsl/redundant_geometry_gl_position.glsl b/tests/glsl/redundant_geometry_gl_position.glsl new file mode 100644 index 00000000..99d4330f --- /dev/null +++ b/tests/glsl/redundant_geometry_gl_position.glsl @@ -0,0 +1,41 @@ +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +void main() +{ + gl_Position = position; + gl_Position = position; +} + +#pragma MSP stage(geometry) +layout(triangles) in; +layout(triangles, max_vertices=3) out; +void main() +{ + for(int i=0; i<3; ++i) + { + passthrough[i]; + gl_Position = gl_in[i].gl_Position; + EmitVertex(); + } +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +void main() +{ + gl_Position = position; +} +*/ + +/* Expected output: geometry +layout(triangles) in; +layout(triangles, max_vertices=3) out; +void main() +{ + for(int i = 0; i<3; ++i) + { + gl_Position = gl_in[i].gl_Position; + EmitVertex(); + } +} +*/ diff --git a/tests/glsl/redundant_gl_position.glsl b/tests/glsl/redundant_gl_position.glsl new file mode 100644 index 00000000..3ac45340 --- /dev/null +++ b/tests/glsl/redundant_gl_position.glsl @@ -0,0 +1,15 @@ +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +void main() +{ + gl_Position = position; + gl_Position = position; +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +void main() +{ + gl_Position = position; +} +*/ diff --git a/tests/glsl/unused_function_removal.glsl b/tests/glsl/unused_function_removal.glsl new file mode 100644 index 00000000..67541de1 --- /dev/null +++ b/tests/glsl/unused_function_removal.glsl @@ -0,0 +1,18 @@ +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +float func() +{ + return 3.0; +} +void main() +{ + gl_Position = position; +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +void main() +{ + gl_Position = position; +} +*/ diff --git a/tests/glsl/unused_variable_removal.glsl b/tests/glsl/unused_variable_removal.glsl new file mode 100644 index 00000000..a295dc70 --- /dev/null +++ b/tests/glsl/unused_variable_removal.glsl @@ -0,0 +1,16 @@ +#pragma MSP stage(vertex) +layout(location=0) in vec4 position; +layout(location=1) in vec4 color; +void main() +{ + out vec2 texcoord = position.xy*0.5+0.5; + gl_Position = position; +} + +/* Expected output: vertex +layout(location=0) in vec4 position; +void main() +{ + gl_Position = position; +} +*/