aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLei Zhang <antiagainst@google.com>2016-09-19 14:58:00 -0400
committerLei Zhang <antiagainst@google.com>2016-09-23 14:26:26 -0400
commit72d6deecad661cfa34d479b3ca1843456d75c893 (patch)
treebe384eb697a0bab3690074a370be736d750cba79
parent29a28289169c82adea647387ba8c2162d6aa8d7f (diff)
downloadshaderc-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.h11
-rw-r--r--libshaderc/include/shaderc/shaderc.hpp6
-rw-r--r--libshaderc/src/shaderc.cc14
-rw-r--r--libshaderc/src/shaderc_cpp_test.cc66
-rw-r--r--libshaderc/src/shaderc_test.cc79
-rw-r--r--libshaderc_util/CMakeLists.txt6
-rw-r--r--libshaderc_util/include/libshaderc_util/compiler.h29
-rw-r--r--libshaderc_util/include/libshaderc_util/spirv_tools_wrapper.h17
-rw-r--r--libshaderc_util/src/compiler.cc40
-rw-r--r--libshaderc_util/src/spirv_tools_wrapper.cc40
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