// 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/shaderc.h" #include #include #include #include #include #include "common_shaders_for_test.h" #include "spirv/unified1/spirv.hpp" namespace { using testing::Each; using testing::HasSubstr; using testing::Not; 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); } #ifndef SHADERC_DISABLE_THREADED_TESTS 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); } #endif TEST(Init, SPVVersion) { unsigned int version = 0; unsigned int revision = 0; shaderc_get_spv_version(&version, &revision); EXPECT_EQ(spv::Version, version); EXPECT_EQ(spv::Revision, revision); } // Determines the kind of output required from the compiler. enum class OutputType { SpirvBinary, SpirvAssemblyText, PreprocessedText, }; // Generate a compilation result object with the given compile, // shader source, shader kind, input file name, entry point name, options, // and for the specified output type. The entry point name is only significant // for HLSL compilation. shaderc_compilation_result_t MakeCompilationResult( const shaderc_compiler_t compiler, const std::string& shader, shaderc_shader_kind kind, const char* input_file_name, const char* entry_point_name, const shaderc_compile_options_t options, OutputType output_type) { switch (output_type) { case OutputType::SpirvBinary: return shaderc_compile_into_spv(compiler, shader.c_str(), shader.size(), kind, input_file_name, entry_point_name, options); break; case OutputType::SpirvAssemblyText: return shaderc_compile_into_spv_assembly( compiler, shader.c_str(), shader.size(), kind, input_file_name, entry_point_name, options); break; case OutputType::PreprocessedText: return shaderc_compile_into_preprocessed_text( compiler, shader.c_str(), shader.size(), kind, input_file_name, entry_point_name, options); break; } // We shouldn't reach here. But some compilers might not know that. // Be a little defensive and produce something. return shaderc_compile_into_spv(compiler, shader.c_str(), shader.size(), kind, input_file_name, entry_point_name, options); } // RAII class for shaderc_compilation_result. Used for shader compilation. class Compilation { public: // Compiles shader and keeps the result. Compilation(const shaderc_compiler_t compiler, const std::string& shader, shaderc_shader_kind kind, const char* input_file_name, const char* entry_point_name, const shaderc_compile_options_t options = nullptr, OutputType output_type = OutputType::SpirvBinary) : compiled_result_( MakeCompilationResult(compiler, shader, kind, input_file_name, entry_point_name, options, output_type)) {} ~Compilation() { shaderc_result_release(compiled_result_); } shaderc_compilation_result_t result() const { return compiled_result_; } private: shaderc_compilation_result_t compiled_result_; }; // RAII class for shaderc_compilation_result. Used for shader assembling. class Assembling { public: // Assembles shader and keeps the result. Assembling(const shaderc_compiler_t compiler, const std::string& assembly, const shaderc_compile_options_t options = nullptr) : compiled_result_(shaderc_assemble_into_spv(compiler, assembly.data(), assembly.size(), options)) {} ~Assembling() { shaderc_result_release(compiled_result_); } shaderc_compilation_result_t result() const { return compiled_result_; } private: shaderc_compilation_result_t compiled_result_; }; struct CleanupOptions { void operator()(shaderc_compile_options_t options) const { shaderc_compile_options_release(options); } }; typedef std::unique_ptr compile_options_ptr; // 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; }; // RAII class for shader_compiler_options_t class Options { public: Options() : options_(shaderc_compile_options_initialize()) {} ~Options() { shaderc_compile_options_release(options_); } shaderc_compile_options_t get() { return options_; } private: shaderc_compile_options_t options_; }; // Helper function to check if the compilation result indicates a successful // compilation. bool CompilationResultIsSuccess(const shaderc_compilation_result_t result) { return shaderc_result_get_compilation_status(result) == shaderc_compilation_status_success; } // Returns true if the given result contains a SPIR-V module that contains // at least the number of bytes of the header and the correct magic number. bool ResultContainsValidSpv(shaderc_compilation_result_t result) { if (!CompilationResultIsSuccess(result)) return false; size_t length = shaderc_result_get_length(result); if (length < 20) return false; const uint32_t* bytes = static_cast( static_cast(shaderc_result_get_bytes(result))); return bytes[0] == spv::MagicNumber; } // Compiles a shader and returns true if the result is valid SPIR-V. bool CompilesToValidSpv(Compiler& compiler, const std::string& shader, shaderc_shader_kind kind, const shaderc_compile_options_t options = nullptr) { const Compilation comp(compiler.get_compiler_handle(), shader, kind, "shader", "main", options, OutputType::SpirvBinary); return ResultContainsValidSpv(comp.result()); } // A testing class to test the compilation of a string with or without options. // This class wraps the initailization of compiler and compiler options and // groups the result checking methods. Subclass tests can access the compiler // object and compiler option object to set their properties. Input file names // are set to "shader". class CompileStringTest : public testing::Test { protected: // Compiles a shader and returns true on success, false on failure. bool CompilationSuccess(const std::string& shader, shaderc_shader_kind kind, shaderc_compile_options_t options = nullptr, OutputType output_type = OutputType::SpirvBinary) { return CompilationResultIsSuccess( Compilation(compiler_.get_compiler_handle(), shader, kind, "shader", "main", options, output_type) .result()); } // Compiles a shader, expects compilation success, and returns the warning // messages. const std::string CompilationWarnings( const std::string& shader, shaderc_shader_kind kind, const shaderc_compile_options_t options = nullptr, OutputType output_type = OutputType::SpirvBinary) { const Compilation comp(compiler_.get_compiler_handle(), shader, kind, "shader", "main", options, output_type); EXPECT_TRUE(CompilationResultIsSuccess(comp.result())) << kind << '\n' << shader; return shaderc_result_get_error_message(comp.result()); } // Compiles a shader, expects compilation failure, and returns the messages. const std::string CompilationErrors( const std::string& shader, shaderc_shader_kind kind, const shaderc_compile_options_t options = nullptr, OutputType output_type = OutputType::SpirvBinary, const char* source_name = "shader") { const Compilation comp(compiler_.get_compiler_handle(), shader, kind, source_name, "main", options, output_type); EXPECT_FALSE(CompilationResultIsSuccess(comp.result())) << kind << '\n' << shader; EXPECT_EQ(0u, shaderc_result_get_length(comp.result())); return shaderc_result_get_error_message(comp.result()); } // Compiles a shader and returns the messages. const std::string CompilationMessages( const std::string& shader, shaderc_shader_kind kind, const shaderc_compile_options_t options = nullptr, OutputType output_type = OutputType::SpirvBinary) { const Compilation comp(compiler_.get_compiler_handle(), shader, kind, "shader", "main", options, output_type); return shaderc_result_get_error_message(comp.result()); } // Compiles a shader, expects compilation success, and returns the output // bytes. const std::string CompilationOutput( const std::string& shader, shaderc_shader_kind kind, const shaderc_compile_options_t options = nullptr, OutputType output_type = OutputType::SpirvBinary) { const Compilation comp(compiler_.get_compiler_handle(), shader, kind, "shader", "main", options, output_type); EXPECT_TRUE(CompilationResultIsSuccess(comp.result())) << "shader kind: " << kind << "\nerror message: " << shaderc_result_get_error_message(comp.result()) << "\nshader source code: \n" << shader; // Use string(const char* s, size_t n) constructor instead of // string(const char* s) to make sure the string has complete binary data. // string(const char* s) assumes a null-terminated C-string, which will cut // the binary data when it sees a '\0' byte. return std::string(shaderc_result_get_bytes(comp.result()), shaderc_result_get_length(comp.result())); } Compiler compiler_; compile_options_ptr options_; public: CompileStringTest() : options_(shaderc_compile_options_initialize()) {} }; // A testing class to test the assembling of a string. // This class wraps the initailization of compiler and groups the result // checking methods. Subclass tests can access the compiler object to set their // properties. class AssembleStringTest : public testing::Test { public: AssembleStringTest() : options_(shaderc_compile_options_initialize()) {} ~AssembleStringTest() { shaderc_compile_options_release(options_); } protected: // Assembles the given assembly and returns true on success. bool AssemblingSuccess(const std::string& assembly) { return CompilationResultIsSuccess( Assembling(compiler_.get_compiler_handle(), assembly, options_) .result()); } bool AssemblingValid(const std::string& assembly) { const auto assembling = Assembling(compiler_.get_compiler_handle(), assembly); return ResultContainsValidSpv(assembling.result()); } Compiler compiler_; shaderc_compile_options_t options_; }; // Name holders so that we have test cases being grouped with only one real // compilation class. using CompileStringWithOptionsTest = CompileStringTest; using CompileKindsTest = CompileStringTest; TEST_F(CompileStringTest, EmptyString) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); EXPECT_FALSE(CompilationSuccess("", shaderc_glsl_vertex_shader)); EXPECT_FALSE(CompilationSuccess("", shaderc_glsl_fragment_shader)); } TEST_F(AssembleStringTest, EmptyString) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); EXPECT_TRUE(AssemblingSuccess("")); } TEST_F(CompileStringTest, GarbageString) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); EXPECT_FALSE(CompilationSuccess("jfalkds", shaderc_glsl_vertex_shader)); EXPECT_FALSE(CompilationSuccess("jfalkds", shaderc_glsl_fragment_shader)); } TEST_F(AssembleStringTest, GarbageString) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); auto assembling = Assembling(compiler_.get_compiler_handle(), "jfalkds"); EXPECT_FALSE(CompilationResultIsSuccess(assembling.result())); EXPECT_EQ(1u, shaderc_result_get_num_errors(assembling.result())); EXPECT_EQ(0u, shaderc_result_get_num_warnings(assembling.result())); } // TODO(antiagainst): right now there is no assembling difference for all the // target environments exposed by shaderc. So the following is just testing the // target environment is accepted. TEST_F(AssembleStringTest, AcceptTargetEnv) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); shaderc_compile_options_set_target_env(options_, shaderc_target_env_opengl, /* version = */ 0); EXPECT_TRUE(AssemblingSuccess("OpCapability Shader")); } TEST_F(CompileStringTest, ReallyLongShader) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); std::string minimal_shader = ""; minimal_shader += "#version 140\n"; minimal_shader += "void foo(){}"; minimal_shader.append(1024 * 1024 * 8, ' '); // 8MB of spaces. minimal_shader += "void main(){}"; EXPECT_TRUE(CompilesToValidSpv(compiler_, minimal_shader, shaderc_glsl_vertex_shader)); EXPECT_TRUE(CompilesToValidSpv(compiler_, minimal_shader, shaderc_glsl_fragment_shader)); } TEST_F(CompileStringTest, MinimalShader) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); EXPECT_TRUE(CompilesToValidSpv(compiler_, kMinimalShader, shaderc_glsl_vertex_shader)); EXPECT_TRUE(CompilesToValidSpv(compiler_, kMinimalShader, shaderc_glsl_fragment_shader)); } TEST_F(AssembleStringTest, MinimalShader) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); EXPECT_TRUE(AssemblingValid(kMinimalShaderAssembly)); } TEST_F(CompileStringTest, WorksWithCompileOptions) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); EXPECT_TRUE(CompilesToValidSpv(compiler_, kMinimalShader, shaderc_glsl_vertex_shader, options_.get())); } TEST_F(CompileStringTest, GetNumErrors) { Compilation comp(compiler_.get_compiler_handle(), kTwoErrorsShader, shaderc_glsl_vertex_shader, "shader", "main"); // Expects compilation failure and two errors. EXPECT_FALSE(CompilationResultIsSuccess(comp.result())); EXPECT_EQ(2u, shaderc_result_get_num_errors(comp.result())); // Expects the number of warnings to be zero. EXPECT_EQ(0u, shaderc_result_get_num_warnings(comp.result())); } TEST_F(CompileStringTest, GetNumWarnings) { Compilation comp(compiler_.get_compiler_handle(), kTwoWarningsShader, shaderc_glsl_vertex_shader, "shader", "main"); // Expects compilation success with two warnings. EXPECT_TRUE(CompilationResultIsSuccess(comp.result())); EXPECT_EQ(2u, shaderc_result_get_num_warnings(comp.result())); // Expects the number of errors to be zero. EXPECT_EQ(0u, shaderc_result_get_num_errors(comp.result())); } TEST_F(CompileStringTest, ZeroErrorsZeroWarnings) { Compilation comp(compiler_.get_compiler_handle(), kMinimalShader, shaderc_glsl_vertex_shader, "shader", "main"); // Expects compilation success with zero warnings. EXPECT_TRUE(CompilationResultIsSuccess(comp.result())); EXPECT_EQ(0u, shaderc_result_get_num_warnings(comp.result())); // Expects the number of errors to be zero. EXPECT_EQ(0u, shaderc_result_get_num_errors(comp.result())); } TEST_F(CompileStringTest, ErrorTypeUnknownShaderStage) { // The shader kind/stage can not be determined, the error type field should // indicate the error type is shaderc_shader_kind_error. Compilation comp(compiler_.get_compiler_handle(), kMinimalShader, shaderc_glsl_infer_from_source, "shader", "main"); EXPECT_EQ(shaderc_compilation_status_invalid_stage, shaderc_result_get_compilation_status(comp.result())); } TEST_F(CompileStringTest, ErrorTypeCompilationError) { // The shader kind is valid, the result object's error type field should // indicate this compilaion fails due to compilation errors. Compilation comp(compiler_.get_compiler_handle(), kTwoErrorsShader, shaderc_glsl_vertex_shader, "shader", "main"); EXPECT_EQ(shaderc_compilation_status_compilation_error, shaderc_result_get_compilation_status(comp.result())); } TEST_F(CompileStringWithOptionsTest, CloneCompilerOptions) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); compile_options_ptr options_(shaderc_compile_options_initialize()); EXPECT_TRUE(CompilesToValidSpv(compiler_, kMinimalShader, shaderc_glsl_vertex_shader, options_.get())); compile_options_ptr cloned_options( shaderc_compile_options_clone(options_.get())); EXPECT_TRUE(CompilesToValidSpv(compiler_, kMinimalShader, shaderc_glsl_vertex_shader, cloned_options.get())); } TEST_F(CompileStringWithOptionsTest, MacroCompileOptions) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); shaderc_compile_options_add_macro_definition(options_.get(), "E", 1u, "main", 4u); const std::string kMinimalExpandedShader = "#version 140\nvoid E(){}"; const std::string kMinimalDoubleExpandedShader = "#version 140\nF E(){}"; EXPECT_TRUE(CompilesToValidSpv(compiler_, kMinimalExpandedShader, shaderc_glsl_vertex_shader, options_.get())); compile_options_ptr cloned_options( shaderc_compile_options_clone(options_.get())); // The simplest should still compile with the cloned options. EXPECT_TRUE(CompilesToValidSpv(compiler_, kMinimalExpandedShader, shaderc_glsl_vertex_shader, cloned_options.get())); EXPECT_FALSE(CompilesToValidSpv(compiler_, kMinimalDoubleExpandedShader, shaderc_glsl_vertex_shader, cloned_options.get())); shaderc_compile_options_add_macro_definition(cloned_options.get(), "F", 1u, "void", 4u); // This should still not work with the original options. EXPECT_FALSE(CompilesToValidSpv(compiler_, kMinimalDoubleExpandedShader, shaderc_glsl_vertex_shader, options_.get())); // This should work with the cloned options that have the additional // parameter. EXPECT_TRUE(CompilesToValidSpv(compiler_, kMinimalDoubleExpandedShader, shaderc_glsl_vertex_shader, cloned_options.get())); } TEST_F(CompileStringWithOptionsTest, MacroCompileOptionsNotNullTerminated) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); shaderc_compile_options_add_macro_definition(options_.get(), "EFGH", 1u, "mainnnnnn", 4u); const std::string kMinimalExpandedShader = "#version 140\nvoid E(){}"; EXPECT_TRUE(CompilesToValidSpv(compiler_, kMinimalExpandedShader, shaderc_glsl_vertex_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, ValuelessMacroCompileOptionsZeroLength) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); shaderc_compile_options_add_macro_definition(options_.get(), "E", 1u, "somthing", 0u); EXPECT_TRUE(CompilesToValidSpv(compiler_, kValuelessPredefinitionShader, shaderc_glsl_vertex_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, ValuelessMacroCompileOptionsNullPointer) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); shaderc_compile_options_add_macro_definition(options_.get(), "E", 1u, nullptr, 100u); EXPECT_TRUE(CompilesToValidSpv(compiler_, kValuelessPredefinitionShader, shaderc_glsl_vertex_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, DisassemblyOption) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); // This should work with both the glslang assembly format and the // SPIR-V tools assembly format. const std::string disassembly_text = CompilationOutput(kMinimalShader, shaderc_glsl_vertex_shader, options_.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("Capability Shader")); EXPECT_THAT(disassembly_text, HasSubstr("MemoryModel")); } TEST_F(CompileStringWithOptionsTest, DisassembleMinimalShader) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); const std::string disassembly_text = CompilationOutput(kMinimalShader, shaderc_glsl_vertex_shader, options_.get(), OutputType::SpirvAssemblyText); for (const auto& substring : kMinimalShaderDisassemblySubstrings) { EXPECT_THAT(disassembly_text, HasSubstr(substring)); } } TEST_F(CompileStringWithOptionsTest, ForcedVersionProfileCorrectStd) { // Forces the version and profile to 450core, which fixes the missing // #version. shaderc_compile_options_set_forced_version_profile(options_.get(), 450, shaderc_profile_core); ASSERT_NE(nullptr, compiler_.get_compiler_handle()); EXPECT_TRUE(CompilesToValidSpv(compiler_, kCoreVertShaderWithoutVersion, shaderc_glsl_vertex_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, ForcedVersionProfileCorrectStdClonedOptions) { // Forces the version and profile to 450core, which fixes the missing // #version. shaderc_compile_options_set_forced_version_profile(options_.get(), 450, shaderc_profile_core); // This mode should be carried to any clone of the original options object. compile_options_ptr cloned_options( shaderc_compile_options_clone(options_.get())); ASSERT_NE(nullptr, compiler_.get_compiler_handle()); EXPECT_TRUE(CompilesToValidSpv(compiler_, kCoreVertShaderWithoutVersion, shaderc_glsl_vertex_shader, cloned_options.get())); } TEST_F(CompileStringWithOptionsTest, ForcedVersionProfileInvalidModule) { // Forces the version and profile to 310es, while the source module is invalid // for this version of GLSL. Compilation should fail. shaderc_compile_options_set_forced_version_profile(options_.get(), 310, shaderc_profile_es); ASSERT_NE(nullptr, compiler_.get_compiler_handle()); EXPECT_THAT(CompilationErrors(kCoreVertShaderWithoutVersion, shaderc_glsl_vertex_shader, options_.get()), HasSubstr("error: 'gl_ClipDistance' : undeclared identifier\n")); } TEST_F(CompileStringWithOptionsTest, ForcedVersionProfileConflictingStd) { // Forces the version and profile to 450core, which is in conflict with the // #version in shader. shaderc_compile_options_set_forced_version_profile(options_.get(), 450, shaderc_profile_core); const std::string kVertexShader = std::string("#version 310 es\n") + kCoreVertShaderWithoutVersion; ASSERT_NE(nullptr, compiler_.get_compiler_handle()); EXPECT_THAT(CompilationWarnings(kVertexShader, shaderc_glsl_vertex_shader, options_.get()), HasSubstr("warning: (version, profile) forced to be (450, core), " "while in source code it is (310, es)\n")); } TEST_F(CompileStringWithOptionsTest, ForcedVersionProfileUnknownVersionStd) { // Forces the version and profile to 4242core, which is an unknown version. shaderc_compile_options_set_forced_version_profile( options_.get(), 4242 /*unknown version*/, shaderc_profile_core); ASSERT_NE(nullptr, compiler_.get_compiler_handle()); // Warning message should complain about the unknown version. // // Also, Glslang errors out on unkown versions, due to commit: // https://github.com/KhronosGroup/glslang/commit/9353f1afab8d1c2b1811c6acd807675128eaabc5 const auto errs = CompilationErrors( kMinimalShader, shaderc_glsl_vertex_shader, options_.get()); EXPECT_THAT( errs, HasSubstr("warning: (version, profile) forced to be (4242, core), " "while in source code it is (140, none)\n")); EXPECT_THAT(errs, HasSubstr("error: version not supported\n")); } TEST_F(CompileStringWithOptionsTest, ForcedVersionProfileVersionsBefore150) { // Versions before 150 do not allow a profile token, shaderc_profile_none // should be passed down as the profile parameter. shaderc_compile_options_set_forced_version_profile(options_.get(), 140, shaderc_profile_none); ASSERT_NE(nullptr, compiler_.get_compiler_handle()); EXPECT_TRUE(CompilationSuccess(kMinimalShaderWithoutVersion, shaderc_glsl_vertex_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, ForcedVersionProfileRedundantProfileStd) { // Forces the version and profile to 100core. But versions before 150 do not // allow a profile token, compilation should fail. shaderc_compile_options_set_forced_version_profile(options_.get(), 100, shaderc_profile_core); ASSERT_NE(nullptr, compiler_.get_compiler_handle()); EXPECT_THAT(CompilationErrors(kMinimalShader, shaderc_glsl_vertex_shader, options_.get()), HasSubstr("error: #version: versions before 150 do not allow a " "profile token\n")); } TEST_F(CompileStringWithOptionsTest, GenerateDebugInfoBinary) { shaderc_compile_options_set_generate_debug_info(options_.get()); ASSERT_NE(nullptr, compiler_.get_compiler_handle()); const std::string binary_output = CompilationOutput(kMinimalDebugInfoShader, shaderc_glsl_vertex_shader, options_.get()); // The binary output should contain the name of the vector (debug_info_sample) // null-terminated, as well as the whole original source. std::string vector_name("debug_info_sample"); vector_name.resize(vector_name.size() + 1); EXPECT_THAT(binary_output, HasSubstr(vector_name)); EXPECT_THAT(binary_output, HasSubstr(kMinimalDebugInfoShader)); } TEST_F(CompileStringWithOptionsTest, GenerateDebugInfoBinaryClonedOptions) { shaderc_compile_options_set_generate_debug_info(options_.get()); compile_options_ptr cloned_options( shaderc_compile_options_clone(options_.get())); ASSERT_NE(nullptr, compiler_.get_compiler_handle()); const std::string binary_output = CompilationOutput(kMinimalDebugInfoShader, shaderc_glsl_vertex_shader, cloned_options.get()); // The binary output should contain the name of the vector (debug_info_sample) // null-terminated, as well as the whole original source. std::string vector_name("debug_info_sample"); vector_name.resize(vector_name.size() + 1); EXPECT_THAT(binary_output, HasSubstr(vector_name)); EXPECT_THAT(binary_output, HasSubstr(kMinimalDebugInfoShader)); } TEST_F(CompileStringWithOptionsTest, GenerateDebugInfoDisassembly) { shaderc_compile_options_set_generate_debug_info(options_.get()); ASSERT_NE(nullptr, compiler_.get_compiler_handle()); // Generate assembly text we can compare its output as a string. // The disassembly output should contain the name of the vector: // debug_info_sample. EXPECT_THAT( CompilationOutput(kMinimalDebugInfoShader, shaderc_glsl_vertex_shader, options_.get(), OutputType::SpirvAssemblyText), HasSubstr("debug_info_sample")); } TEST_F(CompileStringWithOptionsTest, CompileAndOptimizeWithLevelZero) { shaderc_compile_options_set_optimization_level( options_.get(), shaderc_optimization_level_zero); const std::string disassembly_text = CompilationOutput(kMinimalShader, shaderc_glsl_vertex_shader, options_.get(), OutputType::SpirvAssemblyText); for (const auto& substring : kMinimalShaderDisassemblySubstrings) { EXPECT_THAT(disassembly_text, HasSubstr(substring)); } // Check that we still have debug instructions. EXPECT_THAT(disassembly_text, HasSubstr("OpName")); EXPECT_THAT(disassembly_text, HasSubstr("OpSource")); } TEST_F(CompileStringWithOptionsTest, CompileAndOptimizeWithLevelPerformance) { shaderc_compile_options_set_optimization_level( options_.get(), shaderc_optimization_level_performance); const std::string disassembly_text = CompilationOutput(kGlslMultipleFnShader, shaderc_glsl_fragment_shader, options_.get(), OutputType::SpirvAssemblyText); // Check that we do not have function calls anymore. EXPECT_THAT(disassembly_text, Not(HasSubstr("OpFunctionCall"))); } TEST_F(CompileStringWithOptionsTest, CompileAndOptimizeWithLevelSize) { shaderc_compile_options_set_optimization_level( options_.get(), shaderc_optimization_level_size); const std::string disassembly_text = CompilationOutput(kMinimalShader, shaderc_glsl_vertex_shader, options_.get(), OutputType::SpirvAssemblyText); for (const auto& substring : kMinimalShaderDisassemblySubstrings) { EXPECT_THAT(disassembly_text, HasSubstr(substring)); } // Check that we do not have debug instructions. EXPECT_THAT(disassembly_text, Not(HasSubstr("OpName"))); EXPECT_THAT(disassembly_text, Not(HasSubstr("OpSource"))); } TEST_F(CompileStringWithOptionsTest, CompileAndOptimizeForVulkan10Failure) { shaderc_compile_options_set_source_language(options_.get(), shaderc_source_language_hlsl); shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_0); shaderc_compile_options_set_optimization_level( options_.get(), shaderc_optimization_level_performance); EXPECT_FALSE(CompilesToValidSpv(compiler_, kHlslWaveActiveSumeComputeShader, shaderc_compute_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, CompileAndOptimizeForVulkan11Success) { shaderc_compile_options_set_source_language(options_.get(), shaderc_source_language_hlsl); shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_1); shaderc_compile_options_set_optimization_level( options_.get(), shaderc_optimization_level_performance); const std::string disassembly_text = CompilationOutput( kHlslWaveActiveSumeComputeShader, shaderc_compute_shader, options_.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpGroupNonUniformIAdd")); } TEST_F(CompileStringWithOptionsTest, FollowingOptLevelOverridesPreviousOne) { shaderc_compile_options_set_optimization_level( options_.get(), shaderc_optimization_level_size); // Optimization level settings overridden by shaderc_compile_options_set_optimization_level( options_.get(), shaderc_optimization_level_zero); const std::string disassembly_text = CompilationOutput(kMinimalShader, shaderc_glsl_vertex_shader, options_.get(), OutputType::SpirvAssemblyText); for (const auto& substring : kMinimalShaderDisassemblySubstrings) { EXPECT_THAT(disassembly_text, HasSubstr(substring)); } // Check that we still have debug instructions. EXPECT_THAT(disassembly_text, HasSubstr("OpName")); EXPECT_THAT(disassembly_text, HasSubstr("OpSource")); } TEST_F(CompileStringWithOptionsTest, GenerateDebugInfoOverridesOptimizationLevel) { shaderc_compile_options_set_optimization_level( options_.get(), shaderc_optimization_level_size); // Optimization level settings overridden by shaderc_compile_options_set_generate_debug_info(options_.get()); const std::string disassembly_text = CompilationOutput(kMinimalShader, shaderc_glsl_vertex_shader, options_.get(), OutputType::SpirvAssemblyText); for (const auto& substring : kMinimalShaderDebugInfoDisassemblySubstrings) { EXPECT_THAT(disassembly_text, HasSubstr(substring)); } // Check that we still have debug instructions. EXPECT_THAT(disassembly_text, HasSubstr("OpName")); EXPECT_THAT(disassembly_text, HasSubstr("OpSource")); } TEST_F(CompileStringWithOptionsTest, GenerateDebugInfoProhibitsOptimizationLevel) { // Setting generate debug info first also works. shaderc_compile_options_set_generate_debug_info(options_.get()); shaderc_compile_options_set_optimization_level( options_.get(), shaderc_optimization_level_size); const std::string disassembly_text = CompilationOutput(kMinimalShader, shaderc_glsl_vertex_shader, options_.get(), OutputType::SpirvAssemblyText); for (const auto& substring : kMinimalShaderDebugInfoDisassemblySubstrings) { EXPECT_THAT(disassembly_text, HasSubstr(substring)); } // Check that we still have debug instructions. EXPECT_THAT(disassembly_text, HasSubstr("OpName")); EXPECT_THAT(disassembly_text, HasSubstr("OpSource")); } TEST_F(CompileStringWithOptionsTest, PreprocessingOnlyOption) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); const std::string kMinimalShaderWithMacro = "#version 150\n" "#define E main\n" "void E(){}\n"; const std::string preprocessed_text = CompilationOutput(kMinimalShaderWithMacro, shaderc_glsl_vertex_shader, options_.get(), OutputType::PreprocessedText); EXPECT_THAT(preprocessed_text, HasSubstr("void main() { }")); const std::string kMinimalShaderWithMacroCloneOption = "#version 150\n" "#define E_CLONE_OPTION main\n" "void E_CLONE_OPTION(){}\n"; compile_options_ptr cloned_options( shaderc_compile_options_clone(options_.get())); const std::string preprocessed_text_cloned_options = CompilationOutput( kMinimalShaderWithMacroCloneOption, shaderc_glsl_vertex_shader, options_.get(), OutputType::PreprocessedText); EXPECT_THAT(preprocessed_text_cloned_options, HasSubstr("void main() { }")); } // A shader kind test cases needs: 1) A shader text with or without #pragma // annotation, 2) shader_kind. struct ShaderKindTestCase { const char* shader_; shaderc_shader_kind shader_kind_; }; // Test the shader kind deduction process. If the shader kind is one of the // forced ones, the compiler will just try to compile the source code in that // specified shader kind. If the shader kind is shaderc_glsl_deduce_from_pragma, // the compiler will determine the shader kind from #pragma annotation in the // source code and emit error if none such annotation is found. When the shader // kind is one of the default ones, the compiler will fall back to use the // specified shader kind if and only if #pragma annoation is not found. // Valid shader kind settings should generate valid SPIR-V code. using ValidShaderKind = testing::TestWithParam; TEST_P(ValidShaderKind, ValidSpvCode) { const ShaderKindTestCase& test_case = GetParam(); Compiler compiler; EXPECT_TRUE( CompilesToValidSpv(compiler, test_case.shader_, test_case.shader_kind_)); } INSTANTIATE_TEST_SUITE_P( CompileStringTest, ValidShaderKind, testing::ValuesIn(std::vector{ // Valid default shader kinds. {kEmpty310ESShader, shaderc_glsl_default_vertex_shader}, {kEmpty310ESShader, shaderc_glsl_default_fragment_shader}, {kEmpty310ESShader, shaderc_glsl_default_compute_shader}, {kGeometryOnlyShader, shaderc_glsl_default_geometry_shader}, {kTessControlOnlyShader, shaderc_glsl_default_tess_control_shader}, {kTessEvaluationOnlyShader, shaderc_glsl_default_tess_evaluation_shader}, {kNVMeshShader, shaderc_glsl_default_mesh_shader}, {kNVTaskShader, shaderc_glsl_default_task_shader}, // #pragma annotation overrides default shader kinds. {kVertexOnlyShaderWithPragma, shaderc_glsl_default_compute_shader}, {kFragmentOnlyShaderWithPragma, shaderc_glsl_default_vertex_shader}, {kTessControlOnlyShaderWithPragma, shaderc_glsl_default_fragment_shader}, {kTessEvaluationOnlyShaderWithPragma, shaderc_glsl_default_tess_control_shader}, {kGeometryOnlyShaderWithPragma, shaderc_glsl_default_tess_evaluation_shader}, {kComputeOnlyShaderWithPragma, shaderc_glsl_default_geometry_shader}, {kNVMeshShaderWithPragma, shaderc_glsl_default_geometry_shader}, {kNVTaskShaderWithPragma, shaderc_glsl_default_geometry_shader}, // Infer from source {kVertexOnlyShaderWithPragma, shaderc_glsl_infer_from_source}, {kNVMeshShaderWithPragma, shaderc_glsl_infer_from_source}, {kNVTaskShaderWithPragma, shaderc_glsl_infer_from_source}, // Specified non-default shader kind overrides #pragma annotation. {kVertexOnlyShaderWithInvalidPragma, shaderc_glsl_vertex_shader}, })); using InvalidShaderKind = testing::TestWithParam; // Invalid shader kind settings should generate errors. TEST_P(InvalidShaderKind, CompilationShouldFail) { const ShaderKindTestCase& test_case = GetParam(); Compiler compiler; EXPECT_FALSE( CompilesToValidSpv(compiler, test_case.shader_, test_case.shader_kind_)); } INSTANTIATE_TEST_SUITE_P( CompileStringTest, InvalidShaderKind, testing::ValuesIn(std::vector{ // Invalid default shader kind. {kVertexOnlyShader, shaderc_glsl_default_fragment_shader}, // Sets to deduce shader kind from #pragma, but #pragma is defined in // the source code. {kVertexOnlyShader, shaderc_glsl_infer_from_source}, // Invalid #pragma cause errors, even though default shader kind is set // to valid shader kind. {kVertexOnlyShaderWithInvalidPragma, shaderc_glsl_default_vertex_shader}, })); // To test file inclusion, use an unordered_map as a fake file system to store // fake files to be included. The unordered_map represents a filesystem by // mapping filename (or path) string to the contents of that file as a string. using FakeFS = std::unordered_map; // An includer test case needs: 1) A fake file system which is actually an // unordered_map, so that we can resolve the content given a string. A valid // fake file system must have one entry with key:'root' to specify the start // shader file for compilation. 2) An string that we expect to see in the // compilation output. class IncluderTestCase { public: IncluderTestCase(FakeFS fake_fs, std::string expected_substring) : fake_fs_(fake_fs), expected_substring_(expected_substring) { assert(fake_fs_.find("root") != fake_fs_.end() && "Valid fake file system needs a 'root' file\n"); } const FakeFS& fake_fs() const { return fake_fs_; } const std::string& expected_substring() const { return expected_substring_; } private: FakeFS fake_fs_; std::string expected_substring_; }; // A mock class that simulate an includer. C API needs two function pointers // each for get including data and release the data. This class defined two // static functions, which wrap their matching member functions, to be passed to // libshaderc C API. class TestIncluder { public: explicit TestIncluder(const FakeFS& fake_fs) : fake_fs_(fake_fs), responses_({}) {} // Get path and content from the fake file system. shaderc_include_result* GetInclude(const char* filename) { responses_.emplace_back(shaderc_include_result{ filename, strlen(filename), fake_fs_.at(std::string(filename)).c_str(), fake_fs_.at(std::string(filename)).size()}); return &responses_.back(); } // Response data is owned as private property, no need to release explicitly. void ReleaseInclude(shaderc_include_result*) {} // Wrapper for the corresponding member function. static shaderc_include_result* GetIncluderResponseWrapper( void* user_data, const char* filename, int, const char* includer, size_t include_depth) { return static_cast(user_data)->GetInclude(filename); } // Wrapper for the corresponding member function. static void ReleaseIncluderResponseWrapper(void* user_data, shaderc_include_result* data) { return static_cast(user_data)->ReleaseInclude(data); } private: // Includer response data is stored as private property. const FakeFS& fake_fs_; std::vector responses_; }; using IncluderTests = testing::TestWithParam; // Parameterized tests for includer. TEST_P(IncluderTests, SetIncluderCallbacks) { const IncluderTestCase& test_case = GetParam(); const FakeFS& fs = test_case.fake_fs(); const std::string& shader = fs.at("root"); TestIncluder includer(fs); Compiler compiler; compile_options_ptr options(shaderc_compile_options_initialize()); shaderc_compile_options_set_include_callbacks( options.get(), TestIncluder::GetIncluderResponseWrapper, TestIncluder::ReleaseIncluderResponseWrapper, &includer); const Compilation comp(compiler.get_compiler_handle(), shader, shaderc_glsl_vertex_shader, "shader", "main", options.get(), OutputType::PreprocessedText); // Checks the existence of the expected string. EXPECT_THAT(shaderc_result_get_bytes(comp.result()), HasSubstr(test_case.expected_substring())); } TEST_P(IncluderTests, SetIncluderCallbacksClonedOptions) { const IncluderTestCase& test_case = GetParam(); const FakeFS& fs = test_case.fake_fs(); const std::string& shader = fs.at("root"); TestIncluder includer(fs); Compiler compiler; compile_options_ptr options(shaderc_compile_options_initialize()); shaderc_compile_options_set_include_callbacks( options.get(), TestIncluder::GetIncluderResponseWrapper, TestIncluder::ReleaseIncluderResponseWrapper, &includer); // Cloned options should have all the settings. compile_options_ptr cloned_options( shaderc_compile_options_clone(options.get())); const Compilation comp(compiler.get_compiler_handle(), shader, shaderc_glsl_vertex_shader, "shader", "main", cloned_options.get(), OutputType::PreprocessedText); // Checks the existence of the expected string. EXPECT_THAT(shaderc_result_get_bytes(comp.result()), HasSubstr(test_case.expected_substring())); } INSTANTIATE_TEST_SUITE_P(CompileStringTest, IncluderTests, testing::ValuesIn(std::vector{ IncluderTestCase( // Fake file system. { {"root", "#version 150\n" "void foo() {}\n" "#include \"path/to/file_1\"\n"}, {"path/to/file_1", "content of file_1\n"}, }, // Expected output. "#line 0 \"path/to/file_1\"\n" " content of file_1\n" "#line 3"), IncluderTestCase( // Fake file system. {{"root", "#version 150\n" "void foo() {}\n" "#include \"path/to/file_1\"\n"}, {"path/to/file_1", "#include \"path/to/file_2\"\n" "content of file_1\n"}, {"path/to/file_2", "content of file_2\n"}}, // Expected output. "#line 0 \"path/to/file_1\"\n" "#line 0 \"path/to/file_2\"\n" " content of file_2\n" "#line 1 \"path/to/file_1\"\n" " content of file_1\n" "#line 3"), })); TEST_F(CompileStringWithOptionsTest, WarningsOnLine) { // Some versions of Glslang will return an error, some will return just // warnings. EXPECT_THAT( CompilationMessages(kDeprecatedAttributeShader, shaderc_glsl_vertex_shader, options_.get()), HasSubstr(":2: warning: attribute deprecated in version 130; may be " "removed in future release\n")); } TEST_F(CompileStringWithOptionsTest, WarningsOnLineAsErrors) { shaderc_compile_options_set_warnings_as_errors(options_.get()); ASSERT_NE(nullptr, compiler_.get_compiler_handle()); EXPECT_THAT( CompilationErrors(kDeprecatedAttributeShader, shaderc_glsl_vertex_shader, options_.get()), HasSubstr(":2: error: attribute deprecated in version 130; may be " "removed in future release\n")); } TEST_F(CompileStringWithOptionsTest, SuppressWarningsOnLine) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); shaderc_compile_options_set_suppress_warnings(options_.get()); EXPECT_THAT( CompilationMessages(kDeprecatedAttributeShader, shaderc_glsl_vertex_shader, options_.get()), Not(HasSubstr(":2: warning: attribute deprecated in version 130; may be " "removed in future release\n"))); } TEST_F(CompileStringWithOptionsTest, GlobalWarnings) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); shaderc_compile_options_set_forced_version_profile(options_.get(), 400, shaderc_profile_core); EXPECT_THAT(CompilationWarnings(kMinimalUnknownVersionShader, shaderc_glsl_vertex_shader, options_.get()), HasSubstr("(version, profile) forced to be (400, core)," " while in source code it is (550, none)\n")); } TEST_F(CompileStringWithOptionsTest, GlobalWarningsAsErrors) { shaderc_compile_options_set_warnings_as_errors(options_.get()); ASSERT_NE(nullptr, compiler_.get_compiler_handle()); shaderc_compile_options_set_forced_version_profile(options_.get(), 400, shaderc_profile_core); EXPECT_THAT(CompilationErrors(kMinimalUnknownVersionShader, shaderc_glsl_vertex_shader, options_.get()), HasSubstr("(version, profile) forced to be (400, core)," " while in source code it is (550, none)\n")); } TEST_F(CompileStringWithOptionsTest, SuppressGlobalWarnings) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); shaderc_compile_options_set_suppress_warnings(options_.get()); shaderc_compile_options_set_forced_version_profile(options_.get(), 400, shaderc_profile_core); EXPECT_EQ("", CompilationWarnings(kMinimalUnknownVersionShader, shaderc_glsl_vertex_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, SuppressWarningsModeFirstOverridesWarningsAsErrorsMode) { // Sets suppress-warnings mode first, then sets warnings-as-errors mode. // suppress-warnings mode should override warnings-as-errors mode. shaderc_compile_options_set_suppress_warnings(options_.get()); shaderc_compile_options_set_warnings_as_errors(options_.get()); ASSERT_NE(nullptr, compiler_.get_compiler_handle()); // Warnings on particular lines should be inhibited. Compilation comp_line(compiler_.get_compiler_handle(), kDeprecatedAttributeShader, shaderc_glsl_vertex_shader, "shader", "main", options_.get()); EXPECT_EQ(0u, shaderc_result_get_num_warnings(comp_line.result())); // Global warnings should be inhibited. Compilation comp_global( compiler_.get_compiler_handle(), kMinimalUnknownVersionShader, shaderc_glsl_vertex_shader, "shader", "main", options_.get()); EXPECT_EQ(0u, shaderc_result_get_num_warnings(comp_global.result())); } TEST_F(CompileStringWithOptionsTest, SuppressWarningsModeSecondOverridesWarningsAsErrorsMode) { // Sets suppress-warnings mode first, then sets warnings-as-errors mode. // suppress-warnings mode should override warnings-as-errors mode. shaderc_compile_options_set_warnings_as_errors(options_.get()); shaderc_compile_options_set_suppress_warnings(options_.get()); ASSERT_NE(nullptr, compiler_.get_compiler_handle()); // Warnings on particular lines should be inhibited. Compilation comp_line(compiler_.get_compiler_handle(), kDeprecatedAttributeShader, shaderc_glsl_vertex_shader, "shader", "main", options_.get()); EXPECT_EQ(0u, shaderc_result_get_num_warnings(comp_line.result())); // Global warnings should be inhibited. Compilation comp_global( compiler_.get_compiler_handle(), kMinimalUnknownVersionShader, shaderc_glsl_vertex_shader, "shader", "main", options_.get()); EXPECT_EQ(0u, shaderc_result_get_num_warnings(comp_global.result())); } TEST_F(CompileStringWithOptionsTest, IfDefCompileOption) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); shaderc_compile_options_add_macro_definition(options_.get(), "E", 1u, nullptr, 0u); const std::string kMinimalExpandedShader = "#version 140\n" "#ifdef E\n" "void main(){}\n" "#else\n" "#error\n" "#endif"; EXPECT_TRUE(CompilesToValidSpv(compiler_, kMinimalExpandedShader, shaderc_glsl_vertex_shader, options_.get())); } TEST_F( CompileStringWithOptionsTest, TargetEnvRespectedWhenCompilingOpenGLCompatibilityShaderToBinaryAndAlwaysFails) { // Glslang does not support generating SPIR-V for compatibility profile // shaders. EXPECT_FALSE(CompilesToValidSpv(compiler_, kOpenGLCompatibilityFragmentShader, shaderc_glsl_fragment_shader, options_.get())); shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_opengl_compat, 0); EXPECT_FALSE(CompilesToValidSpv(compiler_, kOpenGLCompatibilityFragmentShader, shaderc_glsl_fragment_shader, options_.get())); shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_opengl, 0); EXPECT_FALSE(CompilesToValidSpv(compiler_, kOpenGLCompatibilityFragmentShader, shaderc_glsl_fragment_shader, options_.get())); shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_vulkan, 0); EXPECT_FALSE(CompilesToValidSpv(compiler_, kOpenGLCompatibilityFragmentShader, shaderc_glsl_fragment_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, CompilingFailsWhenTargetingOpenGLCompat) { // Confirm that kOpenGLVertexShader fails when targeting OpenGL // compatibility profile. shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_opengl_compat, 0); EXPECT_FALSE(CompilesToValidSpv(compiler_, kOpenGLVertexShader, shaderc_glsl_vertex_shader, options_.get())); const std::string errors = CompilationErrors( kOpenGLVertexShader, shaderc_glsl_vertex_shader, options_.get()); EXPECT_EQ(errors, "error: OpenGL compatibility profile is not supported"); } TEST_F(CompileStringWithOptionsTest, TargetEnvRespectedWhenCompilingOpenGLCoreShaderToBinary) { // Confirm that kOpenGLVertexShader compiles when targeting OpenGL core // profile. shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_opengl, 0); EXPECT_TRUE(CompilesToValidSpv(compiler_, kOpenGLVertexShader, shaderc_glsl_vertex_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, TargetEnvRespectedWhenCompilingVulkan1_0ShaderToVulkan1_0Succeeds) { shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_0); EXPECT_TRUE(CompilesToValidSpv(compiler_, kGlslShaderComputeBarrier, shaderc_glsl_compute_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, TargetEnvRespectedWhenCompilingVulkan1_0ShaderToVulkan1_1Succeeds) { shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_1); EXPECT_TRUE(CompilesToValidSpv(compiler_, kGlslShaderComputeBarrier, shaderc_glsl_compute_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, TargetEnvRespectedWhenCompilingVulkan1_1ShaderToVulkan1_0Fails) { shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_0); EXPECT_FALSE(CompilesToValidSpv(compiler_, kGlslShaderComputeSubgroupBarrier, shaderc_glsl_compute_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, TargetEnvRespectedWhenCompilingVulkan1_1ShaderToVulkan1_1Succeeds) { shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_1); EXPECT_TRUE(CompilesToValidSpv(compiler_, kGlslShaderComputeSubgroupBarrier, shaderc_glsl_compute_shader, options_.get())); } // task shader TEST_F(CompileStringWithOptionsTest, TargetEnvRespectedWhenCompilingVulkan1_0TaskShaderToVulkan1_0Succeeds) { shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_0); EXPECT_TRUE(CompilesToValidSpv(compiler_, kGlslShaderTaskBarrier, shaderc_glsl_task_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, TargetEnvRespectedWhenCompilingVulkan1_0TaskShaderToVulkan1_1Succeeds) { shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_1); EXPECT_TRUE(CompilesToValidSpv(compiler_, kGlslShaderTaskBarrier, shaderc_glsl_task_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, TargetEnvRespectedWhenCompilingVulkan1_1TaskShaderToVulkan1_0Fails) { shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_0); EXPECT_FALSE(CompilesToValidSpv(compiler_, kGlslShaderTaskSubgroupBarrier, shaderc_glsl_task_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, TargetEnvRespectedWhenCompilingVulkan1_1TaskShaderToVulkan1_1Succeeds) { shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_1); EXPECT_TRUE(CompilesToValidSpv(compiler_, kGlslShaderTaskSubgroupBarrier, shaderc_glsl_task_shader, options_.get())); } // mesh shader TEST_F(CompileStringWithOptionsTest, TargetEnvRespectedWhenCompilingVulkan1_0MeshShaderToVulkan1_0Succeeds) { shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_0); EXPECT_TRUE(CompilesToValidSpv(compiler_, kGlslShaderMeshBarrier, shaderc_glsl_mesh_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, TargetEnvRespectedWhenCompilingVulkan1_0MeshShaderToVulkan1_1Succeeds) { shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_1); EXPECT_TRUE(CompilesToValidSpv(compiler_, kGlslShaderMeshBarrier, shaderc_glsl_mesh_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, TargetEnvRespectedWhenCompilingVulkan1_1MeshShaderToVulkan1_0Fails) { shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_0); EXPECT_FALSE(CompilesToValidSpv(compiler_, kGlslShaderMeshSubgroupBarrier, shaderc_glsl_mesh_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, TargetEnvRespectedWhenCompilingVulkan1_1MeshShaderToVulkan1_1Succeeds) { shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_1); EXPECT_TRUE(CompilesToValidSpv(compiler_, kGlslShaderMeshSubgroupBarrier, shaderc_glsl_mesh_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, DISABLED_TargetEnvIgnoredWhenPreprocessing) { // This test is disabled since some versions of glslang may refuse to compile // very old shaders to SPIR-V with OpenGL target. Re-enable and rewrite this // test once we have a differential set of environments to test. const auto output_type = OutputType::PreprocessedText; EXPECT_TRUE(CompilationSuccess(kOpenGLCompatibilityFragmentShader, shaderc_glsl_fragment_shader, options_.get(), output_type)); shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_opengl_compat, 0); EXPECT_TRUE(CompilationSuccess(kOpenGLCompatibilityFragmentShader, shaderc_glsl_fragment_shader, options_.get(), output_type)); shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_opengl, 0); EXPECT_TRUE(CompilationSuccess(kOpenGLCompatibilityFragmentShader, shaderc_glsl_fragment_shader, options_.get(), output_type)); shaderc_compile_options_set_target_env(options_.get(), shaderc_target_env_vulkan, 0); EXPECT_TRUE(CompilationSuccess(kOpenGLCompatibilityFragmentShader, shaderc_glsl_fragment_shader, options_.get(), output_type)); } TEST_F(CompileStringTest, ShaderKindRespected) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); const std::string kVertexShader = "#version 140\nvoid main(){ gl_Position = vec4(0);}"; EXPECT_TRUE(CompilationSuccess(kVertexShader, shaderc_glsl_vertex_shader)); EXPECT_FALSE(CompilationSuccess(kVertexShader, shaderc_glsl_fragment_shader)); } TEST_F(CompileStringTest, ErrorsReported) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); EXPECT_THAT(CompilationErrors("int f(){return wrongname;}", shaderc_glsl_vertex_shader), HasSubstr("wrongname")); } #ifndef SHADERC_DISABLE_THREADED_TESTS TEST_F(CompileStringTest, MultipleThreadsCalling) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); bool results[10]; std::vector threads; for (auto& r : results) { threads.emplace_back([&r, this]() { r = CompilationSuccess("#version 140\nvoid main(){}", shaderc_glsl_vertex_shader); }); } for (auto& t : threads) { t.join(); } EXPECT_THAT(results, Each(true)); } #endif TEST_F(CompileKindsTest, Vertex) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); const std::string kVertexShader = "#version 140\nvoid main(){ gl_Position = vec4(0);}"; EXPECT_TRUE(CompilationSuccess(kVertexShader, shaderc_glsl_vertex_shader)); } TEST_F(CompileKindsTest, Fragment) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); const std::string kFragShader = "#version 140\nvoid main(){ gl_FragColor = vec4(0);}"; EXPECT_TRUE(CompilationSuccess(kFragShader, shaderc_glsl_fragment_shader)); } TEST_F(CompileKindsTest, Compute) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); const std::string kCompShader = R"(#version 310 es void main() {} )"; EXPECT_TRUE(CompilationSuccess(kCompShader, shaderc_glsl_compute_shader)); } TEST_F(CompileKindsTest, Geometry) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); const std::string kGeoShader = R"(#version 310 es #extension GL_OES_geometry_shader : enable layout(points) in; layout(points, max_vertices=1) out; void main() { gl_Position = vec4(1.0); EmitVertex(); EndPrimitive(); } )"; EXPECT_TRUE(CompilationSuccess(kGeoShader, shaderc_glsl_geometry_shader)); } TEST_F(CompileKindsTest, TessControl) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); const std::string kTessControlShader = R"(#version 310 es #extension GL_OES_tessellation_shader : enable layout(vertices=1) out; void main() {} )"; EXPECT_TRUE( CompilationSuccess(kTessControlShader, shaderc_glsl_tess_control_shader)); } TEST_F(CompileKindsTest, TessEvaluation) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); const std::string kTessEvaluationShader = R"(#version 310 es #extension GL_OES_tessellation_shader : enable layout(triangles, equal_spacing, ccw) in; void main() { gl_Position = vec4(gl_TessCoord, 1.0); } )"; EXPECT_TRUE(CompilationSuccess(kTessEvaluationShader, shaderc_glsl_tess_evaluation_shader)); } // A test case for ParseVersionProfileTest needs: 1) the input string, 2) // expected parsing results, including 'succeed' flag, version value, and // profile enum. struct ParseVersionProfileTestCase { ParseVersionProfileTestCase( const std::string& input_string, bool expected_succeed, int expected_version = 0, shaderc_profile expected_profile = shaderc_profile_none) : input_string_(input_string), expected_succeed_(expected_succeed), expected_version_(expected_version), expected_profile_(expected_profile) {} std::string input_string_; bool expected_succeed_; int expected_version_; shaderc_profile expected_profile_; }; // Test for a helper function to parse version and profile from string. using ParseVersionProfileTest = testing::TestWithParam; TEST_P(ParseVersionProfileTest, FromNullTerminatedString) { const ParseVersionProfileTestCase& test_case = GetParam(); int version; shaderc_profile profile; bool succeed = shaderc_parse_version_profile(test_case.input_string_.c_str(), &version, &profile); EXPECT_EQ(test_case.expected_succeed_, succeed); // check the return version and profile only when the parsing succeeds. if (succeed) { EXPECT_EQ(test_case.expected_version_, version); EXPECT_EQ(test_case.expected_profile_, profile); } } INSTANTIATE_TEST_SUITE_P( HelperMethods, ParseVersionProfileTest, testing::ValuesIn(std::vector{ // Valid version profiles ParseVersionProfileTestCase("450core", true, 450, shaderc_profile_core), ParseVersionProfileTestCase("450compatibility", true, 450, shaderc_profile_compatibility), ParseVersionProfileTestCase("310es", true, 310, shaderc_profile_es), ParseVersionProfileTestCase("100", true, 100, shaderc_profile_none), // Invalid version profiles, the expected_version and expected_profile // doesn't matter as they won't be checked if the tests pass correctly. ParseVersionProfileTestCase("totally_wrong", false), ParseVersionProfileTestCase("111core", false), ParseVersionProfileTestCase("450wrongprofile", false), ParseVersionProfileTestCase("", false), })); TEST_F(CompileStringTest, NullSourceNameFailsCompilingToBinary) { EXPECT_THAT(CompilationErrors(kEmpty310ESShader, shaderc_glsl_vertex_shader, nullptr, OutputType::SpirvBinary, nullptr), HasSubstr("Input file name string was null.")); } TEST_F(CompileStringTest, NullSourceNameFailsCompilingToAssemblyText) { EXPECT_THAT( CompilationErrors(kEmpty310ESShader, shaderc_glsl_vertex_shader, nullptr, OutputType::SpirvAssemblyText, nullptr), HasSubstr("Input file name string was null.")); } TEST_F(CompileStringTest, NullSourceNameFailsCompilingToPreprocessedText) { EXPECT_THAT(CompilationErrors(kEmpty310ESShader, shaderc_glsl_vertex_shader, nullptr, OutputType::PreprocessedText, nullptr), HasSubstr("Input file name string was null.")); } const char kGlslVertexShader[] = "#version 140\nvoid main(){ gl_Position = vec4(0);}"; const char kHlslVertexShader[] = "float4 EntryPoint(uint index : SV_VERTEXID) : SV_POSITION\n" "{ return float4(1.0, 2.0, 3.0, 4.0); }"; TEST_F(CompileStringTest, LangGlslOnGlslVertexSucceeds) { shaderc_compile_options_set_source_language(options_.get(), shaderc_source_language_glsl); EXPECT_TRUE(CompilationSuccess(kGlslVertexShader, shaderc_glsl_vertex_shader, options_.get())); } TEST_F(CompileStringTest, LangGlslOnHlslVertexFails) { shaderc_compile_options_set_source_language(options_.get(), shaderc_source_language_glsl); EXPECT_FALSE(CompilationSuccess(kHlslVertexShader, shaderc_glsl_vertex_shader, options_.get())); } TEST_F(CompileStringTest, LangHlslOnGlslVertexFails) { shaderc_compile_options_set_source_language(options_.get(), shaderc_source_language_hlsl); EXPECT_FALSE(CompilationSuccess(kGlslVertexShader, shaderc_glsl_vertex_shader, options_.get())); } TEST_F(CompileStringTest, LangHlslOnHlslVertexSucceeds) { shaderc_compile_options_set_source_language(options_.get(), shaderc_source_language_hlsl); EXPECT_TRUE(CompilationSuccess(kHlslVertexShader, shaderc_glsl_vertex_shader, options_.get())); } TEST(EntryPointTest, LangGlslOnHlslVertexSucceedsButAssumesEntryPointNameIsMain) { Compiler compiler; Options options; auto compilation = Compilation(compiler.get_compiler_handle(), kGlslVertexShader, shaderc_glsl_vertex_shader, "shader", "blah blah blah", options.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(shaderc_result_get_bytes(compilation.result()), HasSubstr("OpEntryPoint Vertex %main \"main\"")) << std::string(shaderc_result_get_bytes(compilation.result())); } TEST(EntryPointTest, LangHlslOnHlslVertexSucceedsWithGivenEntryPointName) { Compiler compiler; Options options; shaderc_compile_options_set_source_language(options.get(), shaderc_source_language_hlsl); auto compilation = Compilation(compiler.get_compiler_handle(), kHlslVertexShader, shaderc_glsl_vertex_shader, "shader", "EntryPoint", options.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(shaderc_result_get_bytes(compilation.result()), HasSubstr("OpEntryPoint Vertex %EntryPoint \"EntryPoint\"")) << std::string(shaderc_result_get_bytes(compilation.result())); } // Returns a fragment shader accessing a texture with the given // offset. std::string ShaderWithTexOffset(int offset) { std::ostringstream oss; oss << "#version 450\n" "layout (binding=0) uniform sampler1D tex;\n" "void main() { vec4 x = textureOffset(tex, 1.0, " << offset << "); }\n"; return oss.str(); } // Ensure compilation is sensitive to limit setting. Sample just // two particular limits. TEST_F(CompileStringTest, LimitsTexelOffsetDefault) { EXPECT_FALSE(CompilationSuccess(ShaderWithTexOffset(-9).c_str(), shaderc_glsl_fragment_shader, options_.get())); EXPECT_TRUE(CompilationSuccess(ShaderWithTexOffset(-8).c_str(), shaderc_glsl_fragment_shader, options_.get())); EXPECT_TRUE(CompilationSuccess(ShaderWithTexOffset(7).c_str(), shaderc_glsl_fragment_shader, options_.get())); EXPECT_FALSE(CompilationSuccess(ShaderWithTexOffset(8).c_str(), shaderc_glsl_fragment_shader, options_.get())); } TEST_F(CompileStringTest, LimitsTexelOffsetLowerMinimum) { shaderc_compile_options_set_limit( options_.get(), shaderc_limit_min_program_texel_offset, -99); EXPECT_FALSE(CompilationSuccess(ShaderWithTexOffset(-100).c_str(), shaderc_glsl_fragment_shader, options_.get())); EXPECT_TRUE(CompilationSuccess(ShaderWithTexOffset(-99).c_str(), shaderc_glsl_fragment_shader, options_.get())); } TEST_F(CompileStringTest, LimitsTexelOffsetHigherMaximum) { shaderc_compile_options_set_limit(options_.get(), shaderc_limit_max_program_texel_offset, 10); EXPECT_TRUE(CompilationSuccess(ShaderWithTexOffset(10).c_str(), shaderc_glsl_fragment_shader, options_.get())); EXPECT_FALSE(CompilationSuccess(ShaderWithTexOffset(11).c_str(), shaderc_glsl_fragment_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, UniformsWithoutBindingsFailCompilation) { const std::string errors = CompilationErrors(kShaderWithUniformsWithoutBindings, shaderc_glsl_vertex_shader, options_.get()); EXPECT_THAT(errors, HasSubstr("sampler/texture/image requires layout(binding=X)")); } TEST_F(CompileStringWithOptionsTest, UniformsWithoutBindingsOptionSetAutoBindingsAssignsBindings) { shaderc_compile_options_set_auto_bind_uniforms(options_.get(), true); const std::string disassembly_text = CompilationOutput( kShaderWithUniformsWithoutBindings, shaderc_glsl_vertex_shader, options_.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_tex Binding 0")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_sam Binding 1")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_img Binding 2")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_imbuf Binding 3")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_ubo Binding 4")); } TEST_F(CompileStringWithOptionsTest, AutoBindUniformsOptionsSurvivesCloning) { shaderc_compile_options_set_auto_bind_uniforms(options_.get(), true); compile_options_ptr cloned_options( shaderc_compile_options_clone(options_.get())); const std::string disassembly_text = CompilationOutput( kShaderWithUniformsWithoutBindings, shaderc_glsl_vertex_shader, cloned_options.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_tex Binding 0")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_sam Binding 1")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_img Binding 2")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_imbuf Binding 3")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_ubo Binding 4")); } TEST_F(CompileStringWithOptionsTest, SetBindingBaseForTextureAdjustsTextureBindingsOnly) { shaderc_compile_options_set_auto_bind_uniforms(options_.get(), true); shaderc_compile_options_set_binding_base(options_.get(), shaderc_uniform_kind_texture, 44); const std::string disassembly_text = CompilationOutput( kShaderWithUniformsWithoutBindings, shaderc_glsl_vertex_shader, options_.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_tex Binding 44")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_sam Binding 0")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_img Binding 1")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_imbuf Binding 2")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_ubo Binding 3")); } TEST_F(CompileStringWithOptionsTest, SetBindingBaseForSamplerAdjustsSamplerBindingsOnly) { shaderc_compile_options_set_auto_bind_uniforms(options_.get(), true); shaderc_compile_options_set_binding_base(options_.get(), shaderc_uniform_kind_sampler, 44); const std::string disassembly_text = CompilationOutput( kShaderWithUniformsWithoutBindings, shaderc_glsl_vertex_shader, options_.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_tex Binding 0")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_sam Binding 44")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_img Binding 1")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_imbuf Binding 2")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_ubo Binding 3")); } TEST_F(CompileStringWithOptionsTest, SetBindingBaseForImageAdjustsImageBindingsOnly) { shaderc_compile_options_set_auto_bind_uniforms(options_.get(), true); shaderc_compile_options_set_binding_base(options_.get(), shaderc_uniform_kind_image, 44); const std::string disassembly_text = CompilationOutput( kShaderWithUniformsWithoutBindings, shaderc_glsl_vertex_shader, options_.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_tex Binding 0")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_sam Binding 1")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_img Binding 44")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_imbuf Binding 45")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_ubo Binding 2")); } TEST_F(CompileStringWithOptionsTest, SetBindingBaseForBufferAdjustsBufferBindingsOnly) { shaderc_compile_options_set_auto_bind_uniforms(options_.get(), true); shaderc_compile_options_set_binding_base(options_.get(), shaderc_uniform_kind_buffer, 44); const std::string disassembly_text = CompilationOutput( kShaderWithUniformsWithoutBindings, shaderc_glsl_vertex_shader, options_.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_tex Binding 0")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_sam Binding 1")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_img Binding 2")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_imbuf Binding 3")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_ubo Binding 44")); } TEST_F(CompileStringWithOptionsTest, SetBindingBaseSurvivesCloning) { shaderc_compile_options_set_auto_bind_uniforms(options_.get(), true); shaderc_compile_options_set_binding_base(options_.get(), shaderc_uniform_kind_texture, 40); shaderc_compile_options_set_binding_base(options_.get(), shaderc_uniform_kind_sampler, 50); shaderc_compile_options_set_binding_base(options_.get(), shaderc_uniform_kind_image, 60); shaderc_compile_options_set_binding_base(options_.get(), shaderc_uniform_kind_buffer, 70); compile_options_ptr cloned_options( shaderc_compile_options_clone(options_.get())); const std::string disassembly_text = CompilationOutput( kShaderWithUniformsWithoutBindings, shaderc_glsl_vertex_shader, cloned_options.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_tex Binding 40")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_sam Binding 50")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_img Binding 60")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_imbuf Binding 61")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_ubo Binding 70")); } TEST(Compiler, IncludeWithoutOptionsReturnsValidError) { auto compiler = shaderc_compiler_initialize(); const char source[] = "#version 450\n#include \"no where\""; auto result = shaderc_compile_into_spv(compiler, source, strlen(source), shaderc_glsl_vertex_shader, "file", "main", nullptr); EXPECT_EQ(shaderc_compilation_status_compilation_error, shaderc_result_get_compilation_status(result)); EXPECT_THAT(shaderc_result_get_error_message(result), HasSubstr("error: '#include' : #error unexpected include " "directive for header name: no where")); shaderc_result_release(result); shaderc_compiler_release(compiler); } TEST_F( CompileStringWithOptionsTest, SetBindingBaseForTextureForVertexAdjustsTextureBindingsOnlyCompilingAsVertex) { shaderc_compile_options_set_auto_bind_uniforms(options_.get(), true); shaderc_compile_options_set_binding_base_for_stage( options_.get(), shaderc_vertex_shader, shaderc_uniform_kind_texture, 100); const std::string disassembly_text = CompilationOutput( kShaderWithUniformsWithoutBindings, shaderc_vertex_shader, options_.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_tex Binding 100")) << disassembly_text; EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_sam Binding 0")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_img Binding 1")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_imbuf Binding 2")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_ubo Binding 3")); } TEST_F(CompileStringWithOptionsTest, SetBindingBaseForTextureForVertexIgnoredWhenCompilingAsFragment) { shaderc_compile_options_set_auto_bind_uniforms(options_.get(), true); // This is ignored since we're compiling as a different stage. shaderc_compile_options_set_binding_base_for_stage( options_.get(), shaderc_vertex_shader, shaderc_uniform_kind_texture, 100); const std::string disassembly_text = CompilationOutput( kShaderWithUniformsWithoutBindings, shaderc_fragment_shader, options_.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_tex Binding 0")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_sam Binding 1")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_img Binding 2")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_imbuf Binding 3")); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorate %my_ubo Binding 4")); } TEST_F(CompileStringWithOptionsTest, GlslDefaultPackingUsed) { const std::string disassembly_text = CompilationOutput(kGlslShaderWeirdPacking, shaderc_vertex_shader, options_.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpMemberDecorate %B 1 Offset 16")); } TEST_F(CompileStringWithOptionsTest, HlslOffsetsOptionDisableRespected) { shaderc_compile_options_set_hlsl_offsets(options_.get(), false); const std::string disassembly_text = CompilationOutput(kGlslShaderWeirdPacking, shaderc_vertex_shader, options_.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpMemberDecorate %B 1 Offset 16")); } TEST_F(CompileStringWithOptionsTest, HlslOffsetsOptionEnableRespected) { shaderc_compile_options_set_hlsl_offsets(options_.get(), true); const std::string disassembly_text = CompilationOutput(kGlslShaderWeirdPacking, shaderc_vertex_shader, options_.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpMemberDecorate %B 1 Offset 4")); } TEST_F(CompileStringWithOptionsTest, HlslFunctionality1OffByDefault) { shaderc_compile_options_set_source_language(options_.get(), shaderc_source_language_hlsl); // The counter should automatically get a binding. shaderc_compile_options_set_auto_bind_uniforms(options_.get(), true); const std::string disassembly_text = CompilationOutput(kHlslShaderWithCounterBuffer, shaderc_fragment_shader, options_.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, Not(HasSubstr("OpDecorateString"))) << disassembly_text; } TEST_F(CompileStringWithOptionsTest, HlslFunctionality1Respected) { shaderc_compile_options_set_source_language(options_.get(), shaderc_source_language_hlsl); shaderc_compile_options_set_hlsl_functionality1(options_.get(), true); // The counter should automatically get a binding. shaderc_compile_options_set_auto_bind_uniforms(options_.get(), true); const std::string disassembly_text = CompilationOutput(kHlslShaderWithCounterBuffer, shaderc_fragment_shader, options_.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorateString")); } TEST_F(CompileStringWithOptionsTest, HlslFunctionality1SurvivesCloning) { shaderc_compile_options_set_source_language(options_.get(), shaderc_source_language_hlsl); shaderc_compile_options_set_hlsl_functionality1(options_.get(), true); // The counter should automatically get a binding. shaderc_compile_options_set_auto_bind_uniforms(options_.get(), true); compile_options_ptr cloned_options( shaderc_compile_options_clone(options_.get())); const std::string disassembly_text = CompilationOutput(kHlslShaderWithCounterBuffer, shaderc_fragment_shader, cloned_options.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpDecorateString")); } TEST_F(CompileStringWithOptionsTest, HlslFlexibleMemoryLayoutAllowed) { shaderc_compile_options_set_source_language(options_.get(), shaderc_source_language_hlsl); shaderc_compile_options_set_optimization_level( options_.get(), shaderc_optimization_level_performance); // There is no way to set the counter's binding, so set it automatically. // See https://github.com/KhronosGroup/glslang/issues/1616 shaderc_compile_options_set_auto_bind_uniforms(options_.get(), true); EXPECT_TRUE(CompilesToValidSpv(compiler_, kHlslMemLayoutResourceSelect, shaderc_fragment_shader, options_.get())); } TEST_F(CompileStringWithOptionsTest, ClampMapsToFClampByDefault) { const std::string disassembly_text = CompilationOutput(kGlslShaderWithClamp, shaderc_fragment_shader, options_.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpExtInst %v4float %1 FClamp")); } TEST_F(CompileStringWithOptionsTest, ClampMapsToNClampWithNanClamp) { shaderc_compile_options_set_nan_clamp(options_.get(), true); const std::string disassembly_text = CompilationOutput(kGlslShaderWithClamp, shaderc_fragment_shader, options_.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpExtInst %v4float %1 NClamp")); } TEST_F(CompileStringWithOptionsTest, NanClampSurvivesCloning) { shaderc_compile_options_set_nan_clamp(options_.get(), true); compile_options_ptr cloned_options( shaderc_compile_options_clone(options_.get())); const std::string disassembly_text = CompilationOutput(kGlslShaderWithClamp, shaderc_fragment_shader, cloned_options.get(), OutputType::SpirvAssemblyText); EXPECT_THAT(disassembly_text, HasSubstr("OpExtInst %v4float %1 NClamp")); } } // anonymous namespace