diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 268a248e7585b..977fe8df74e95 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -5016,6 +5016,8 @@ ORIGIN: ../../../flutter/impeller/compiler/runtime_stage_data.cc + ../../../flut ORIGIN: ../../../flutter/impeller/compiler/runtime_stage_data.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/compiler/shader_bundle.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/compiler/shader_bundle.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/compiler/shader_bundle_data.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/compiler/shader_bundle_data.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/compiler/shader_lib/flutter/runtime_effect.glsl + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/blending.glsl + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/compiler/shader_lib/impeller/branching.glsl + ../../../flutter/LICENSE @@ -7826,6 +7828,8 @@ FILE: ../../../flutter/impeller/compiler/runtime_stage_data.cc FILE: ../../../flutter/impeller/compiler/runtime_stage_data.h FILE: ../../../flutter/impeller/compiler/shader_bundle.cc FILE: ../../../flutter/impeller/compiler/shader_bundle.h +FILE: ../../../flutter/impeller/compiler/shader_bundle_data.cc +FILE: ../../../flutter/impeller/compiler/shader_bundle_data.h FILE: ../../../flutter/impeller/compiler/shader_lib/flutter/runtime_effect.glsl FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/blending.glsl FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/branching.glsl diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index 3f508fe605f1a..34cafa6925b07 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -48,6 +48,8 @@ impeller_component("compiler_lib") { "runtime_stage_data.h", "shader_bundle.cc", "shader_bundle.h", + "shader_bundle_data.cc", + "shader_bundle_data.h", "source_options.cc", "source_options.h", "spirv_compiler.cc", diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index ca3ecd163e914..49a20d4b51c42 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -132,6 +132,11 @@ Reflector::Reflector(Options options, return; } + shader_bundle_data_ = GenerateShaderBundleData(); + if (!shader_bundle_data_) { + return; + } + is_valid_ = true; } @@ -166,6 +171,10 @@ std::shared_ptr Reflector::GetRuntimeStageData() const { return runtime_stage_data_; } +std::shared_ptr Reflector::GetShaderBundleData() const { + return shader_bundle_data_; +} + std::optional Reflector::GenerateTemplateArguments() const { nlohmann::json root; @@ -380,6 +389,69 @@ std::shared_ptr Reflector::GenerateRuntimeStageData() const { return data; } +std::shared_ptr Reflector::GenerateShaderBundleData() const { + const auto& entrypoints = compiler_->get_entry_points_and_stages(); + if (entrypoints.size() != 1u) { + VALIDATION_LOG << "Single entrypoint not found."; + return nullptr; + } + auto data = std::make_shared( + options_.entry_point_name, // + entrypoints.front().execution_model, // + options_.target_platform // + ); + data->SetShaderData(shader_data_); + + // Sort the IR so that the uniforms are in declaration order. + std::vector uniforms = + SortUniforms(ir_.get(), compiler_.GetCompiler()); + + for (auto& sorted_id : uniforms) { + auto var = ir_->ids[sorted_id].get(); + const auto spir_type = compiler_->get_type(var.basetype); + UniformDescription uniform_description; + uniform_description.name = compiler_->get_name(var.self); + uniform_description.location = compiler_->get_decoration( + var.self, spv::Decoration::DecorationLocation); + uniform_description.type = spir_type.basetype; + uniform_description.rows = spir_type.vecsize; + uniform_description.columns = spir_type.columns; + uniform_description.bit_width = spir_type.width; + uniform_description.array_elements = GetArrayElements(spir_type); + data->AddUniformDescription(std::move(uniform_description)); + } + + // We only need to worry about storing vertex attributes. + if (entrypoints.front().execution_model == spv::ExecutionModelVertex) { + const auto inputs = compiler_->get_shader_resources().stage_inputs; + auto input_offsets = ComputeOffsets(inputs); + for (const auto& input : inputs) { + auto location = compiler_->get_decoration( + input.id, spv::Decoration::DecorationLocation); + std::optional offset = input_offsets[location]; + + const auto type = compiler_->get_type(input.type_id); + + InputDescription input_description; + input_description.name = input.name; + input_description.location = compiler_->get_decoration( + input.id, spv::Decoration::DecorationLocation); + input_description.set = compiler_->get_decoration( + input.id, spv::Decoration::DecorationDescriptorSet); + input_description.binding = compiler_->get_decoration( + input.id, spv::Decoration::DecorationBinding); + input_description.type = type.basetype; + input_description.bit_width = type.width; + input_description.vec_size = type.vecsize; + input_description.columns = type.columns; + input_description.offset = offset.value_or(0u); + data->AddInputDescription(std::move(input_description)); + } + } + + return data; +} + std::optional Reflector::GetArrayElements( const spirv_cross::SPIRType& type) const { if (type.array.empty()) { diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index d55a1420efd71..e965b41633266 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -13,6 +13,7 @@ #include "flutter/fml/mapping.h" #include "impeller/compiler/compiler_backend.h" #include "impeller/compiler/runtime_stage_data.h" +#include "impeller/compiler/shader_bundle_data.h" #include "inja/inja.hpp" #include "spirv_msl.hpp" #include "spirv_parser.hpp" @@ -74,6 +75,8 @@ class Reflector { std::shared_ptr GetRuntimeStageData() const; + std::shared_ptr GetShaderBundleData() const; + private: struct StructDefinition { std::string name; @@ -101,6 +104,7 @@ class Reflector { std::shared_ptr reflection_header_; std::shared_ptr reflection_cc_; std::shared_ptr runtime_stage_data_; + std::shared_ptr shader_bundle_data_; bool is_valid_ = false; std::optional GenerateTemplateArguments() const; @@ -111,6 +115,8 @@ class Reflector { std::shared_ptr GenerateRuntimeStageData() const; + std::shared_ptr GenerateShaderBundleData() const; + std::shared_ptr InflateTemplate(std::string_view tmpl) const; std::optional ReflectResource( diff --git a/impeller/compiler/runtime_stage_data.h b/impeller/compiler/runtime_stage_data.h index 8d0cbc5bf9679..7a98926fd14a4 100644 --- a/impeller/compiler/runtime_stage_data.h +++ b/impeller/compiler/runtime_stage_data.h @@ -17,29 +17,6 @@ namespace impeller { namespace compiler { -struct UniformDescription { - std::string name; - size_t location = 0u; - spirv_cross::SPIRType::BaseType type = spirv_cross::SPIRType::BaseType::Float; - size_t rows = 0u; - size_t columns = 0u; - size_t bit_width = 0u; - std::optional array_elements = std::nullopt; -}; - -struct InputDescription { - std::string name; - size_t location; - size_t set; - size_t binding; - spirv_cross::SPIRType::BaseType type = - spirv_cross::SPIRType::BaseType::Unknown; - size_t bit_width; - size_t vec_size; - size_t columns; - size_t offset; -}; - class RuntimeStageData { public: RuntimeStageData(std::string entrypoint, diff --git a/impeller/compiler/shader_bundle.cc b/impeller/compiler/shader_bundle.cc index d71d3bfa3c3a1..3abebdbdbd3b4 100644 --- a/impeller/compiler/shader_bundle.cc +++ b/impeller/compiler/shader_bundle.cc @@ -80,12 +80,12 @@ std::optional ParseShaderBundleConfig( return bundle; } -static std::unique_ptr GenerateShaderFB( +static std::unique_ptr GenerateShaderBackendFB( + TargetPlatform target_platform, SourceOptions& options, const std::string& shader_name, const ShaderConfig& shader_config) { - auto result = std::make_unique(); - result->name = shader_name; + auto result = std::make_unique(); std::shared_ptr source_file_mapping = fml::FileMapping::CreateReadOnly(shader_config.source_file_name); @@ -96,6 +96,8 @@ static std::unique_ptr GenerateShaderFB( } /// Override options. + options.target_platform = target_platform; + options.file_name = shader_name; // This is just used for error messages. options.type = shader_config.type; options.source_language = shader_config.language; options.entry_point_name = EntryPointFunctionNameFromSourceName( @@ -122,15 +124,15 @@ static std::unique_ptr GenerateShaderFB( return nullptr; } - auto stage_data = reflector->GetRuntimeStageData(); - if (!stage_data) { - std::cerr << "Runtime stage information was nil for bundled shader \"" - << shader_name << "\"." << std::endl; + auto bundle_data = reflector->GetShaderBundleData(); + if (!bundle_data) { + std::cerr << "Bundled shader information was nil for \"" << shader_name + << "\"." << std::endl; return nullptr; } - result->shader = stage_data->CreateFlatbuffer(); - if (!result->shader) { + result = bundle_data->CreateFlatbuffer(); + if (!result) { std::cerr << "Failed to create flatbuffer for bundled shader \"" << shader_name << "\"." << std::endl; return nullptr; @@ -139,6 +141,29 @@ static std::unique_ptr GenerateShaderFB( return result; } +static std::unique_ptr GenerateShaderFB( + SourceOptions& options, + const std::string& shader_name, + const ShaderConfig& shader_config) { + auto result = std::make_unique(); + result->name = shader_name; + result->metal_ios = GenerateShaderBackendFB( + TargetPlatform::kMetalIOS, options, shader_name, shader_config); + result->metal_desktop = GenerateShaderBackendFB( + TargetPlatform::kMetalDesktop, options, shader_name, shader_config); + result->opengl_es = GenerateShaderBackendFB( + TargetPlatform::kOpenGLES, options, shader_name, shader_config); + result->opengl_desktop = GenerateShaderBackendFB( + TargetPlatform::kOpenGLDesktop, options, shader_name, shader_config); + result->vulkan = GenerateShaderBackendFB(TargetPlatform::kVulkan, options, + shader_name, shader_config); + if (!(result->metal_ios && result->metal_desktop && result->opengl_es && + result->opengl_desktop && result->vulkan)) { + return nullptr; + } + return result; +} + std::optional GenerateShaderBundleFlatbuffer( const std::string& bundle_config_json, SourceOptions& options) { diff --git a/impeller/compiler/shader_bundle_data.cc b/impeller/compiler/shader_bundle_data.cc new file mode 100644 index 0000000000000..fa36511c522a9 --- /dev/null +++ b/impeller/compiler/shader_bundle_data.cc @@ -0,0 +1,224 @@ +// Copyright 2013 The Flutter 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 "impeller/compiler/shader_bundle_data.h" + +#include +#include +#include + +#include "inja/inja.hpp" + +#include "impeller/base/validation.h" +#include "impeller/shader_bundle/shader_bundle_flatbuffers.h" +#include "runtime_stage_types_flatbuffers.h" + +namespace impeller { +namespace compiler { + +ShaderBundleData::ShaderBundleData(std::string entrypoint, + spv::ExecutionModel stage, + TargetPlatform target_platform) + : entrypoint_(std::move(entrypoint)), + stage_(stage), + target_platform_(target_platform) {} + +ShaderBundleData::~ShaderBundleData() = default; + +void ShaderBundleData::AddUniformDescription(UniformDescription uniform) { + uniforms_.emplace_back(std::move(uniform)); +} + +void ShaderBundleData::AddInputDescription(InputDescription input) { + inputs_.emplace_back(std::move(input)); +} + +void ShaderBundleData::SetShaderData(std::shared_ptr shader) { + shader_ = std::move(shader); +} + +static std::optional ToStage(spv::ExecutionModel stage) { + switch (stage) { + case spv::ExecutionModel::ExecutionModelVertex: + return fb::Stage::kVertex; + case spv::ExecutionModel::ExecutionModelFragment: + return fb::Stage::kFragment; + case spv::ExecutionModel::ExecutionModelGLCompute: + return fb::Stage::kCompute; + default: + return std::nullopt; + } + FML_UNREACHABLE(); +} + +static std::optional ToUniformType( + spirv_cross::SPIRType::BaseType type) { + switch (type) { + case spirv_cross::SPIRType::Boolean: + return fb::UniformDataType::kBoolean; + case spirv_cross::SPIRType::SByte: + return fb::UniformDataType::kSignedByte; + case spirv_cross::SPIRType::UByte: + return fb::UniformDataType::kUnsignedByte; + case spirv_cross::SPIRType::Short: + return fb::UniformDataType::kSignedShort; + case spirv_cross::SPIRType::UShort: + return fb::UniformDataType::kUnsignedShort; + case spirv_cross::SPIRType::Int: + return fb::UniformDataType::kSignedInt; + case spirv_cross::SPIRType::UInt: + return fb::UniformDataType::kUnsignedInt; + case spirv_cross::SPIRType::Int64: + return fb::UniformDataType::kSignedInt64; + case spirv_cross::SPIRType::UInt64: + return fb::UniformDataType::kUnsignedInt64; + case spirv_cross::SPIRType::Half: + return fb::UniformDataType::kHalfFloat; + case spirv_cross::SPIRType::Float: + return fb::UniformDataType::kFloat; + case spirv_cross::SPIRType::Double: + return fb::UniformDataType::kDouble; + case spirv_cross::SPIRType::SampledImage: + return fb::UniformDataType::kSampledImage; + case spirv_cross::SPIRType::AccelerationStructure: + case spirv_cross::SPIRType::AtomicCounter: + case spirv_cross::SPIRType::Char: + case spirv_cross::SPIRType::ControlPointArray: + case spirv_cross::SPIRType::Image: + case spirv_cross::SPIRType::Interpolant: + case spirv_cross::SPIRType::RayQuery: + case spirv_cross::SPIRType::Sampler: + case spirv_cross::SPIRType::Struct: + case spirv_cross::SPIRType::Unknown: + case spirv_cross::SPIRType::Void: + return std::nullopt; + } + FML_UNREACHABLE(); +} +static std::optional ToInputType( + spirv_cross::SPIRType::BaseType type) { + switch (type) { + case spirv_cross::SPIRType::Boolean: + return fb::InputDataType::kBoolean; + case spirv_cross::SPIRType::SByte: + return fb::InputDataType::kSignedByte; + case spirv_cross::SPIRType::UByte: + return fb::InputDataType::kUnsignedByte; + case spirv_cross::SPIRType::Short: + return fb::InputDataType::kSignedShort; + case spirv_cross::SPIRType::UShort: + return fb::InputDataType::kUnsignedShort; + case spirv_cross::SPIRType::Int: + return fb::InputDataType::kSignedInt; + case spirv_cross::SPIRType::UInt: + return fb::InputDataType::kUnsignedInt; + case spirv_cross::SPIRType::Int64: + return fb::InputDataType::kSignedInt64; + case spirv_cross::SPIRType::UInt64: + return fb::InputDataType::kUnsignedInt64; + case spirv_cross::SPIRType::Float: + return fb::InputDataType::kFloat; + case spirv_cross::SPIRType::Double: + return fb::InputDataType::kDouble; + case spirv_cross::SPIRType::Unknown: + case spirv_cross::SPIRType::Void: + case spirv_cross::SPIRType::Half: + case spirv_cross::SPIRType::AtomicCounter: + case spirv_cross::SPIRType::Struct: + case spirv_cross::SPIRType::Image: + case spirv_cross::SPIRType::SampledImage: + case spirv_cross::SPIRType::Sampler: + case spirv_cross::SPIRType::AccelerationStructure: + case spirv_cross::SPIRType::RayQuery: + case spirv_cross::SPIRType::ControlPointArray: + case spirv_cross::SPIRType::Interpolant: + case spirv_cross::SPIRType::Char: + return std::nullopt; + } + FML_UNREACHABLE(); +} + +std::unique_ptr ShaderBundleData::CreateFlatbuffer() const { + auto shader_bundle = std::make_unique(); + + // The high level object API is used here for writing to the buffer. This is + // just a convenience. + shader_bundle->entrypoint = entrypoint_; + const auto stage = ToStage(stage_); + if (!stage.has_value()) { + VALIDATION_LOG << "Invalid shader bundle."; + return nullptr; + } + shader_bundle->stage = stage.value(); + // This field is ignored, so just set it to anything. + shader_bundle->target_platform = fb::TargetPlatform::kMetal; + if (!shader_) { + VALIDATION_LOG << "No shader specified for shader bundle."; + return nullptr; + } + if (shader_->GetSize() > 0u) { + shader_bundle->shader = {shader_->GetMapping(), + shader_->GetMapping() + shader_->GetSize()}; + } + // It is not an error for the SkSL to be ommitted. + if (sksl_ && sksl_->GetSize() > 0u) { + shader_bundle->sksl = {sksl_->GetMapping(), + sksl_->GetMapping() + sksl_->GetSize()}; + } + for (const auto& uniform : uniforms_) { + auto desc = std::make_unique(); + + desc->name = uniform.name; + if (desc->name.empty()) { + VALIDATION_LOG << "Uniform name cannot be empty."; + return nullptr; + } + desc->location = uniform.location; + desc->rows = uniform.rows; + desc->columns = uniform.columns; + auto uniform_type = ToUniformType(uniform.type); + if (!uniform_type.has_value()) { + VALIDATION_LOG << "Invalid uniform type for runtime stage."; + return nullptr; + } + desc->type = uniform_type.value(); + desc->bit_width = uniform.bit_width; + if (uniform.array_elements.has_value()) { + desc->array_elements = uniform.array_elements.value(); + } + + shader_bundle->uniforms.emplace_back(std::move(desc)); + } + + for (const auto& input : inputs_) { + auto desc = std::make_unique(); + + desc->name = input.name; + + if (desc->name.empty()) { + VALIDATION_LOG << "Stage input name cannot be empty."; + return nullptr; + } + desc->location = input.location; + desc->set = input.set; + desc->binding = input.binding; + auto input_type = ToInputType(input.type); + if (!input_type.has_value()) { + VALIDATION_LOG << "Invalid uniform type for runtime stage."; + return nullptr; + } + desc->type = input_type.value(); + desc->bit_width = input.bit_width; + desc->vec_size = input.vec_size; + desc->columns = input.columns; + desc->offset = input.offset; + + shader_bundle->inputs.emplace_back(std::move(desc)); + } + + return shader_bundle; +} + +} // namespace compiler +} // namespace impeller diff --git a/impeller/compiler/shader_bundle_data.h b/impeller/compiler/shader_bundle_data.h new file mode 100644 index 0000000000000..ca9a548f3ccd7 --- /dev/null +++ b/impeller/compiler/shader_bundle_data.h @@ -0,0 +1,53 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_IMPELLER_COMPILER_SHADER_BUNDLE_DATA_H_ +#define FLUTTER_IMPELLER_COMPILER_SHADER_BUNDLE_DATA_H_ + +#include +#include + +#include "flutter/fml/mapping.h" +#include "impeller/compiler/types.h" +#include "runtime_stage_types_flatbuffers.h" + +namespace impeller { +namespace compiler { + +class ShaderBundleData { + public: + ShaderBundleData(std::string entrypoint, + spv::ExecutionModel stage, + TargetPlatform target_platform); + + ~ShaderBundleData(); + + void AddUniformDescription(UniformDescription uniform); + + void AddInputDescription(InputDescription input); + + void SetShaderData(std::shared_ptr shader); + + void SetSkSLData(std::shared_ptr sksl); + + std::unique_ptr CreateFlatbuffer() const; + + private: + const std::string entrypoint_; + const spv::ExecutionModel stage_; + const TargetPlatform target_platform_; + std::vector uniforms_; + std::vector inputs_; + std::shared_ptr shader_; + std::shared_ptr sksl_; + + ShaderBundleData(const ShaderBundleData&) = delete; + + ShaderBundleData& operator=(const ShaderBundleData&) = delete; +}; + +} // namespace compiler +} // namespace impeller + +#endif // FLUTTER_IMPELLER_COMPILER_SHADER_BUNDLE_DATA_H_ diff --git a/impeller/compiler/shader_bundle_unittests.cc b/impeller/compiler/shader_bundle_unittests.cc index 375f366bf64a4..e21f235db7dfa 100644 --- a/impeller/compiler/shader_bundle_unittests.cc +++ b/impeller/compiler/shader_bundle_unittests.cc @@ -156,14 +156,14 @@ TEST(ShaderBundleTest, GenerateShaderBundleFlatbufferProducesCorrectResult) { /// Verify vertex shader. /// - EXPECT_STREQ(vertex->shader->entrypoint.c_str(), + EXPECT_STREQ(vertex->metal_desktop->entrypoint.c_str(), "flutter_gpu_unlit_vertex_main"); - EXPECT_EQ(vertex->shader->stage, fb::Stage::kVertex); - EXPECT_EQ(vertex->shader->target_platform, fb::TargetPlatform::kMetal); + EXPECT_EQ(vertex->metal_desktop->stage, fb::Stage::kVertex); + EXPECT_EQ(vertex->metal_desktop->target_platform, fb::TargetPlatform::kMetal); // Inputs. - ASSERT_EQ(vertex->shader->inputs.size(), 1u); - const auto& v_in_position = vertex->shader->inputs[0]; + ASSERT_EQ(vertex->metal_desktop->inputs.size(), 1u); + const auto& v_in_position = vertex->metal_desktop->inputs[0]; EXPECT_STREQ(v_in_position->name.c_str(), "position"); EXPECT_EQ(v_in_position->location, 0u); EXPECT_EQ(v_in_position->set, 0u); @@ -175,8 +175,8 @@ TEST(ShaderBundleTest, GenerateShaderBundleFlatbufferProducesCorrectResult) { EXPECT_EQ(v_in_position->offset, 0u); // Uniforms. - ASSERT_EQ(vertex->shader->uniforms.size(), 2u); - const auto* v_mvp = FindByName(vertex->shader->uniforms, "mvp"); + ASSERT_EQ(vertex->metal_desktop->uniforms.size(), 2u); + const auto* v_mvp = FindByName(vertex->metal_desktop->uniforms, "mvp"); ASSERT_NE(v_mvp, nullptr); EXPECT_EQ(v_mvp->location, 0u); EXPECT_EQ(v_mvp->type, fb::UniformDataType::kFloat); @@ -184,7 +184,7 @@ TEST(ShaderBundleTest, GenerateShaderBundleFlatbufferProducesCorrectResult) { EXPECT_EQ(v_mvp->rows, 4u); EXPECT_EQ(v_mvp->columns, 4u); EXPECT_EQ(v_mvp->array_elements, 0u); - const auto* v_color = FindByName(vertex->shader->uniforms, "color"); + const auto* v_color = FindByName(vertex->metal_desktop->uniforms, "color"); ASSERT_NE(v_color, nullptr); EXPECT_EQ(v_color->location, 1u); EXPECT_EQ(v_color->type, fb::UniformDataType::kFloat); @@ -197,16 +197,17 @@ TEST(ShaderBundleTest, GenerateShaderBundleFlatbufferProducesCorrectResult) { /// Verify fragment shader. /// - EXPECT_STREQ(fragment->shader->entrypoint.c_str(), + EXPECT_STREQ(fragment->metal_desktop->entrypoint.c_str(), "flutter_gpu_unlit_fragment_main"); - EXPECT_EQ(fragment->shader->stage, fb::Stage::kFragment); - EXPECT_EQ(fragment->shader->target_platform, fb::TargetPlatform::kMetal); + EXPECT_EQ(fragment->metal_desktop->stage, fb::Stage::kFragment); + EXPECT_EQ(fragment->metal_desktop->target_platform, + fb::TargetPlatform::kMetal); // Inputs (not recorded for fragment shaders). - ASSERT_EQ(fragment->shader->inputs.size(), 0u); + ASSERT_EQ(fragment->metal_desktop->inputs.size(), 0u); // Uniforms. - ASSERT_EQ(fragment->shader->inputs.size(), 0u); + ASSERT_EQ(fragment->metal_desktop->inputs.size(), 0u); } } // namespace testing diff --git a/impeller/compiler/switches.cc b/impeller/compiler/switches.cc index 46a65c246f294..0773f32283ccb 100644 --- a/impeller/compiler/switches.cc +++ b/impeller/compiler/switches.cc @@ -200,7 +200,7 @@ bool Switches::AreValid(std::ostream& explain) const { const bool shader_bundle_mode = !shader_bundle.empty(); bool valid = true; - if (target_platform == TargetPlatform::kUnknown) { + if (target_platform == TargetPlatform::kUnknown && !shader_bundle_mode) { explain << "The target platform (only one) was not specified." << std::endl; valid = false; } diff --git a/impeller/compiler/types.h b/impeller/compiler/types.h index c3fc0923ce8c3..2d0c7419066d8 100644 --- a/impeller/compiler/types.h +++ b/impeller/compiler/types.h @@ -44,6 +44,29 @@ enum class SourceLanguage { kHLSL, }; +struct UniformDescription { + std::string name; + size_t location = 0u; + spirv_cross::SPIRType::BaseType type = spirv_cross::SPIRType::BaseType::Float; + size_t rows = 0u; + size_t columns = 0u; + size_t bit_width = 0u; + std::optional array_elements = std::nullopt; +}; + +struct InputDescription { + std::string name; + size_t location; + size_t set; + size_t binding; + spirv_cross::SPIRType::BaseType type = + spirv_cross::SPIRType::BaseType::Unknown; + size_t bit_width; + size_t vec_size; + size_t columns; + size_t offset; +}; + /// A shader config parsed as part of a ShaderBundleConfig. struct ShaderConfig { std::string source_file_name; diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index 1867b65862e0d..3db6629236814 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -125,7 +125,6 @@ impellerc("flutter_gpu_shaders") { "flutter_gpu_texture.frag", "flutter_gpu_texture.vert", ] - shader_target_flag = "--runtime-stage-metal" shader_bundle = "{\"UnlitFragment\": {\"type\": \"fragment\", \"file\": \"../../flutter/impeller/fixtures/flutter_gpu_unlit.frag\"}, \"UnlitVertex\": {\"type\": \"vertex\", \"file\": \"../../flutter/impeller/fixtures/flutter_gpu_unlit.vert\"}, \"TextureFragment\": {\"type\": \"fragment\", \"file\": \"../../flutter/impeller/fixtures/flutter_gpu_texture.frag\"}, \"TextureVertex\": {\"type\": \"vertex\", \"file\": \"../../flutter/impeller/fixtures/flutter_gpu_texture.vert\"}}" shader_bundle_output = "playground.shaderbundle" } diff --git a/impeller/fixtures/dart_tests.dart b/impeller/fixtures/dart_tests.dart index 51c9029aac866..04c74c8e92a7a 100644 --- a/impeller/fixtures/dart_tests.dart +++ b/impeller/fixtures/dart_tests.dart @@ -208,21 +208,18 @@ void canCreateRenderPassAndSubmit() { 0.5, 0.5, // 0.5, -0.5, // ])); - final gpu.BufferView color = - transients.emplace(float32([0, 1, 0, 1])); // rgba - final gpu.BufferView mvp = transients.emplace(float32([ - 1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1, // + final gpu.BufferView vertInfoData = transients.emplace(float32([ + 1, 0, 0, 0, // mvp + 0, 1, 0, 0, // mvp + 0, 0, 1, 0, // mvp + 0, 0, 0, 1, // mvp + 0, 1, 0, 1, // color ])); encoder.bindVertexBuffer(vertices, 3); - final gpu.UniformSlot? colorSlot = - pipeline.vertexShader.getUniformSlot('color'); - final gpu.UniformSlot? mvpSlot = pipeline.vertexShader.getUniformSlot('mvp'); - encoder.bindUniform(mvpSlot!, mvp); - encoder.bindUniform(colorSlot!, color); + final gpu.UniformSlot? vertInfo = + pipeline.vertexShader.getUniformSlot('vert_info'); + encoder.bindUniform(vertInfo!, vertInfoData); encoder.draw(); commandBuffer.submit(); diff --git a/impeller/fixtures/flutter_gpu_texture.vert b/impeller/fixtures/flutter_gpu_texture.vert index 8f3807d71b15c..69a0fcdf3b116 100644 --- a/impeller/fixtures/flutter_gpu_texture.vert +++ b/impeller/fixtures/flutter_gpu_texture.vert @@ -2,7 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -uniform mat4 mvp; +uniform VertInfo { + mat4 mvp; +} +vert_info; in vec3 position; in vec2 texture_coords; @@ -13,5 +16,5 @@ out vec4 v_color; void main() { v_texture_coords = texture_coords; v_color = color; - gl_Position = mvp * vec4(position, 1.0); + gl_Position = vert_info.mvp * vec4(position, 1.0); } diff --git a/impeller/fixtures/flutter_gpu_unlit.vert b/impeller/fixtures/flutter_gpu_unlit.vert index 1de82a3294fb7..cdf109d53970e 100644 --- a/impeller/fixtures/flutter_gpu_unlit.vert +++ b/impeller/fixtures/flutter_gpu_unlit.vert @@ -2,15 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#version 320 es - -layout(location = 0) uniform mat4 mvp; -layout(location = 1) uniform vec4 color; +uniform VertInfo { + mat4 mvp; + vec4 color; +} +vert_info; in vec2 position; out vec4 v_color; void main() { - v_color = color; - gl_Position = mvp * vec4(position, 0.0, 1.0); + v_color = vert_info.color; + gl_Position = vert_info.mvp * vec4(position, 0.0, 1.0); } diff --git a/impeller/renderer/renderer_dart_unittests.cc b/impeller/renderer/renderer_dart_unittests.cc index ae9a7804d82e6..423f2a29d00db 100644 --- a/impeller/renderer/renderer_dart_unittests.cc +++ b/impeller/renderer/renderer_dart_unittests.cc @@ -9,7 +9,6 @@ #include "flutter/common/settings.h" #include "flutter/common/task_runners.h" #include "flutter/lib/gpu/context.h" -#include "flutter/lib/gpu/shader.h" #include "flutter/lib/gpu/shader_library.h" #include "flutter/runtime/dart_isolate.h" #include "flutter/runtime/dart_vm_lifecycle.h" @@ -17,11 +16,8 @@ #include "flutter/testing/dart_isolate_runner.h" #include "flutter/testing/testing.h" #include "fml/memory/ref_ptr.h" -#include "impeller/core/shader_types.h" #include "impeller/playground/playground_test.h" #include "impeller/renderer/render_pass.h" -#include "impeller/renderer/vertex_descriptor.h" -#include "impeller/runtime_stage/runtime_stage.h" #include "gtest/gtest.h" #include "third_party/imgui/imgui.h" @@ -29,11 +25,11 @@ namespace impeller { namespace testing { -static void InstantiateTestShaderLibrary() { +static void InstantiateTestShaderLibrary(Context::BackendType backend_type) { auto fixture = flutter::testing::OpenFixtureAsMapping("playground.shaderbundle"); - auto library = - flutter::gpu::ShaderLibrary::MakeFromFlatbuffer(std::move(fixture)); + auto library = flutter::gpu::ShaderLibrary::MakeFromFlatbuffer( + backend_type, std::move(fixture)); flutter::gpu::ShaderLibrary::SetOverride(library); } @@ -57,7 +53,7 @@ class RendererDartTest : public PlaygroundTest, assert(GetContext() != nullptr); flutter::gpu::Context::SetOverrideContext(GetContext()); - InstantiateTestShaderLibrary(); + InstantiateTestShaderLibrary(GetContext()->GetBackendType()); return isolate_.get(); } diff --git a/impeller/shader_bundle/BUILD.gn b/impeller/shader_bundle/BUILD.gn index fd58931d755ef..34f5827bf8504 100644 --- a/impeller/shader_bundle/BUILD.gn +++ b/impeller/shader_bundle/BUILD.gn @@ -10,7 +10,6 @@ flatbuffers("shader_bundle_flatbuffers") { public_configs = [ "//flutter/impeller/runtime_stage:runtime_stage_config" ] public_deps = [ "//flutter/impeller/runtime_stage:runtime_stage_flatbuffers", - "//flutter/impeller/runtime_stage:runtime_stage_types_flatbuffers", "//flutter/third_party/flatbuffers", ] } diff --git a/impeller/shader_bundle/shader_bundle.fbs b/impeller/shader_bundle/shader_bundle.fbs index 1c0f4ce038967..9eae9c6ef7684 100644 --- a/impeller/shader_bundle/shader_bundle.fbs +++ b/impeller/shader_bundle/shader_bundle.fbs @@ -8,7 +8,11 @@ namespace impeller.fb; table Shader { name: string; - shader: RuntimeStage; + metal_ios: RuntimeStage; + metal_desktop: RuntimeStage; + opengl_es: RuntimeStage; + opengl_desktop: RuntimeStage; + vulkan: RuntimeStage; } table ShaderBundle { diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index db66f60a6d0ca..eca6c8fa40798 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -288,6 +288,7 @@ template("_impellerc") { # --opengl-desktop # --vulkan # --runtime-stage-metal +# Not required for --shader_bundle mode. # Required: sl_file_extension The file extension to use for output files. # Not required for --shader_bundle mode. # Optional: iplr Causes --sl output to be in iplr/runtime @@ -305,7 +306,7 @@ template("_impellerc") { # flatbuffer. template("impellerc") { assert(defined(invoker.shaders), "Impeller shaders must be specified.") - assert(defined(invoker.shader_target_flag), + assert(defined(invoker.shader_target_flag) || defined(invoker.shader_bundle), "The flag to impellerc for target selection must be specified.") assert(defined(invoker.sl_file_extension) || defined(invoker.shader_bundle), "The extension of the SL file must be specified (metal, glsl, etc..).") @@ -315,7 +316,7 @@ template("impellerc") { "When shader_bundle is specified, shader_output_bundle must also be specified.") } - sksl = invoker.shader_target_flag == "--sksl" + sksl = defined(invoker.iplr) && invoker.shader_target_flag == "--sksl" iplr = false if (defined(invoker.iplr) && invoker.iplr) { iplr = invoker.iplr @@ -347,13 +348,8 @@ template("impellerc") { generated_dir = "$target_gen_dir" } - shader_target_flag = invoker.shader_target_flag - shader_lib_dir = rebase_path("//flutter/impeller/compiler/shader_lib") - args = [ - "--include=$shader_lib_dir", - "$shader_target_flag", - ] + args = [ "--include=$shader_lib_dir" ] # When we're in single invocation mode, we can't use source enumeration. if (!single_invocation) { @@ -365,6 +361,10 @@ template("impellerc") { "--include={{source_dir}}", "--depfile=$depfile_intermediate_path", ] + + if (defined(invoker.shader_target_flag)) { + args += [ "${invoker.shader_target_flag}" ] + } } if (defined(invoker.gles_language_version)) { diff --git a/lib/gpu/context.cc b/lib/gpu/context.cc index 03536ef352d56..6f5d0bd29b53d 100644 --- a/lib/gpu/context.cc +++ b/lib/gpu/context.cc @@ -22,10 +22,41 @@ void Context::SetOverrideContext(std::shared_ptr context) { default_context_ = std::move(context); } -std::shared_ptr Context::GetDefaultContext() { +std::shared_ptr Context::GetOverrideContext() { return default_context_; } +std::shared_ptr Context::GetDefaultContext( + std::optional& out_error) { + auto override_context = GetOverrideContext(); + if (override_context) { + return override_context; + } + + auto dart_state = flutter::UIDartState::Current(); + if (!dart_state->IsImpellerEnabled()) { + out_error = + "Flutter GPU requires the Impeller rendering backend to be enabled."; + return nullptr; + } + // Grab the Impeller context from the IO manager. + std::promise> context_promise; + auto impeller_context_future = context_promise.get_future(); + fml::TaskRunner::RunNowOrPostTask( + dart_state->GetTaskRunners().GetIOTaskRunner(), + fml::MakeCopyable([promise = std::move(context_promise), + io_manager = dart_state->GetIOManager()]() mutable { + promise.set_value(io_manager ? io_manager->GetImpellerContext() + : nullptr); + })); + auto context = impeller_context_future.get(); + + if (!context) { + out_error = "Unable to retrieve the Impeller context."; + } + return context; +} + Context::Context(std::shared_ptr context) : context_(std::move(context)) {} @@ -43,33 +74,12 @@ std::shared_ptr Context::GetContext() { /// Dart_Handle InternalFlutterGpu_Context_InitializeDefault(Dart_Handle wrapper) { - auto dart_state = flutter::UIDartState::Current(); - - std::shared_ptr impeller_context = - flutter::gpu::Context::GetDefaultContext(); - - if (!impeller_context) { - if (!dart_state->IsImpellerEnabled()) { - return tonic::ToDart( - "Flutter GPU requires the Impeller rendering backend to be enabled."); - } - - // Grab the Impeller context from the IO manager. - std::promise> context_promise; - auto impeller_context_future = context_promise.get_future(); - fml::TaskRunner::RunNowOrPostTask( - dart_state->GetTaskRunners().GetIOTaskRunner(), - fml::MakeCopyable([promise = std::move(context_promise), - io_manager = dart_state->GetIOManager()]() mutable { - promise.set_value(io_manager ? io_manager->GetImpellerContext() - : nullptr); - })); - impeller_context = impeller_context_future.get(); + std::optional out_error; + auto impeller_context = flutter::gpu::Context::GetDefaultContext(out_error); + if (out_error.has_value()) { + return tonic::ToDart(out_error.value()); } - if (!impeller_context) { - return tonic::ToDart("Unable to retrieve the Impeller context."); - } auto res = fml::MakeRefCounted(impeller_context); res->AssociateWithDartWrapper(wrapper); diff --git a/lib/gpu/context.h b/lib/gpu/context.h index 943686edf12a9..903b4a0af98e5 100644 --- a/lib/gpu/context.h +++ b/lib/gpu/context.h @@ -20,7 +20,10 @@ class Context : public RefCountedDartWrappable { public: static void SetOverrideContext(std::shared_ptr context); - static std::shared_ptr GetDefaultContext(); + static std::shared_ptr GetOverrideContext(); + + static std::shared_ptr GetDefaultContext( + std::optional& out_error); explicit Context(std::shared_ptr context); ~Context() override; diff --git a/lib/gpu/shader_library.cc b/lib/gpu/shader_library.cc index 72a39a138824e..4ba6e5c7d1b16 100644 --- a/lib/gpu/shader_library.cc +++ b/lib/gpu/shader_library.cc @@ -19,6 +19,7 @@ #include "impeller/renderer/vertex_descriptor.h" #include "impeller/runtime_stage/runtime_stage.h" #include "impeller/shader_bundle/shader_bundle_flatbuffers.h" +#include "lib/gpu/context.h" namespace flutter { namespace gpu { @@ -28,6 +29,7 @@ IMPLEMENT_WRAPPERTYPEINFO(flutter_gpu, ShaderLibrary); fml::RefPtr ShaderLibrary::override_shader_library_; fml::RefPtr ShaderLibrary::MakeFromAsset( + impeller::Context::BackendType backend_type, const std::string& name, std::string& out_error) { if (override_shader_library_) { @@ -44,7 +46,7 @@ fml::RefPtr ShaderLibrary::MakeFromAsset( return nullptr; } - return MakeFromFlatbuffer(std::move(data)); + return MakeFromFlatbuffer(backend_type, std::move(data)); } fml::RefPtr ShaderLibrary::MakeFromShaders(ShaderMap shaders) { @@ -107,7 +109,29 @@ static size_t SizeOfInputType(impeller::fb::InputDataType input_type) { } } +static const impeller::fb::RuntimeStage* GetShaderBackend( + impeller::Context::BackendType backend_type, + const impeller::fb::Shader* shader) { + switch (backend_type) { + case impeller::Context::BackendType::kMetal: +#ifdef FML_OS_IOS + return shader->metal_ios(); +#else + return shader->metal_desktop(); +#endif + case impeller::Context::BackendType::kOpenGLES: +#ifdef FML_OS_ANDROID + return shader->opengl_es(); +#else + return shader->opengl_desktop(); +#endif + case impeller::Context::BackendType::kVulkan: + return shader->vulkan(); + } +} + fml::RefPtr ShaderLibrary::MakeFromFlatbuffer( + impeller::Context::BackendType backend_type, std::shared_ptr payload) { if (payload == nullptr || !payload->GetMapping()) { return nullptr; @@ -123,7 +147,14 @@ fml::RefPtr ShaderLibrary::MakeFromFlatbuffer( ShaderLibrary::ShaderMap shader_map; for (const auto* bundled_shader : *bundle->shaders()) { - const impeller::fb::RuntimeStage* runtime_stage = bundled_shader->shader(); + const impeller::fb::RuntimeStage* runtime_stage = + GetShaderBackend(backend_type, bundled_shader); + if (!runtime_stage) { + FML_LOG(ERROR) << "Failed to unpack shader \"" + << bundled_shader->name()->c_str() << "\" from bundle."; + continue; + } + impeller::RuntimeStage stage(runtime_stage); std::shared_ptr vertex_descriptor = nullptr; @@ -209,9 +240,16 @@ Dart_Handle InternalFlutterGpu_ShaderLibrary_InitializeWithAsset( return tonic::ToDart("Asset name must be a string"); } + std::optional out_error; + auto impeller_context = flutter::gpu::Context::GetDefaultContext(out_error); + if (out_error.has_value()) { + return tonic::ToDart(out_error.value()); + } + std::string error; auto res = flutter::gpu::ShaderLibrary::MakeFromAsset( - tonic::StdStringFromDart(asset_name), error); + impeller_context->GetBackendType(), tonic::StdStringFromDart(asset_name), + error); if (!res) { return tonic::ToDart(error); } diff --git a/lib/gpu/shader_library.h b/lib/gpu/shader_library.h index 7285ae35284f3..449faf3dfa951 100644 --- a/lib/gpu/shader_library.h +++ b/lib/gpu/shader_library.h @@ -26,12 +26,15 @@ class ShaderLibrary : public RefCountedDartWrappable { public: using ShaderMap = std::unordered_map>; - static fml::RefPtr MakeFromAsset(const std::string& name, - std::string& out_error); + static fml::RefPtr MakeFromAsset( + impeller::Context::BackendType backend_type, + const std::string& name, + std::string& out_error); static fml::RefPtr MakeFromShaders(ShaderMap shaders); static fml::RefPtr MakeFromFlatbuffer( + impeller::Context::BackendType backend_type, std::shared_ptr payload); /// Sets a return override for `MakeFromAsset` for testing purposes.