blob: 30a7025568c77c3e94e521e37d8452b7bfec890e [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "lib/escher/impl/glsl_compiler.h"
#include <string>
#include <thread>
#include "SPIRV/GlslangToSpv.h"
#include "StandAlone/ResourceLimits.h"
#include "glslang/Public/ShaderLang.h"
#include "spirv-tools/libspirv.hpp"
#include "spirv-tools/optimizer.hpp"
#include "lib/escher/util/trace_macros.h"
#include "lib/fxl/logging.h"
namespace escher {
namespace impl {
GlslToSpirvCompiler::GlslToSpirvCompiler() : active_compile_count_(0) {}
GlslToSpirvCompiler::~GlslToSpirvCompiler() {
FXL_CHECK(active_compile_count_ == 0);
}
std::future<SpirvData> GlslToSpirvCompiler::Compile(
vk::ShaderStageFlagBits stage, std::vector<std::string> source_code,
std::string preamble, std::string entry_point) {
// Count will be decremented by SynchronousCompile.
++active_compile_count_;
#if !defined(ESCHER_DISABLE_BACKGROUND_COMPILATION)
return std::async(
std::launch::async, &GlslToSpirvCompiler::SynchronousCompile, this, stage,
std::move(source_code), std::move(preamble), std::move(entry_point));
#else
std::promise<SpirvData> p;
p.set_value(SynchronousCompile(stage, std::move(source_code),
std::move(preamble), std::move(entry_point)));
return p.get_future();
#endif
}
SpirvData GlslToSpirvCompiler::SynchronousCompile(
vk::ShaderStageFlagBits stage, std::vector<std::string> source_code,
std::string preamble, std::string entry_point) {
TRACE_DURATION("gfx", "escher::GlslToSpirvCompiler::SynchronousCompile");
// SynchronousCompileImpl has many return points; wrap it so that we don't
// forget to --active_compile_count_ at one of them.
auto result =
SynchronousCompileImpl(stage, std::move(source_code), std::move(preamble),
std::move(entry_point));
// Count was already incremented by Compile().
--active_compile_count_;
return result;
}
// SynchronousCompileImpl has many return points; wrap it so that we don't
// forget to --active_compile_count_ at one of them.
SpirvData GlslToSpirvCompiler::SynchronousCompileImpl(
vk::ShaderStageFlagBits stage_in, std::vector<std::string> source_code,
std::string preamble, std::string entry_point) {
EShLanguage stage;
switch (stage_in) {
case vk::ShaderStageFlagBits::eVertex:
stage = EShLangVertex;
break;
case vk::ShaderStageFlagBits::eTessellationControl:
stage = EShLangTessControl;
break;
case vk::ShaderStageFlagBits::eTessellationEvaluation:
stage = EShLangTessEvaluation;
break;
case vk::ShaderStageFlagBits::eGeometry:
stage = EShLangGeometry;
break;
case vk::ShaderStageFlagBits::eFragment:
stage = EShLangFragment;
break;
case vk::ShaderStageFlagBits::eCompute:
stage = EShLangCompute;
break;
default:
FXL_LOG(WARNING) << "invalid shader stage";
return SpirvData();
}
glslang::TShader shader(stage);
std::vector<const char*> source_chars;
std::vector<int> source_lengths;
for (auto& s : source_code) {
source_chars.push_back(s.c_str());
source_lengths.push_back(s.length());
}
shader.setStringsWithLengths(source_chars.data(), source_lengths.data(),
source_code.size());
if (!preamble.empty()) {
shader.setPreamble(preamble.c_str());
}
if (!entry_point.empty()) {
shader.setEntryPoint(entry_point.c_str());
}
constexpr int kDefaultGlslVersion = 450;
constexpr EShMessages kMessageFlags =
static_cast<EShMessages>(EShMsgVulkanRules | EShMsgSpvRules);
if (!shader.parse(&glslang::DefaultTBuiltInResource, kDefaultGlslVersion,
false, kMessageFlags)) {
FXL_LOG(WARNING) << "failed to parse shader \n\tinfo log: "
<< shader.getInfoLog()
<< "\n\tdebug log: " << shader.getInfoDebugLog();
return SpirvData();
}
glslang::TProgram program;
program.addShader(&shader);
if (!program.link(kMessageFlags)) {
FXL_LOG(WARNING) << "failed to link program \n\tinfo log: "
<< program.getInfoLog()
<< "\n\tdebug log: " << program.getInfoDebugLog();
return SpirvData();
}
glslang::TIntermediate* intermediate = program.getIntermediate(stage);
if (!intermediate) {
FXL_LOG(WARNING) << "failed to obtain program intermediate representation"
<< "\n\tinfo log : " << program.getInfoLog()
<< "\n\tdebug log: " << program.getInfoDebugLog();
return SpirvData();
}
SpirvData spirv;
std::string warningsErrors;
spv::SpvBuildLogger logger;
glslang::GlslangToSpv(*intermediate, spirv, &logger);
if (spirv.empty()) {
FXL_LOG(WARNING) << "failed to generate SPIR-V from GLSL IR: "
<< logger.getAllMessages();
}
// TODO(ES-24): Find a central place for all code that makes reference to a
// particular version of Vulkan.
const char* kVulkanMinorVersion = "42";
spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_0);
optimizer.RegisterPass(spvtools::CreateSetSpecConstantDefaultValuePass(
{{1, kVulkanMinorVersion}}));
optimizer.RegisterPass(spvtools::CreateFreezeSpecConstantValuePass());
optimizer.RegisterPass(spvtools::CreateFoldSpecConstantOpAndCompositePass());
optimizer.RegisterPass(spvtools::CreateEliminateDeadConstantPass());
optimizer.RegisterPass(spvtools::CreateUnifyConstantPass());
auto optimizer_error_logger = [](spv_message_level_t, const char*,
const spv_position_t&, const char* m) {
FXL_LOG(WARNING) << "Error while running SPIR-V optimizer: " << m;
};
optimizer.SetMessageConsumer(optimizer_error_logger);
if (!optimizer.Run(spirv.data(), spirv.size(), &spirv)) {
FXL_LOG(WARNING) << "failed to optimize SPIR-V";
return SpirvData();
}
return spirv;
}
} // namespace impl
} // namespace escher