diff options
author | Lei Zhang <antiagainst@google.com> | 2016-09-19 14:58:00 -0400 |
---|---|---|
committer | Lei Zhang <antiagainst@google.com> | 2016-09-23 14:26:26 -0400 |
commit | 72d6deecad661cfa34d479b3ca1843456d75c893 (patch) | |
tree | be384eb697a0bab3690074a370be736d750cba79 | |
parent | 29a28289169c82adea647387ba8c2162d6aa8d7f (diff) | |
download | shaderc-72d6deecad661cfa34d479b3ca1843456d75c893.tar.gz |
Add support for optimization after compilation in the library.
Two optimization levels are added:
* shaderc_optimization_level_zero
* shaderc_optimization_level_size
The first does no optimization, while the latter runs
strip-debug-info and then unify-constant pass.
This is just for compilation; no assembling support yet.
-rw-r--r-- | libshaderc/include/shaderc/shaderc.h | 11 | ||||
-rw-r--r-- | libshaderc/include/shaderc/shaderc.hpp | 6 | ||||
-rw-r--r-- | libshaderc/src/shaderc.cc | 14 | ||||
-rw-r--r-- | libshaderc/src/shaderc_cpp_test.cc | 66 | ||||
-rw-r--r-- | libshaderc/src/shaderc_test.cc | 79 | ||||
-rw-r--r-- | libshaderc_util/CMakeLists.txt | 6 | ||||
-rw-r--r-- | libshaderc_util/include/libshaderc_util/compiler.h | 29 | ||||
-rw-r--r-- | libshaderc_util/include/libshaderc_util/spirv_tools_wrapper.h | 17 | ||||
-rw-r--r-- | libshaderc_util/src/compiler.cc | 40 | ||||
-rw-r--r-- | libshaderc_util/src/spirv_tools_wrapper.cc | 40 |
10 files changed, 294 insertions, 14 deletions
diff --git a/libshaderc/include/shaderc/shaderc.h b/libshaderc/include/shaderc/shaderc.h index 279addb..e798229 100644 --- a/libshaderc/include/shaderc/shaderc.h +++ b/libshaderc/include/shaderc/shaderc.h @@ -74,6 +74,12 @@ typedef enum { shaderc_compilation_status_invalid_assembly, } shaderc_compilation_status; +// Optimization level. +typedef enum { + shaderc_optimization_level_zero, // no optimization + shaderc_optimization_level_size, // optimize towards reducing code size +} shaderc_optimization_level; + // Usage examples: // // Aggressively release compiler resources, but spend time in initialization @@ -156,6 +162,11 @@ void shaderc_compile_options_add_macro_definition( void shaderc_compile_options_set_generate_debug_info( shaderc_compile_options_t options); +// Sets the compiler optimization level to the given level. Only the last one +// takes effect if multiple calls of this function exist. +void shaderc_compile_options_set_optimization_level( + shaderc_compile_options_t options, shaderc_optimization_level level); + // Forces the GLSL language version and profile to a given pair. The version // number is the same as would appear in the #version annotation in the source. // Version and profile specified here overrides the #version annotation in the diff --git a/libshaderc/include/shaderc/shaderc.hpp b/libshaderc/include/shaderc/shaderc.hpp index 269445d..1666397 100644 --- a/libshaderc/include/shaderc/shaderc.hpp +++ b/libshaderc/include/shaderc/shaderc.hpp @@ -163,6 +163,12 @@ class CompileOptions { shaderc_compile_options_set_generate_debug_info(options_); } + // Sets the compiler optimization level to the given level. Only the last one + // takes effect if multiple calls of this function exist. + void SetOptimizationLevel(shaderc_optimization_level level) { + shaderc_compile_options_set_optimization_level(options_, level); + } + // A C++ version of the libshaderc includer interface. class IncluderInterface { public: diff --git a/libshaderc/src/shaderc.cc b/libshaderc/src/shaderc.cc index aa85bd3..55f9b4b 100644 --- a/libshaderc/src/shaderc.cc +++ b/libshaderc/src/shaderc.cc @@ -274,6 +274,20 @@ void shaderc_compile_options_set_generate_debug_info( options->compiler.SetGenerateDebugInfo(); } +void shaderc_compile_options_set_optimization_level( + shaderc_compile_options_t options, shaderc_optimization_level level) { + auto opt_level = shaderc_util::Compiler::OptimizationLevel::Zero; + switch (level) { + case shaderc_optimization_level_size: + opt_level = shaderc_util::Compiler::OptimizationLevel::Size; + break; + default: + break; + } + + options->compiler.SetOptimizationLevel(opt_level); +} + void shaderc_compile_options_set_forced_version_profile( shaderc_compile_options_t options, int version, shaderc_profile profile) { // Transfer the profile parameter from public enum type to glslang internal diff --git a/libshaderc/src/shaderc_cpp_test.cc b/libshaderc/src/shaderc_cpp_test.cc index 8f6a22a..88ed03d 100644 --- a/libshaderc/src/shaderc_cpp_test.cc +++ b/libshaderc/src/shaderc_cpp_test.cc @@ -479,6 +479,72 @@ TEST_F(CppInterface, GenerateDebugInfoDisassemblyClonedOptions) { HasSubstr("debug_info_sample")); } +TEST_F(CppInterface, CompileAndOptimizeWithLevelZero) { + options_.SetOptimizationLevel(shaderc_optimization_level_zero); + const std::string disassembly_text = + AssemblyOutput(kMinimalShader, shaderc_glsl_vertex_shader, options_); + 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(CppInterface, CompileAndOptimizeWithLevelSize) { + options_.SetOptimizationLevel(shaderc_optimization_level_size); + const std::string disassembly_text = + AssemblyOutput(kMinimalShader, shaderc_glsl_vertex_shader, options_); + 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(CppInterface, FollowingOptLevelOverridesPreviousOne) { + options_.SetOptimizationLevel(shaderc_optimization_level_size); + // Optimization level settings overridden by + options_.SetOptimizationLevel(shaderc_optimization_level_zero); + const std::string disassembly_text = + AssemblyOutput(kMinimalShader, shaderc_glsl_vertex_shader, options_); + 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(CppInterface, GenerateDebugInfoOverridesOptimizationLevel) { + options_.SetOptimizationLevel(shaderc_optimization_level_size); + // Optimization level settings overridden by + options_.SetGenerateDebugInfo(); + const std::string disassembly_text = + AssemblyOutput(kMinimalShader, shaderc_glsl_vertex_shader, options_); + 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(CppInterface, GenerateDebugInfoProhibitsOptimizationLevel) { + // Setting generate debug info first also works. + options_.SetGenerateDebugInfo(); + options_.SetOptimizationLevel(shaderc_optimization_level_size); + const std::string disassembly_text = + AssemblyOutput(kMinimalShader, shaderc_glsl_vertex_shader, options_); + 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(CppInterface, GetNumErrors) { std::string shader(kTwoErrorsShader); const SpvCompilationResult compilation_result = diff --git a/libshaderc/src/shaderc_test.cc b/libshaderc/src/shaderc_test.cc index b4ce44b..878f3d5 100644 --- a/libshaderc/src/shaderc_test.cc +++ b/libshaderc/src/shaderc_test.cc @@ -594,6 +594,85 @@ TEST_F(CompileStringWithOptionsTest, GenerateDebugInfoDisassembly) { 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, 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, 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 : 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, + 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 : 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, PreprocessingOnlyOption) { ASSERT_NE(nullptr, compiler_.get_compiler_handle()); const std::string kMinimalShaderWithMacro = diff --git a/libshaderc_util/CMakeLists.txt b/libshaderc_util/CMakeLists.txt index 284645b..0ea43c7 100644 --- a/libshaderc_util/CMakeLists.txt +++ b/libshaderc_util/CMakeLists.txt @@ -28,7 +28,7 @@ target_include_directories(shaderc_util find_package(Threads) target_link_libraries(shaderc_util PRIVATE glslang OSDependent OGLCompiler HLSL glslang SPIRV - SPIRV-Tools ${CMAKE_THREAD_LIBS_INIT}) + SPIRV-Tools-opt ${CMAKE_THREAD_LIBS_INIT}) shaderc_add_tests( TEST_PREFIX shaderc_util @@ -50,7 +50,9 @@ endif() shaderc_add_tests( TEST_PREFIX shaderc_util LINK_LIBS shaderc_util - INCLUDE_DIRS ${glslang_SOURCE_DIR} + INCLUDE_DIRS + ${glslang_SOURCE_DIR} + ${spirv-tools_SOURCE_DIR}/include TEST_NAMES compiler) diff --git a/libshaderc_util/include/libshaderc_util/compiler.h b/libshaderc_util/include/libshaderc_util/compiler.h index 149e2aa..8647a93 100644 --- a/libshaderc_util/include/libshaderc_util/compiler.h +++ b/libshaderc_util/include/libshaderc_util/compiler.h @@ -22,6 +22,7 @@ #include <utility> #include "glslang/Public/ShaderLang.h" +#include "libshaderc_util/spirv_tools_wrapper.h" #include "counting_includer.h" #include "file_finder.h" @@ -88,6 +89,18 @@ using MacroDictionary = std::unordered_map<std::string, std::string>; // Holds all of the state required to compile source GLSL into SPIR-V. class Compiler { public: + enum class OutputType { + SpirvBinary, // A binary module, as defined by the SPIR-V specification. + SpirvAssemblyText, // Assembly syntax defined by the SPIRV-Tools project. + PreprocessedText, // Preprocessed source code. + }; + + // Supported optimization levels. + enum class OptimizationLevel { + Zero, // No optimization. + Size, // Optimization towards reducing code size. + }; + Compiler() // The default version for glsl is 110, or 100 if you are using an es // profile. But we want to default to a non-es profile. @@ -97,12 +110,17 @@ class Compiler { warnings_as_errors_(false), suppress_warnings_(false), generate_debug_info_(false), + enabled_opt_passes_(), message_rules_(GetDefaultRules()) {} // Requests that the compiler place debug information into the object code, // such as identifier names and line numbers. void SetGenerateDebugInfo(); + // Sets the optimization level to the given level. Only the last one takes + // effect if multiple calls of this method exist. + void SetOptimizationLevel(OptimizationLevel level); + // When a warning is encountered it treat it as an error. void SetWarningsAsErrors(); @@ -126,11 +144,6 @@ class Compiler { // subsequent CompileShader() calls. void SetForcedVersionProfile(int version, EProfile profile); - enum class OutputType { - SpirvBinary, // A binary module, as defined by the SPIR-V specification. - SpirvAssemblyText, // Assembly syntax defined by the SPIRV-Tools project. - PreprocessedText, // Preprocessed source code. - }; // Compiles the shader source in the input_source_string parameter. // // If the forced_shader stage parameter is not EShLangCount then @@ -264,10 +277,12 @@ class Compiler { // output. bool generate_debug_info_; + // Optimization passes to be applied. + std::vector<PassId> enabled_opt_passes_; + // Sets the glslang EshMessages bitmask for determining which dialect of GLSL // and which SPIR-V codegen semantics are used. This impacts the warning & - // error - // messages as well as the set of available builtins + // error messages as well as the set of available builtins EShMessages message_rules_; }; diff --git a/libshaderc_util/include/libshaderc_util/spirv_tools_wrapper.h b/libshaderc_util/include/libshaderc_util/spirv_tools_wrapper.h index 83a31e1..14a597d 100644 --- a/libshaderc_util/include/libshaderc_util/spirv_tools_wrapper.h +++ b/libshaderc_util/include/libshaderc_util/spirv_tools_wrapper.h @@ -20,7 +20,7 @@ #include "libshaderc_util/string_piece.h" -#include "spirv-tools/libspirv.h" +#include "spirv-tools/libspirv.hpp" namespace shaderc_util { // Assembles the given assembly. On success, returns true, writes the assembled @@ -34,6 +34,21 @@ bool SpirvToolsAssemble(const string_piece assembly, spv_binary* binary, // *text_or_error. bool SpirvToolsDisassemble(const std::vector<uint32_t>& binary, std::string* text_or_error); + +// The ids of a list of supported optimization passes. +enum class PassId { + kNullPass, + kStripDebugInfo, + kUnifyConstant, +}; + +// Optimizes the given binary. Passes are registered in the exact order as shown +// in enabled_passes, without de-duplication. Returns true and writes the +// optimized binary back to *binary if successful. Otherwise, writes errors to +// *errors and the content of binary may be in an invalid state. +bool SpirvToolsOptimize(const std::vector<PassId>& enabled_passes, + std::vector<uint32_t>* binary, std::string* errors); + } // namespace shaderc_util #endif // LIBSHADERC_UTIL_INC_SPIRV_TOOLS_WRAPPER_H diff --git a/libshaderc_util/src/compiler.cc b/libshaderc_util/src/compiler.cc index 6ef35c2..fd6f65a 100644 --- a/libshaderc_util/src/compiler.cc +++ b/libshaderc_util/src/compiler.cc @@ -22,7 +22,6 @@ #include "libshaderc_util/message.h" #include "libshaderc_util/resources.h" #include "libshaderc_util/shader_stage.h" -#include "libshaderc_util/spirv_tools_wrapper.h" #include "libshaderc_util/string_piece.h" #include "libshaderc_util/version_profile.h" @@ -177,11 +176,21 @@ std::tuple<bool, std::vector<uint32_t>, size_t> Compiler::Compile( if (!success) return result_tuple; // 'spirv' is an alias for the compilation_output_data. This alias is added - // to - // serve as an input for the call to DissassemblyBinary. + // to serve as an input for the call to DissassemblyBinary. std::vector<uint32_t>& spirv = compilation_output_data; // Note the call to GlslangToSpv also populates compilation_output_data. glslang::GlslangToSpv(*program.getIntermediate(used_shader_stage), spirv); + + if (!enabled_opt_passes_.empty()) { + std::string opt_errors; + if (!SpirvToolsOptimize(enabled_opt_passes_, &spirv, &opt_errors)) { + *error_stream << "shaderc: internal error: compilation succeeded but " + "failed to optimize: " + << opt_errors << "\n"; + return result_tuple; + } + } + if (output_type == OutputType::SpirvAssemblyText) { std::string text_or_error; if (!SpirvToolsDisassemble(spirv, &text_or_error)) { @@ -221,7 +230,30 @@ void Compiler::SetForcedVersionProfile(int version, EProfile profile) { void Compiler::SetWarningsAsErrors() { warnings_as_errors_ = true; } -void Compiler::SetGenerateDebugInfo() { generate_debug_info_ = true; } +void Compiler::SetGenerateDebugInfo() { + generate_debug_info_ = true; + for (size_t i = 0; i < enabled_opt_passes_.size(); ++i) { + if (enabled_opt_passes_[i] == PassId::kStripDebugInfo) { + enabled_opt_passes_[i] = PassId::kNullPass; + } + } +} + +void Compiler::SetOptimizationLevel(Compiler::OptimizationLevel level) { + // Clear previous settings first. + enabled_opt_passes_.clear(); + + switch (level) { + case OptimizationLevel::Size: + if (!generate_debug_info_) { + enabled_opt_passes_.push_back(PassId::kStripDebugInfo); + } + enabled_opt_passes_.push_back(PassId::kUnifyConstant); + break; + default: + break; + } +} void Compiler::SetSuppressWarnings() { suppress_warnings_ = true; } diff --git a/libshaderc_util/src/spirv_tools_wrapper.cc b/libshaderc_util/src/spirv_tools_wrapper.cc index 96a40b0..f23c534 100644 --- a/libshaderc_util/src/spirv_tools_wrapper.cc +++ b/libshaderc_util/src/spirv_tools_wrapper.cc @@ -14,9 +14,12 @@ #include "libshaderc_util/spirv_tools_wrapper.h" +#include <algorithm> #include <cassert> #include <sstream> +#include "spirv-tools/optimizer.hpp" + namespace { // Writes the message contained in the diagnostic parameter to *dest. Assumes @@ -79,4 +82,41 @@ bool SpirvToolsAssemble(const string_piece assembly, spv_binary* binary, return result; } +bool SpirvToolsOptimize(const std::vector<PassId>& enabled_passes, + std::vector<uint32_t>* binary, std::string* errors) { + errors->clear(); + if (enabled_passes.empty()) return true; + if (std::all_of( + enabled_passes.cbegin(), enabled_passes.cend(), + [](const PassId& pass) { return pass == PassId::kNullPass; })) { + return true; + } + + spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_0); + std::ostringstream oss; + optimizer.SetMessageConsumer( + [&oss](spv_message_level_t, const char*, const spv_position_t&, + const char* message) { oss << message << "\n"; }); + + for (const auto& pass : enabled_passes) { + switch (pass) { + case PassId::kNullPass: + // We actually don't need to do anything for null pass. + break; + case PassId::kStripDebugInfo: + optimizer.RegisterPass(spvtools::CreateStripDebugInfoPass()); + break; + case PassId::kUnifyConstant: + optimizer.RegisterPass(spvtools::CreateUnifyConstantPass()); + break; + } + } + + if (!optimizer.Run(binary->data(), binary->size(), binary)) { + *errors = oss.str(); + return false; + } + return true; +} + } // namespace shaderc_util |