26#include "third_party/skia/include/core/SkString.h"
27#include "third_party/skia/include/effects/SkRuntimeEffect.h"
33constexpr const char* kEGLImageExternalExtension =
"GL_OES_EGL_image_external";
34constexpr const char* kEGLImageExternalExtension300 =
35 "GL_OES_EGL_image_external_essl3";
36constexpr int kVerboseErrorLineThreshold = 6;
40constexpr const char* kYFlipUniformName =
"_impeller_y_flip";
41constexpr const char* kUserMainName =
"_impeller_user_main";
54std::string InjectYFlipForGLESVertexShader(std::string source) {
57 constexpr std::string_view kMainPattern =
"\nvoid main(";
58 const size_t main_pos = source.find(kMainPattern);
59 if (main_pos == std::string::npos) {
62 const std::string user_main_decl =
63 std::string(
"\nvoid ") + kUserMainName +
"(";
64 source.replace(main_pos, kMainPattern.size(), user_main_decl);
66 std::string wrapper =
"\nvoid main() {\n ";
67 wrapper += kUserMainName;
68 wrapper +=
"();\n gl_Position.y *= ";
69 wrapper += kYFlipUniformName;
71 source.append(wrapper);
75 const std::string declaration =
76 std::string(
"\nuniform float ") + kYFlipUniformName +
";\n";
77 size_t inject_at = std::string::npos;
78 for (
size_t pos = source.find(
"\nprecision "); pos != std::string::npos;
79 pos = source.find(
"\nprecision ", pos + 1)) {
80 const size_t eol = source.find(
'\n', pos + 1);
81 if (eol == std::string::npos) {
86 if (inject_at == std::string::npos) {
87 const size_t version_pos = source.find(
"#version");
88 if (version_pos != std::string::npos) {
89 inject_at = source.find(
'\n', version_pos);
92 if (inject_at == std::string::npos) {
95 source.insert(inject_at, declaration);
106 std::stringstream sstream(msl_version);
107 std::string version_part;
111 if (std::getline(sstream, version_part,
'.')) {
112 major = std::stoi(version_part);
113 if (std::getline(sstream, version_part,
'.')) {
114 minor = std::stoi(version_part);
115 if (std::getline(sstream, version_part,
'.')) {
116 patch = std::stoi(version_part);
120 if (major < 1 || (major == 1 && minor < 2)) {
121 std::cerr <<
"--metal-version version must be at least 1.2. Have "
122 << msl_version << std::endl;
124 return spirv_cross::CompilerMSL::Options::make_msl_version(major, minor,
129 const spirv_cross::ParsedIR& ir,
131 std::optional<uint32_t> msl_version_override = {}) {
132 auto sl_compiler = std::make_shared<spirv_cross::CompilerMSL>(ir);
133 spirv_cross::CompilerMSL::Options sl_options;
134 sl_options.platform =
136 sl_options.msl_version = msl_version_override.value_or(
138 sl_options.ios_use_simdgroup_functions =
139 sl_options.is_ios() &&
140 sl_options.msl_version >=
141 spirv_cross::CompilerMSL::Options::make_msl_version(2, 4, 0);
142 sl_options.use_framebuffer_fetch_subpasses =
true;
143 sl_compiler->set_msl_options(sl_options);
154 SortUniforms(&ir, sl_compiler.get(), spirv_cross::SPIRType::Float);
156 SortUniforms(&ir, sl_compiler.get(), spirv_cross::SPIRType::SampledImage);
158 spv::ExecutionModel execution_model =
159 spv::ExecutionModel::ExecutionModelFragment;
161 execution_model = spv::ExecutionModel::ExecutionModelVertex;
164 uint32_t buffer_offset = 0;
165 uint32_t sampler_offset = 0;
166 for (
auto& float_id : floats) {
167 sl_compiler->add_msl_resource_binding(
168 {.stage = execution_model,
169 .basetype = spirv_cross::SPIRType::BaseType::Float,
170 .desc_set = sl_compiler->get_decoration(float_id,
171 spv::DecorationDescriptorSet),
173 sl_compiler->get_decoration(float_id, spv::DecorationBinding),
175 .msl_buffer = buffer_offset});
178 for (
auto& image_id :
images) {
179 sl_compiler->add_msl_resource_binding({
180 .stage = execution_model,
181 .basetype = spirv_cross::SPIRType::BaseType::SampledImage,
183 sl_compiler->get_decoration(image_id, spv::DecorationDescriptorSet),
185 sl_compiler->get_decoration(image_id, spv::DecorationBinding),
190 .msl_texture = sampler_offset,
191 .msl_sampler = sampler_offset,
196 return CompilerBackend(sl_compiler);
200 const spirv_cross::ParsedIR& ir,
202 auto gl_compiler = std::make_shared<spirv_cross::CompilerGLSL>(ir);
203 spirv_cross::CompilerGLSL::Options sl_options;
204 sl_options.force_zero_initialized_variables =
true;
205 sl_options.vertex.fixup_clipspace =
true;
206 sl_options.vulkan_semantics =
true;
207 gl_compiler->set_common_options(sl_options);
213 auto gl_compiler = std::make_shared<spirv_cross::CompilerGLSL>(ir);
224 for (
auto&
id : ir.ids_for_constant_or_variable) {
227 gl_compiler->require_extension(kEGLImageExternalExtension300);
229 gl_compiler->require_extension(kEGLImageExternalExtension);
235 spirv_cross::CompilerGLSL::Options sl_options;
236 sl_options.force_zero_initialized_variables =
true;
237 sl_options.vertex.fixup_clipspace =
true;
247 sl_options.version < 310) {
248 sl_options.version = 310;
251 sl_options.version < 300) {
252 sl_options.version = 300;
254 sl_options.es =
true;
257 gl_compiler->remap_ext_framebuffer_fetch(0, 0,
true);
259 gl_compiler->set_variable_type_remap_callback(
260 [&](
const spirv_cross::SPIRType&
type,
const std::string& var_name,
261 std::string& name_of_type) {
263 name_of_type =
"samplerExternalOES";
271 sl_options.version < 430) {
272 sl_options.version = 430;
274 sl_options.es =
false;
276 gl_compiler->set_common_options(sl_options);
282 auto sksl_compiler = std::make_shared<CompilerSkSL>(ir);
344uint32_t CalculateUBOSize(
const spirv_cross::Compiler* compiler) {
345 spirv_cross::ShaderResources resources = compiler->get_shader_resources();
347 for (
const spirv_cross::Resource& ubo : resources.uniform_buffers) {
348 const spirv_cross::SPIRType& ubo_type =
349 compiler->get_type(ubo.base_type_id);
350 uint32_t size = compiler->get_declared_struct_size(ubo_type);
358std::string StripUnderscoresAndUpper(std::string_view
name) {
360 result.reserve(
name.size());
361 for (
char ch :
name) {
365 if (ch >=
'a' && ch <=
'z') {
366 result.push_back(
static_cast<char>(ch -
'a' +
'A'));
368 result.push_back(ch);
381void CanonicalizeUniformBlockInstanceNamesForGL(
383 spirv_cross::Compiler* compiler) {
388 for (
const spirv_cross::Resource& ubo :
389 compiler->get_shader_resources().uniform_buffers) {
390 const std::string block_name = compiler->get_name(ubo.base_type_id);
391 const std::string instance_name = compiler->get_name(ubo.id);
392 if (StripUnderscoresAndUpper(instance_name) ==
393 StripUnderscoresAndUpper(block_name)) {
396 compiler->set_name(ubo.id,
"_" + block_name);
405 : options_(source_options) {
406 if (!source_mapping || source_mapping->GetMapping() ==
nullptr) {
408 <<
"Could not read shader source or shader source was empty.";
413 COMPILER_ERROR(error_stream_) <<
"Target platform not specified.";
428 shaderc_source_language::shaderc_source_language_glsl;
430 shaderc_profile::shaderc_profile_core,
436 shaderc_source_language::shaderc_source_language_hlsl;
449 target.
env = shaderc_target_env::shaderc_target_env_opengl;
450 target.version = shaderc_env_version::shaderc_env_version_opengl_4_5;
451 target.spirv_version = shaderc_spirv_version::shaderc_spirv_version_1_0;
453 target.env = shaderc_target_env::shaderc_target_env_vulkan;
454 target.version = shaderc_env_version::shaderc_env_version_vulkan_1_1;
455 target.spirv_version = shaderc_spirv_version::shaderc_spirv_version_1_3;
466 target.
env = shaderc_target_env::shaderc_target_env_vulkan;
467 target.version = shaderc_env_version::shaderc_env_version_vulkan_1_1;
468 target.spirv_version = shaderc_spirv_version::shaderc_spirv_version_1_3;
482 target.
env = shaderc_target_env::shaderc_target_env_opengl;
483 target.version = shaderc_env_version::shaderc_env_version_opengl_4_5;
484 target.spirv_version = shaderc_spirv_version::shaderc_spirv_version_1_0;
495 "IMPELLER_OPENGLES_UNFLIPPED_DEPRECATED");
501 target.
env = shaderc_target_env::shaderc_target_env_opengl;
502 target.version = shaderc_env_version::shaderc_env_version_opengl_4_5;
503 target.spirv_version = shaderc_spirv_version::shaderc_spirv_version_1_0;
510 shaderc_optimization_level::shaderc_optimization_level_zero;
522 for (
const auto& define : source_options.
defines) {
526 std::vector<std::string> included_file_names;
527 spirv_options.
includer = std::make_shared<Includer>(
529 [&included_file_names](
auto included_name) {
530 included_file_names.emplace_back(std::move(included_name));
539 if (!spirv_assembly_) {
542 included_file_names_ = std::move(included_file_names);
546 spirv_cross::Parser parser(
547 reinterpret_cast<const uint32_t*
>(spirv_assembly_->GetMapping()),
548 spirv_assembly_->GetSize() /
sizeof(uint32_t));
553 const auto parsed_ir =
554 std::make_shared<spirv_cross::ParsedIR>(parser.get_parsed_ir());
560 <<
"Could not create compiler for target platform.";
564 CanonicalizeUniformBlockInstanceNamesForGL(source_options.
target_platform,
565 sl_compiler.GetCompiler());
567 uint32_t ubo_size = CalculateUBOSize(sl_compiler.GetCompiler());
569 COMPILER_ERROR(error_stream_) <<
"Uniform buffer size exceeds max ("
577 auto sl_compilation_result_str = sl_compiler.GetCompiler()->compile();
583 sl_compilation_result_str =
584 InjectYFlipForGLESVertexShader(std::move(sl_compilation_result_str));
587 auto sl_compilation_result =
595 auto stripped_spirv_options = spirv_options;
598 error_stream_, stripped_spirv_options.BuildShadercOptions());
600 sl_mapping_ = sl_compilation_result;
604 COMPILER_ERROR(error_stream_) <<
"Could not generate SL from SPIRV";
609 !ValidateSkSLResult(sl_compilation_result_str).ok()) {
613 reflector_ = std::make_unique<Reflector>(std::move(reflector_options),
619 if (!reflector_->IsValid()) {
621 <<
"Could not complete reflection on generated shader.";
628absl::Status Compiler::ValidateSkSLResult(
const std::string& sksl) {
630 SkRuntimeEffect::Result result =
631 SkRuntimeEffect::MakeForShader(SkString(sksl));
633 if (result.effect !=
nullptr) {
634 return absl::OkStatus();
639 std::stringstream output;
640 bool is_truncated =
false;
643 auto append_and_truncate = [&](
const std::string&
text) {
644 std::stringstream text_stream(
text);
646 int lines_outputted = 0;
647 while (std::getline(text_stream, line)) {
648 output <<
"\n " << line;
649 if (++lines_outputted == kVerboseErrorLineThreshold) {
653 if (lines_outputted == kVerboseErrorLineThreshold) {
654 auto full_line_count = std::count(
text.begin(),
text.end(),
'\n') + 1;
655 output <<
"\n... (truncated " << full_line_count - lines_outputted
661 output <<
"\nCompiled to invalid SkSL:";
662 append_and_truncate(sksl);
663 output <<
"\nSkSL Error:";
664 std::string error_text(result.errorText.c_str());
665 append_and_truncate(error_text);
673 COMPILER_ERROR(verbose_error_stream_) <<
"\nCompiled to invalid SkSL:\n"
674 << sksl <<
"\nSkSL Error:\n"
678 return absl::InternalError(
"SkSL validation failed.");
684 return spirv_assembly_;
695std::string Compiler::GetSourcePrefix()
const {
696 std::stringstream stream;
702 return error_stream_.str();
706 return verbose_error_stream_.str();
710 return included_file_names_;
714 const std::string& separator) {
715 std::stringstream stream;
716 for (
size_t i = 0, count = items.size();
i < count;
i++) {
717 const auto is_last = (
i == count - 1);
727std::string Compiler::GetDependencyNames(
const std::string& separator)
const {
728 std::vector<std::string> dependencies = included_file_names_;
734 std::initializer_list<std::string> targets_names)
const {
736 const auto targets =
JoinStrings(targets_names,
" ");
737 const auto dependencies = GetDependencyNames(
" ");
739 std::stringstream stream;
740 stream << targets <<
": " << dependencies <<
"\n";
742 auto contents = std::make_shared<std::string>(stream.str());
743 return std::make_unique<fml::NonOwnedMapping>(
744 reinterpret_cast<const uint8_t*
>(contents->data()), contents->size(),
745 [contents](
auto,
auto) {});
749 return reflector_.get();
std::string GetVerboseErrorMessages() const
std::shared_ptr< fml::Mapping > GetSPIRVAssembly() const
const Reflector * GetReflector() const
Compiler(const std::shared_ptr< const fml::Mapping > &source_mapping, const SourceOptions &options, Reflector::Options reflector_options)
const std::vector< std::string > & GetIncludedFileNames() const
std::unique_ptr< fml::Mapping > CreateDepfileContents(std::initializer_list< std::string > targets) const
std::shared_ptr< fml::Mapping > GetSLShaderSource() const
std::string GetErrorMessages() const
std::shared_ptr< fml::Mapping > CompileToSPV(std::stringstream &error_stream, const shaderc::CompileOptions &spirv_options) const
#define FML_UNREACHABLE()
#define COMPILER_ERROR(stream)
std::array< MockImage, 3 > images
static CompilerBackend CreateSkSLCompiler(const spirv_cross::ParsedIR &ir, const SourceOptions &source_options)
static const uint32_t kMaxUniformBufferSize
static CompilerBackend CreateVulkanCompiler(const spirv_cross::ParsedIR &ir, const SourceOptions &source_options)
constexpr char kExternalTexturePrefix[]
static bool EntryPointMustBeNamedMain(TargetPlatform platform)
static CompilerBackend CreateMSLCompiler(const spirv_cross::ParsedIR &ir, const SourceOptions &source_options, std::optional< uint32_t > msl_version_override={})
static CompilerBackend CreateCompiler(const spirv_cross::ParsedIR &ir, const SourceOptions &source_options)
std::string Utf8FromPath(const std::filesystem::path &path)
Converts a native format path to a utf8 string.
static std::string JoinStrings(std::vector< std::string > items, const std::string &separator)
spirv_cross::CompilerMSL::Options::Platform TargetPlatformToMSLPlatform(TargetPlatform platform)
static uint32_t ParseMSLVersion(const std::string &msl_version)
bool StringStartsWith(const std::string &target, const std::string &prefix)
static CompilerBackend CreateGLSLCompiler(const spirv_cross::ParsedIR &ir, const SourceOptions &source_options)
spv::ExecutionModel ToExecutionModel(SourceType type)
std::vector< spirv_cross::ID > SortUniforms(const spirv_cross::ParsedIR *ir, const spirv_cross::Compiler *compiler, std::optional< spirv_cross::SPIRType::BaseType > type_filter, bool include)
Sorts uniform declarations in an IR according to decoration order.
std::shared_ptr< fml::Mapping > CreateMappingWithString(std::string string)
Creates a mapping with string data.
impeller::ShaderType type
spirv_cross::Compiler * GetCompiler()
std::optional< shaderc_source_language > source_langauge
std::vector< std::string > macro_definitions
bool relaxed_vulkan_rules
shaderc_optimization_level optimization_level
std::optional< SPIRVCompilerSourceProfile > source_profile
std::shared_ptr< Includer > includer
shaderc::CompileOptions BuildShadercOptions() const
std::optional< SPIRVCompilerTargetEnv > target
uint32_t gles_language_version
bool use_half_textures
Whether half-precision textures should be supported, requiring opengl semantics. Only used on metal t...
SourceLanguage source_language
std::vector< IncludeDir > include_dirs
std::filesystem::path file_name
bool require_framebuffer_fetch
Whether the GLSL framebuffer fetch extension will be required.
std::string entry_point_name
TargetPlatform target_platform
std::shared_ptr< fml::UniqueFD > working_directory
std::string metal_version
std::vector< std::string > defines