| // Copyright 2015 The Shaderc Authors. All rights reserved. |
| // |
| // 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 "shaderc.h" |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <thread> |
| |
| #include "SPIRV/spirv.h" |
| |
| namespace { |
| |
| using testing::Each; |
| using testing::HasSubstr; |
| |
| TEST(Init, MultipleCalls) { |
| shaderc_compiler_t compiler1, compiler2, compiler3; |
| EXPECT_NE(nullptr, compiler1 = shaderc_compiler_initialize()); |
| EXPECT_NE(nullptr, compiler2 = shaderc_compiler_initialize()); |
| EXPECT_NE(nullptr, compiler3 = shaderc_compiler_initialize()); |
| shaderc_compiler_release(compiler1); |
| shaderc_compiler_release(compiler2); |
| shaderc_compiler_release(compiler3); |
| } |
| |
| TEST(Init, MultipleThreadsCalling) { |
| shaderc_compiler_t compiler1, compiler2, compiler3; |
| std::thread t1([&compiler1]() { compiler1 = shaderc_compiler_initialize(); }); |
| std::thread t2([&compiler2]() { compiler2 = shaderc_compiler_initialize(); }); |
| std::thread t3([&compiler3]() { compiler3 = shaderc_compiler_initialize(); }); |
| t1.join(); |
| t2.join(); |
| t3.join(); |
| EXPECT_NE(nullptr, compiler1); |
| EXPECT_NE(nullptr, compiler2); |
| EXPECT_NE(nullptr, compiler3); |
| shaderc_compiler_release(compiler1); |
| shaderc_compiler_release(compiler2); |
| shaderc_compiler_release(compiler3); |
| } |
| |
| // RAII class for shaderc_spv_module. |
| class Compilation { |
| public: |
| // Compiles shader, keeping the result. |
| Compilation(const shaderc_compiler_t compiler, const std::string& shader, |
| shaderc_shader_kind kind) |
| : compiled_result_(shaderc_compile_into_spv(compiler, shader.c_str(), |
| shader.size(), kind, "")) {} |
| |
| ~Compilation() { shaderc_module_release(compiled_result_); } |
| |
| shaderc_spv_module_t result() { return compiled_result_; } |
| |
| private: |
| shaderc_spv_module_t compiled_result_; |
| }; |
| |
| // RAII class for shaderc_compiler_t |
| class Compiler { |
| public: |
| Compiler() { compiler = shaderc_compiler_initialize(); } |
| ~Compiler() { shaderc_compiler_release(compiler); } |
| shaderc_compiler_t get_compiler_handle() { return compiler; } |
| |
| private: |
| shaderc_compiler_t compiler; |
| }; |
| |
| // Compiles a shader and returns true on success, false on failure. |
| bool CompilationSuccess(const shaderc_compiler_t compiler, |
| const std::string& shader, shaderc_shader_kind kind) { |
| return shaderc_module_get_success( |
| Compilation(compiler, shader, kind).result()); |
| } |
| |
| // Compiles a shader and returns true if the result is valid SPIR-V. |
| bool CompilesToValidSpv(const shaderc_compiler_t compiler, |
| const std::string& shader, shaderc_shader_kind kind) { |
| Compilation comp(compiler, shader, kind); |
| auto result = comp.result(); |
| if (!shaderc_module_get_success(result)) return false; |
| size_t length = shaderc_module_get_length(result); |
| if (length < 20) return false; |
| const uint32_t* bytes = static_cast<const uint32_t*>( |
| static_cast<const void*>(shaderc_module_get_bytes(result))); |
| return bytes[0] == spv::MagicNumber; |
| } |
| |
| TEST(CompileString, EmptyString) { |
| Compiler compiler; |
| ASSERT_NE(nullptr, compiler.get_compiler_handle()); |
| EXPECT_TRUE(CompilationSuccess(compiler.get_compiler_handle(), "", |
| shaderc_glsl_vertex_shader)); |
| EXPECT_TRUE(CompilationSuccess(compiler.get_compiler_handle(), "", |
| shaderc_glsl_fragment_shader)); |
| } |
| |
| TEST(CompileString, FailsWithCleanedUpCompiler) { |
| Compiler compiler; |
| ASSERT_NE(nullptr, compiler.get_compiler_handle()); |
| EXPECT_TRUE(CompilationSuccess(compiler.get_compiler_handle(), "", |
| shaderc_glsl_vertex_shader)); |
| EXPECT_TRUE(CompilationSuccess(compiler.get_compiler_handle(), "", |
| shaderc_glsl_fragment_shader)); |
| } |
| |
| TEST(CompileString, GarbageString) { |
| Compiler compiler; |
| ASSERT_NE(nullptr, compiler.get_compiler_handle()); |
| EXPECT_FALSE(CompilationSuccess(compiler.get_compiler_handle(), "jfalkds", |
| shaderc_glsl_vertex_shader)); |
| EXPECT_FALSE(CompilationSuccess(compiler.get_compiler_handle(), "jfalkds", |
| shaderc_glsl_fragment_shader)); |
| } |
| |
| TEST(CompileString, ReallyLongShader) { |
| Compiler compiler; |
| ASSERT_NE(nullptr, compiler.get_compiler_handle()); |
| std::string minimal_shader = ""; |
| minimal_shader += "void foo(){}"; |
| minimal_shader.append(1024 * 1024 * 8, ' '); // 8MB of spaces. |
| minimal_shader += "void main(){}"; |
| EXPECT_TRUE(CompilesToValidSpv(compiler.get_compiler_handle(), minimal_shader, |
| shaderc_glsl_vertex_shader)); |
| EXPECT_TRUE(CompilesToValidSpv(compiler.get_compiler_handle(), minimal_shader, |
| shaderc_glsl_fragment_shader)); |
| } |
| |
| TEST(CompileString, MinimalShader) { |
| Compiler compiler; |
| ASSERT_NE(nullptr, compiler.get_compiler_handle()); |
| const std::string kMinimalShader = "void main(){}"; |
| EXPECT_TRUE(CompilesToValidSpv(compiler.get_compiler_handle(), kMinimalShader, |
| shaderc_glsl_vertex_shader)); |
| EXPECT_TRUE(CompilesToValidSpv(compiler.get_compiler_handle(), kMinimalShader, |
| shaderc_glsl_fragment_shader)); |
| } |
| |
| TEST(CompileString, ShaderKindRespected) { |
| Compiler compiler; |
| ASSERT_NE(nullptr, compiler.get_compiler_handle()); |
| const std::string kVertexShader = "vec4 foo(){return gl_Position;}"; |
| EXPECT_TRUE(CompilationSuccess(compiler.get_compiler_handle(), kVertexShader, |
| shaderc_glsl_vertex_shader)); |
| EXPECT_FALSE(CompilationSuccess(compiler.get_compiler_handle(), kVertexShader, |
| shaderc_glsl_fragment_shader)); |
| } |
| |
| TEST(CompileString, ErrorsReported) { |
| Compiler compiler; |
| ASSERT_NE(nullptr, compiler.get_compiler_handle()); |
| Compilation comp(compiler.get_compiler_handle(), "int f(){return wrongname;}", |
| shaderc_glsl_vertex_shader); |
| ASSERT_FALSE(shaderc_module_get_success(comp.result())); |
| EXPECT_THAT(shaderc_module_get_error_message(comp.result()), |
| HasSubstr("wrongname")); |
| } |
| |
| TEST(CompileString, MultipleThreadsCalling) { |
| Compiler compiler; |
| ASSERT_NE(nullptr, compiler.get_compiler_handle()); |
| bool results[10]; |
| std::vector<std::thread> threads; |
| for (auto& r : results) { |
| threads.emplace_back([&compiler, &r]() { |
| r = CompilationSuccess(compiler.get_compiler_handle(), "void main(){}", |
| shaderc_glsl_vertex_shader); |
| }); |
| } |
| for (auto& t : threads) { |
| t.join(); |
| } |
| EXPECT_THAT(results, Each(true)); |
| } |
| |
| } // anonymous namespace |