diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 54d0b1de62401..22a401268d993 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -2947,8 +2947,10 @@ TEST_P(AiksTest, CanRenderClippedRuntimeEffects) { GTEST_SKIP_("This backend doesn't support runtime effects."); } - auto runtime_stage = + auto runtime_stages = OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr"); + auto runtime_stage = runtime_stages[RuntimeStageBackend::kMetal]; + ASSERT_TRUE(runtime_stage); ASSERT_TRUE(runtime_stage->IsDirty()); struct FragUniforms { @@ -2980,7 +2982,9 @@ TEST_P(AiksTest, DrawPaintTransformsBounds) { GTEST_SKIP_("This backend doesn't support runtime effects."); } - auto runtime_stage = OpenAssetAsRuntimeStage("gradient.frag.iplr"); + auto runtime_stages = OpenAssetAsRuntimeStage("gradient.frag.iplr"); + auto runtime_stage = runtime_stages[RuntimeStageBackend::kMetal]; + ASSERT_TRUE(runtime_stage); ASSERT_TRUE(runtime_stage->IsDirty()); struct FragUniforms { diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index 48c7e9cc5fc14..9f3266681a412 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -10,6 +10,7 @@ #include "flutter/fml/file.h" #include "flutter/fml/mapping.h" #include "impeller/compiler/compiler.h" +#include "impeller/compiler/runtime_stage_data.h" #include "impeller/compiler/shader_bundle.h" #include "impeller/compiler/source_options.h" #include "impeller/compiler/switches.h" @@ -19,9 +20,9 @@ namespace impeller { namespace compiler { -/// Run the shader compiler to geneate SkSL. +/// Run the shader compiler to geneate SkSL reflection data. /// If there is an error, prints error text and returns `nullptr`. -static std::shared_ptr CompileSkSL( +static std::shared_ptr CompileSkSL( std::shared_ptr source_file_mapping, SourceOptions& options, Reflector::Options& reflector_options) { @@ -38,7 +39,7 @@ static std::shared_ptr CompileSkSL( std::cerr << sksl_compiler.GetErrorMessages() << std::endl; return nullptr; } - return sksl_compiler.GetSLShaderSource(); + return sksl_compiler.GetReflector()->GetRuntimeStageShaderData(); } /// Outputs artifacts for a single compiler invocation and option configuration. @@ -52,11 +53,11 @@ static bool OutputArtifacts(Compiler& compiler, /// 1. Invoke the compiler to generate SkSL if needed. /// - std::shared_ptr sksl_mapping; + std::shared_ptr sksl_shader; if (switches.iplr && TargetPlatformBundlesSkSL(switches.target_platform)) { - sksl_mapping = + sksl_shader = CompileSkSL(std::move(source_file_mapping), options, reflector_options); - if (!sksl_mapping) { + if (!sksl_shader) { return false; } } @@ -74,17 +75,42 @@ static bool OutputArtifacts(Compiler& compiler, std::cerr << "Could not create reflector." << std::endl; return false; } - auto stage_data = reflector->GetRuntimeStageData(); + auto stage_data = reflector->GetRuntimeStageShaderData(); if (!stage_data) { std::cerr << "Runtime stage information was nil." << std::endl; return false; } - if (sksl_mapping) { - stage_data->SetSkSLData(std::move(sksl_mapping)); + RuntimeStageData stages; + if (sksl_shader) { + stages.AddShader(RuntimeStageBackend::kSkSL, sksl_shader); } - auto stage_data_mapping = options.json_format - ? stage_data->CreateJsonMapping() - : stage_data->CreateMapping(); + switch (switches.target_platform) { + case TargetPlatform::kUnknown: + case TargetPlatform::kMetalDesktop: + case TargetPlatform::kMetalIOS: + case TargetPlatform::kOpenGLES: + case TargetPlatform::kOpenGLDesktop: + case TargetPlatform::kVulkan: + std::cerr << "TargetPlatform " + << TargetPlatformToString(switches.target_platform) + << " not supported for IPLR."; + return false; + case TargetPlatform::kRuntimeStageMetal: + stages.AddShader(RuntimeStageBackend::kMetal, stage_data); + break; + case TargetPlatform::kRuntimeStageGLES: + stages.AddShader(RuntimeStageBackend::kOpenGLES, stage_data); + break; + case TargetPlatform::kRuntimeStageVulkan: + stages.AddShader(RuntimeStageBackend::kVulkan, stage_data); + break; + case TargetPlatform::kSkSL: + // Already handled above. + break; + } + + auto stage_data_mapping = options.json_format ? stages.CreateJsonMapping() + : stages.CreateMapping(); if (!stage_data_mapping) { std::cerr << "Runtime stage data could not be created." << std::endl; return false; diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index ca3ecd163e914..efb0ec4fde911 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -127,8 +127,8 @@ Reflector::Reflector(Options options, return; } - runtime_stage_data_ = GenerateRuntimeStageData(); - if (!runtime_stage_data_) { + runtime_stage_shader_ = GenerateRuntimeStageData(); + if (!runtime_stage_shader_) { return; } @@ -162,8 +162,9 @@ std::shared_ptr Reflector::GetReflectionCC() const { return reflection_cc_; } -std::shared_ptr Reflector::GetRuntimeStageData() const { - return runtime_stage_data_; +std::shared_ptr Reflector::GetRuntimeStageShaderData() + const { + return runtime_stage_shader_; } std::optional Reflector::GenerateTemplateArguments() const { @@ -317,18 +318,17 @@ std::shared_ptr Reflector::GenerateReflectionCC() const { return InflateTemplate(kReflectionCCTemplate); } -std::shared_ptr Reflector::GenerateRuntimeStageData() const { +std::shared_ptr Reflector::GenerateRuntimeStageData() + 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_); + auto data = std::make_unique(); + data->entrypoint = options_.entry_point_name; + data->stage = entrypoints.front().execution_model; + data->shader = shader_data_; // Sort the IR so that the uniforms are in declaration order. std::vector uniforms = @@ -346,7 +346,7 @@ std::shared_ptr Reflector::GenerateRuntimeStageData() const { 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)); + data->uniforms.emplace_back(std::move(uniform_description)); } // We only need to worry about storing vertex attributes. @@ -373,7 +373,7 @@ std::shared_ptr Reflector::GenerateRuntimeStageData() const { input_description.vec_size = type.vecsize; input_description.columns = type.columns; input_description.offset = offset.value_or(0u); - data->AddInputDescription(std::move(input_description)); + data->inputs.emplace_back(std::move(input_description)); } } diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index d55a1420efd71..2ebfc6fbde166 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -72,7 +72,7 @@ class Reflector { std::shared_ptr GetReflectionCC() const; - std::shared_ptr GetRuntimeStageData() const; + std::shared_ptr GetRuntimeStageShaderData() const; private: struct StructDefinition { @@ -100,7 +100,7 @@ class Reflector { std::unique_ptr template_arguments_; std::shared_ptr reflection_header_; std::shared_ptr reflection_cc_; - std::shared_ptr runtime_stage_data_; + std::shared_ptr runtime_stage_shader_; bool is_valid_ = false; std::optional GenerateTemplateArguments() const; @@ -109,7 +109,7 @@ class Reflector { std::shared_ptr GenerateReflectionCC() const; - std::shared_ptr GenerateRuntimeStageData() const; + std::shared_ptr GenerateRuntimeStageData() const; std::shared_ptr InflateTemplate(std::string_view tmpl) const; diff --git a/impeller/compiler/runtime_stage_data.cc b/impeller/compiler/runtime_stage_data.cc index a27517c2369e8..3e9497664abcf 100644 --- a/impeller/compiler/runtime_stage_data.cc +++ b/impeller/compiler/runtime_stage_data.cc @@ -6,39 +6,27 @@ #include #include +#include #include +#include "impeller/core/runtime_types.h" #include "inja/inja.hpp" #include "impeller/base/validation.h" #include "impeller/runtime_stage/runtime_stage_flatbuffers.h" +#include "runtime_stage_types_flatbuffers.h" namespace impeller { namespace compiler { -RuntimeStageData::RuntimeStageData(std::string entrypoint, - spv::ExecutionModel stage, - TargetPlatform target_platform) - : entrypoint_(std::move(entrypoint)), - stage_(stage), - target_platform_(target_platform) {} +RuntimeStageData::RuntimeStageData() = default; RuntimeStageData::~RuntimeStageData() = default; -void RuntimeStageData::AddUniformDescription(UniformDescription uniform) { - uniforms_.emplace_back(std::move(uniform)); -} - -void RuntimeStageData::AddInputDescription(InputDescription input) { - inputs_.emplace_back(std::move(input)); -} - -void RuntimeStageData::SetShaderData(std::shared_ptr shader) { - shader_ = std::move(shader); -} - -void RuntimeStageData::SetSkSLData(std::shared_ptr sksl) { - sksl_ = std::move(sksl); +void RuntimeStageData::AddShader(RuntimeStageBackend backend, + const std::shared_ptr& data) { + FML_DCHECK(data_.find(backend) == data_.end()); + data_[backend] = data; } static std::optional ToStage(spv::ExecutionModel stage) { @@ -69,49 +57,6 @@ static std::optional ToJsonStage(spv::ExecutionModel stage) { FML_UNREACHABLE(); } -static std::optional ToTargetPlatform( - TargetPlatform platform) { - switch (platform) { - case TargetPlatform::kUnknown: - case TargetPlatform::kMetalDesktop: - case TargetPlatform::kMetalIOS: - case TargetPlatform::kOpenGLES: - case TargetPlatform::kOpenGLDesktop: - case TargetPlatform::kVulkan: - return std::nullopt; - case TargetPlatform::kSkSL: - return fb::TargetPlatform::kSkSL; - case TargetPlatform::kRuntimeStageMetal: - return fb::TargetPlatform::kMetal; - case TargetPlatform::kRuntimeStageGLES: - return fb::TargetPlatform::kOpenGLES; - case TargetPlatform::kRuntimeStageVulkan: - return fb::TargetPlatform::kVulkan; - } - FML_UNREACHABLE(); -} - -static std::optional ToJsonTargetPlatform(TargetPlatform platform) { - switch (platform) { - case TargetPlatform::kUnknown: - case TargetPlatform::kMetalDesktop: - case TargetPlatform::kMetalIOS: - case TargetPlatform::kOpenGLES: - case TargetPlatform::kOpenGLDesktop: - case TargetPlatform::kVulkan: - return std::nullopt; - case TargetPlatform::kSkSL: - return static_cast(fb::TargetPlatform::kSkSL); - case TargetPlatform::kRuntimeStageMetal: - return static_cast(fb::TargetPlatform::kMetal); - case TargetPlatform::kRuntimeStageGLES: - return static_cast(fb::TargetPlatform::kOpenGLES); - case TargetPlatform::kRuntimeStageVulkan: - return static_cast(fb::TargetPlatform::kVulkan); - } - FML_UNREACHABLE(); -} - static std::optional ToUniformType( spirv_cross::SPIRType::BaseType type) { switch (type) { @@ -248,7 +193,7 @@ static const char* kStageKey = "stage"; static const char* kTargetPlatformKey = "target_platform"; static const char* kEntrypointKey = "entrypoint"; static const char* kUniformsKey = "uniforms"; -static const char* kShaderKey = "sksl"; +static const char* kShaderKey = "shader"; static const char* kUniformNameKey = "name"; static const char* kUniformLocationKey = "location"; static const char* kUniformTypeKey = "type"; @@ -257,71 +202,87 @@ static const char* kUniformColumnsKey = "columns"; static const char* kUniformBitWidthKey = "bit_width"; static const char* kUniformArrayElementsKey = "array_elements"; +static std::string RuntimeStageBackendToString(RuntimeStageBackend backend) { + switch (backend) { + case RuntimeStageBackend::kSkSL: + return "sksl"; + case RuntimeStageBackend::kMetal: + return "metal"; + case RuntimeStageBackend::kOpenGLES: + return "opengles"; + case RuntimeStageBackend::kVulkan: + return "vulkan"; + } +} + std::shared_ptr RuntimeStageData::CreateJsonMapping() const { // Runtime Stage Data JSON format // { - // "stage": 0, - // "target_platform": "", - // "entrypoint": "", - // "shader": "", - // "sksl": "", - // "uniforms": [ - // { - // "name": "..", - // "location": 0, - // "type": 0, - // "rows": 0, - // "columns": 0, - // "bit_width": 0, - // "array_elements": 0, - // } - // ] + // "sksl": { + // "stage": 0, + // "entrypoint": "", + // "shader": "", + // "uniforms": [ + // { + // "name": "..", + // "location": 0, + // "type": 0, + // "rows": 0, + // "columns": 0, + // "bit_width": 0, + // "array_elements": 0, + // } + // ] + // }, + // "metal": ... // }, nlohmann::json root; - const auto stage = ToJsonStage(stage_); - if (!stage.has_value()) { - VALIDATION_LOG << "Invalid runtime stage."; - return nullptr; - } - root[kStageKey] = static_cast(stage.value()); - - const auto target_platform = ToJsonTargetPlatform(target_platform_); - if (!target_platform.has_value()) { - VALIDATION_LOG << "Invalid target platform for runtime stage."; - return nullptr; - } - root[kTargetPlatformKey] = target_platform.value(); - - if (shader_->GetSize() > 0u) { - std::string shader(reinterpret_cast(shader_->GetMapping()), - shader_->GetSize()); - root[kShaderKey] = shader.c_str(); - } + for (const auto& kvp : data_) { + nlohmann::json platform_object; - auto& uniforms = root[kUniformsKey] = nlohmann::json::array_t{}; - for (const auto& uniform : uniforms_) { - nlohmann::json uniform_object; - uniform_object[kUniformNameKey] = uniform.name.c_str(); - uniform_object[kUniformLocationKey] = uniform.location; - uniform_object[kUniformRowsKey] = uniform.rows; - uniform_object[kUniformColumnsKey] = uniform.columns; - - auto uniform_type = ToJsonType(uniform.type); - if (!uniform_type.has_value()) { - VALIDATION_LOG << "Invalid uniform type for runtime stage."; + const auto stage = ToJsonStage(kvp.second->stage); + if (!stage.has_value()) { + VALIDATION_LOG << "Invalid runtime stage."; return nullptr; } + platform_object[kStageKey] = static_cast(stage.value()); + platform_object[kEntrypointKey] = kvp.second->entrypoint; + + if (kvp.second->shader->GetSize() > 0u) { + std::string shader( + reinterpret_cast(kvp.second->shader->GetMapping()), + kvp.second->shader->GetSize()); + platform_object[kShaderKey] = shader.c_str(); + } - uniform_object[kUniformTypeKey] = uniform_type.value(); - uniform_object[kUniformBitWidthKey] = uniform.bit_width; - - if (uniform.array_elements.has_value()) { - uniform_object[kUniformArrayElementsKey] = uniform.array_elements.value(); - } else { - uniform_object[kUniformArrayElementsKey] = 0; + auto& uniforms = platform_object[kUniformsKey] = nlohmann::json::array_t{}; + for (const auto& uniform : kvp.second->uniforms) { + nlohmann::json uniform_object; + uniform_object[kUniformNameKey] = uniform.name.c_str(); + uniform_object[kUniformLocationKey] = uniform.location; + uniform_object[kUniformRowsKey] = uniform.rows; + uniform_object[kUniformColumnsKey] = uniform.columns; + + auto uniform_type = ToJsonType(uniform.type); + if (!uniform_type.has_value()) { + VALIDATION_LOG << "Invalid uniform type for runtime stage."; + return nullptr; + } + + uniform_object[kUniformTypeKey] = uniform_type.value(); + uniform_object[kUniformBitWidthKey] = uniform.bit_width; + + if (uniform.array_elements.has_value()) { + uniform_object[kUniformArrayElementsKey] = + uniform.array_elements.value(); + } else { + uniform_object[kUniformArrayElementsKey] = 0; + } + uniforms.push_back(uniform_object); } - uniforms.push_back(uniform_object); + + root[RuntimeStageBackendToString(kvp.first)] = platform_object; } auto json_string = std::make_shared(root.dump(2u)); @@ -331,100 +292,106 @@ std::shared_ptr RuntimeStageData::CreateJsonMapping() const { json_string->size(), [json_string](auto, auto) {}); } -std::unique_ptr RuntimeStageData::CreateFlatbuffer() const { - auto runtime_stage = std::make_unique(); - +std::unique_ptr RuntimeStageData::CreateFlatbuffer() const { // The high level object API is used here for writing to the buffer. This is // just a convenience. - runtime_stage->entrypoint = entrypoint_; - const auto stage = ToStage(stage_); - if (!stage.has_value()) { - VALIDATION_LOG << "Invalid runtime stage."; - return nullptr; - } - runtime_stage->stage = stage.value(); - const auto target_platform = ToTargetPlatform(target_platform_); - if (!target_platform.has_value()) { - VALIDATION_LOG << "Invalid target platform for runtime stage."; - return nullptr; - } - runtime_stage->target_platform = target_platform.value(); - if (!shader_) { - VALIDATION_LOG << "No shader specified for runtime stage."; - return nullptr; - } - if (shader_->GetSize() > 0u) { - runtime_stage->shader = {shader_->GetMapping(), - shader_->GetMapping() + shader_->GetSize()}; - } - // It is not an error for the SkSL to be ommitted. - if (sksl_ && sksl_->GetSize() > 0u) { - runtime_stage->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."; + auto runtime_stages = std::make_unique(); + + for (const auto& kvp : data_) { + auto runtime_stage = std::make_unique(); + runtime_stage->entrypoint = kvp.second->entrypoint; + const auto stage = ToStage(kvp.second->stage); + if (!stage.has_value()) { + VALIDATION_LOG << "Invalid runtime stage."; 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."; + runtime_stage->stage = stage.value(); + if (!kvp.second->shader) { + VALIDATION_LOG << "No shader specified 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(); + if (kvp.second->shader->GetSize() > 0u) { + runtime_stage->shader = { + kvp.second->shader->GetMapping(), + kvp.second->shader->GetMapping() + kvp.second->shader->GetSize()}; + } + for (const auto& uniform : kvp.second->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(); + } + + runtime_stage->uniforms.emplace_back(std::move(desc)); } - runtime_stage->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; + for (const auto& input : kvp.second->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; + + runtime_stage->inputs.emplace_back(std::move(desc)); } - 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; + switch (kvp.first) { + case RuntimeStageBackend::kSkSL: + runtime_stages->sksl = std::move(runtime_stage); + break; + case RuntimeStageBackend::kMetal: + runtime_stages->metal = std::move(runtime_stage); + break; + case RuntimeStageBackend::kOpenGLES: + runtime_stages->opengles = std::move(runtime_stage); + break; + case RuntimeStageBackend::kVulkan: + runtime_stages->vulkan = std::move(runtime_stage); + break; } - 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; - - runtime_stage->inputs.emplace_back(std::move(desc)); } - - return runtime_stage; + return runtime_stages; } std::shared_ptr RuntimeStageData::CreateMapping() const { - auto runtime_stage = CreateFlatbuffer(); - if (!runtime_stage) { + auto runtime_stages = CreateFlatbuffer(); + if (!runtime_stages) { return nullptr; } auto builder = std::make_shared(); - builder->Finish(fb::RuntimeStage::Pack(*builder.get(), runtime_stage.get()), - fb::RuntimeStageIdentifier()); + builder->Finish(fb::RuntimeStages::Pack(*builder.get(), runtime_stages.get()), + fb::RuntimeStagesIdentifier()); return std::make_shared(builder->GetBufferPointer(), builder->GetSize(), [builder](auto, auto) {}); diff --git a/impeller/compiler/runtime_stage_data.h b/impeller/compiler/runtime_stage_data.h index 8d0cbc5bf9679..c54efc807999e 100644 --- a/impeller/compiler/runtime_stage_data.h +++ b/impeller/compiler/runtime_stage_data.h @@ -11,6 +11,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" #include "impeller/compiler/types.h" +#include "impeller/core/runtime_types.h" #include "runtime_stage_types_flatbuffers.h" #include "spirv_parser.hpp" @@ -42,34 +43,34 @@ struct InputDescription { class RuntimeStageData { public: - RuntimeStageData(std::string entrypoint, - spv::ExecutionModel stage, - TargetPlatform target_platform); + struct Shader { + Shader() = default; - ~RuntimeStageData(); - - void AddUniformDescription(UniformDescription uniform); + std::string entrypoint; + spv::ExecutionModel stage; + std::vector uniforms; + std::vector inputs; + std::shared_ptr shader; - void AddInputDescription(InputDescription input); + Shader(const Shader&) = delete; + Shader& operator=(const Shader&) = delete; + }; - void SetShaderData(std::shared_ptr shader); + RuntimeStageData(); - void SetSkSLData(std::shared_ptr sksl); + ~RuntimeStageData(); - std::unique_ptr CreateFlatbuffer() const; + void AddShader(RuntimeStageBackend backend, + const std::shared_ptr& data); - std::shared_ptr CreateMapping() const; + std::unique_ptr CreateFlatbuffer() const; std::shared_ptr CreateJsonMapping() const; + std::shared_ptr CreateMapping() 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_; + std::map> data_; RuntimeStageData(const RuntimeStageData&) = delete; diff --git a/impeller/compiler/shader_bundle.cc b/impeller/compiler/shader_bundle.cc index d71d3bfa3c3a1..d32f2251699f6 100644 --- a/impeller/compiler/shader_bundle.cc +++ b/impeller/compiler/shader_bundle.cc @@ -9,6 +9,7 @@ #include "impeller/compiler/types.h" #include "impeller/compiler/utilities.h" +#include "impeller/runtime_stage/runtime_stage.h" #include "impeller/shader_bundle/shader_bundle_flatbuffers.h" #include "third_party/json/include/nlohmann/json.hpp" @@ -122,14 +123,35 @@ static std::unique_ptr GenerateShaderFB( return nullptr; } - auto stage_data = reflector->GetRuntimeStageData(); + auto stage_data = reflector->GetRuntimeStageShaderData(); if (!stage_data) { std::cerr << "Runtime stage information was nil for bundled shader \"" << shader_name << "\"." << std::endl; return nullptr; } - - result->shader = stage_data->CreateFlatbuffer(); + RuntimeStageData stages; + switch (options.target_platform) { + case TargetPlatform::kUnknown: + case TargetPlatform::kMetalDesktop: + case TargetPlatform::kMetalIOS: + case TargetPlatform::kOpenGLES: + case TargetPlatform::kOpenGLDesktop: + case TargetPlatform::kVulkan: + case TargetPlatform::kSkSL: + std::cerr << "Invalid target platform " + << TargetPlatformToString(options.target_platform); + return nullptr; + case TargetPlatform::kRuntimeStageMetal: + stages.AddShader(RuntimeStageBackend::kMetal, stage_data); + break; + case TargetPlatform::kRuntimeStageGLES: + stages.AddShader(RuntimeStageBackend::kOpenGLES, stage_data); + break; + case TargetPlatform::kRuntimeStageVulkan: + stages.AddShader(RuntimeStageBackend::kVulkan, stage_data); + break; + } + result->shader = stages.CreateFlatbuffer(); if (!result->shader) { std::cerr << "Failed to create flatbuffer for bundled shader \"" << shader_name << "\"." << std::endl; diff --git a/impeller/compiler/shader_bundle_unittests.cc b/impeller/compiler/shader_bundle_unittests.cc index 375f366bf64a4..c0edd02e8d3a3 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_TRUE(vertex->shader->metal); + EXPECT_STREQ(vertex->shader->metal->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->shader->metal->stage, fb::Stage::kVertex); // Inputs. - ASSERT_EQ(vertex->shader->inputs.size(), 1u); - const auto& v_in_position = vertex->shader->inputs[0]; + ASSERT_EQ(vertex->shader->metal->inputs.size(), 1u); + const auto& v_in_position = vertex->shader->metal->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->shader->metal->uniforms.size(), 2u); + const auto* v_mvp = FindByName(vertex->shader->metal->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->shader->metal->uniforms, "color"); ASSERT_NE(v_color, nullptr); EXPECT_EQ(v_color->location, 1u); EXPECT_EQ(v_color->type, fb::UniformDataType::kFloat); @@ -197,16 +197,16 @@ TEST(ShaderBundleTest, GenerateShaderBundleFlatbufferProducesCorrectResult) { /// Verify fragment shader. /// - EXPECT_STREQ(fragment->shader->entrypoint.c_str(), + EXPECT_TRUE(fragment->shader->metal); + EXPECT_STREQ(fragment->shader->metal->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->shader->metal->stage, fb::Stage::kFragment); // Inputs (not recorded for fragment shaders). - ASSERT_EQ(fragment->shader->inputs.size(), 0u); + ASSERT_EQ(fragment->shader->metal->inputs.size(), 0u); // Uniforms. - ASSERT_EQ(fragment->shader->inputs.size(), 0u); + ASSERT_EQ(fragment->shader->metal->inputs.size(), 0u); } } // namespace testing diff --git a/impeller/core/runtime_types.h b/impeller/core/runtime_types.h index 4fb210d728ebf..c4f4b38738867 100644 --- a/impeller/core/runtime_types.h +++ b/impeller/core/runtime_types.h @@ -11,6 +11,13 @@ namespace impeller { +enum class RuntimeStageBackend { + kSkSL, + kMetal, + kOpenGLES, + kVulkan, +}; + enum RuntimeUniformType { kBoolean, kSignedByte, diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index b0f1c32501142..4cb0c416d5121 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -2136,8 +2136,10 @@ TEST_P(EntityTest, RuntimeEffect) { GTEST_SKIP_("This backend doesn't support runtime effects."); } - auto runtime_stage = + auto runtime_stages = OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr"); + auto runtime_stage = runtime_stages[RuntimeStageBackend::kMetal]; + ASSERT_TRUE(runtime_stage); ASSERT_TRUE(runtime_stage->IsDirty()); bool first_frame = true; diff --git a/impeller/golden_tests/golden_playground_test.h b/impeller/golden_tests/golden_playground_test.h index 213c4d442c6c3..f411615b51d8e 100644 --- a/impeller/golden_tests/golden_playground_test.h +++ b/impeller/golden_tests/golden_playground_test.h @@ -47,8 +47,7 @@ class GoldenPlaygroundTest const char* fixture_name, bool enable_mipmapping = false) const; - std::shared_ptr OpenAssetAsRuntimeStage( - const char* asset_name) const; + RuntimeStage::Map OpenAssetAsRuntimeStage(const char* asset_name) const; std::shared_ptr GetContext() const; diff --git a/impeller/golden_tests/golden_playground_test_mac.cc b/impeller/golden_tests/golden_playground_test_mac.cc index 2bbea3d4db051..80f5a8630dc08 100644 --- a/impeller/golden_tests/golden_playground_test_mac.cc +++ b/impeller/golden_tests/golden_playground_test_mac.cc @@ -167,17 +167,14 @@ std::shared_ptr GoldenPlaygroundTest::CreateTextureForFixture( return result; } -std::shared_ptr GoldenPlaygroundTest::OpenAssetAsRuntimeStage( +RuntimeStage::Map GoldenPlaygroundTest::OpenAssetAsRuntimeStage( const char* asset_name) const { - auto fixture = flutter::testing::OpenFixtureAsMapping(asset_name); + const std::shared_ptr fixture = + flutter::testing::OpenFixtureAsMapping(asset_name); if (!fixture || fixture->GetSize() == 0) { - return nullptr; + return {}; } - auto stage = std::make_unique(std::move(fixture)); - if (!stage->IsValid()) { - return nullptr; - } - return stage; + return RuntimeStage::DecodeRuntimeStages(fixture); } std::shared_ptr GoldenPlaygroundTest::GetContext() const { diff --git a/impeller/golden_tests/golden_playground_test_stub.cc b/impeller/golden_tests/golden_playground_test_stub.cc index fc141eb70a954..9d7568a1faff2 100644 --- a/impeller/golden_tests/golden_playground_test_stub.cc +++ b/impeller/golden_tests/golden_playground_test_stub.cc @@ -42,10 +42,9 @@ std::shared_ptr GoldenPlaygroundTest::CreateTextureForFixture( bool enable_mipmapping) const { return nullptr; } - -std::shared_ptr GoldenPlaygroundTest::OpenAssetAsRuntimeStage( +RuntimeStage::Map GoldenPlaygroundTest::OpenAssetAsRuntimeStage( const char* asset_name) const { - return nullptr; + return {}; } std::shared_ptr GoldenPlaygroundTest::GetContext() const { diff --git a/impeller/playground/compute_playground_test.cc b/impeller/playground/compute_playground_test.cc index 71721e7577ec8..3df0e62a7260e 100644 --- a/impeller/playground/compute_playground_test.cc +++ b/impeller/playground/compute_playground_test.cc @@ -41,19 +41,6 @@ std::unique_ptr ComputePlaygroundTest::OpenAssetAsMapping( return flutter::testing::OpenFixtureAsMapping(asset_name); } -std::shared_ptr ComputePlaygroundTest::OpenAssetAsRuntimeStage( - const char* asset_name) const { - auto fixture = flutter::testing::OpenFixtureAsMapping(asset_name); - if (!fixture || fixture->GetSize() == 0) { - return nullptr; - } - auto stage = std::make_unique(std::move(fixture)); - if (!stage->IsValid()) { - return nullptr; - } - return stage; -} - static std::string FormatWindowTitle(const std::string& test_name) { std::stringstream stream; stream << "Impeller Playground for '" << test_name << "' (Press ESC to quit)"; diff --git a/impeller/playground/compute_playground_test.h b/impeller/playground/compute_playground_test.h index 1d426a2ae7eb5..66d1858749d5c 100644 --- a/impeller/playground/compute_playground_test.h +++ b/impeller/playground/compute_playground_test.h @@ -32,9 +32,6 @@ class ComputePlaygroundTest std::unique_ptr OpenAssetAsMapping( std::string asset_name) const override; - std::shared_ptr OpenAssetAsRuntimeStage( - const char* asset_name) const; - // |Playground| std::string GetWindowTitle() const override; diff --git a/impeller/playground/playground_test.cc b/impeller/playground/playground_test.cc index 613add35650fa..d9439dacc2a7c 100644 --- a/impeller/playground/playground_test.cc +++ b/impeller/playground/playground_test.cc @@ -46,17 +46,14 @@ std::unique_ptr PlaygroundTest::OpenAssetAsMapping( return flutter::testing::OpenFixtureAsMapping(asset_name); } -std::shared_ptr PlaygroundTest::OpenAssetAsRuntimeStage( +RuntimeStage::Map PlaygroundTest::OpenAssetAsRuntimeStage( const char* asset_name) const { - auto fixture = flutter::testing::OpenFixtureAsMapping(asset_name); + const std::shared_ptr fixture = + flutter::testing::OpenFixtureAsMapping(asset_name); if (!fixture || fixture->GetSize() == 0) { - return nullptr; + return {}; } - auto stage = std::make_unique(std::move(fixture)); - if (!stage->IsValid()) { - return nullptr; - } - return stage; + return RuntimeStage::DecodeRuntimeStages(fixture); } static std::string FormatWindowTitle(const std::string& test_name) { diff --git a/impeller/playground/playground_test.h b/impeller/playground/playground_test.h index 2d2539afa474c..e2051a296776b 100644 --- a/impeller/playground/playground_test.h +++ b/impeller/playground/playground_test.h @@ -37,8 +37,7 @@ class PlaygroundTest : public Playground, std::unique_ptr OpenAssetAsMapping( std::string asset_name) const override; - std::shared_ptr OpenAssetAsRuntimeStage( - const char* asset_name) const; + RuntimeStage::Map OpenAssetAsRuntimeStage(const char* asset_name) const; // |Playground| std::string GetWindowTitle() const override; diff --git a/impeller/runtime_stage/runtime_stage.cc b/impeller/runtime_stage/runtime_stage.cc index cbdee53517ad4..357f54313441c 100644 --- a/impeller/runtime_stage/runtime_stage.cc +++ b/impeller/runtime_stage/runtime_stage.cc @@ -5,9 +5,12 @@ #include "impeller/runtime_stage/runtime_stage.h" #include +#include +#include "fml/mapping.h" #include "impeller/base/validation.h" #include "impeller/runtime_stage/runtime_stage_flatbuffers.h" +#include "runtime_stage_types_flatbuffers.h" namespace impeller { @@ -55,26 +58,44 @@ static RuntimeShaderStage ToShaderStage(fb::Stage stage) { FML_UNREACHABLE(); } -RuntimeStage::RuntimeStage(std::shared_ptr payload) - : payload_(std::move(payload)) { - if (payload_ == nullptr || !payload_->GetMapping()) { - return; - } - if (!fb::RuntimeStageBufferHasIdentifier(payload_->GetMapping())) { - return; +std::unique_ptr RuntimeStage::RuntimeStageIfPresent( + const fb::RuntimeStage* runtime_stage, + const std::shared_ptr& payload) { + if (!runtime_stage) { + return nullptr; } - Setup(fb::GetRuntimeStage(payload_->GetMapping())); -} -RuntimeStage::RuntimeStage(const fb::RuntimeStage* runtime_stage) { - Setup(runtime_stage); + return std::unique_ptr( + new RuntimeStage(runtime_stage, payload)); } -void RuntimeStage::Setup(const fb::RuntimeStage* runtime_stage) { - if (!runtime_stage) { - return; +RuntimeStage::Map RuntimeStage::DecodeRuntimeStages( + const std::shared_ptr& payload) { + if (payload == nullptr || !payload->GetMapping()) { + return {}; + } + if (!fb::RuntimeStagesBufferHasIdentifier(payload->GetMapping())) { + return {}; } + auto raw_stages = fb::GetRuntimeStages(payload->GetMapping()); + return { + {RuntimeStageBackend::kSkSL, + RuntimeStageIfPresent(raw_stages->sksl(), payload)}, + {RuntimeStageBackend::kMetal, + RuntimeStageIfPresent(raw_stages->metal(), payload)}, + {RuntimeStageBackend::kOpenGLES, + RuntimeStageIfPresent(raw_stages->opengles(), payload)}, + {RuntimeStageBackend::kVulkan, + RuntimeStageIfPresent(raw_stages->vulkan(), payload)}, + }; +} + +RuntimeStage::RuntimeStage(const fb::RuntimeStage* runtime_stage, + const std::shared_ptr& payload) + : payload_(payload) { + FML_DCHECK(runtime_stage); + stage_ = ToShaderStage(runtime_stage->stage()); entrypoint_ = runtime_stage->entrypoint()->str(); @@ -99,14 +120,6 @@ void RuntimeStage::Setup(const fb::RuntimeStage* runtime_stage) { [payload = payload_](auto, auto) {} // ); - if (runtime_stage->sksl()) { - sksl_mapping_ = std::make_shared( - runtime_stage->sksl()->data(), // - runtime_stage->sksl()->size(), // - [payload = payload_](auto, auto) {} // - ); - } - is_valid_ = true; } @@ -122,10 +135,6 @@ const std::shared_ptr& RuntimeStage::GetCodeMapping() const { return code_mapping_; } -const std::shared_ptr& RuntimeStage::GetSkSLMapping() const { - return sksl_mapping_; -} - const std::vector& RuntimeStage::GetUniforms() const { return uniforms_; diff --git a/impeller/runtime_stage/runtime_stage.fbs b/impeller/runtime_stage/runtime_stage.fbs index afb2589cbb18b..189fbf0434049 100644 --- a/impeller/runtime_stage/runtime_stage.fbs +++ b/impeller/runtime_stage/runtime_stage.fbs @@ -6,5 +6,5 @@ include "runtime_stage_types.fbs"; namespace impeller.fb; -root_type RuntimeStage; +root_type RuntimeStages; file_identifier "IPLR"; diff --git a/impeller/runtime_stage/runtime_stage.h b/impeller/runtime_stage/runtime_stage.h index 084c36903579d..3b81e540a15eb 100644 --- a/impeller/runtime_stage/runtime_stage.h +++ b/impeller/runtime_stage/runtime_stage.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_IMPELLER_RUNTIME_STAGE_RUNTIME_STAGE_H_ #define FLUTTER_IMPELLER_RUNTIME_STAGE_RUNTIME_STAGE_H_ +#include #include #include @@ -18,10 +19,11 @@ namespace impeller { class RuntimeStage { public: - explicit RuntimeStage(std::shared_ptr payload); - - explicit RuntimeStage(const fb::RuntimeStage* runtime_stage); + using Map = std::map>; + static Map DecodeRuntimeStages(const std::shared_ptr& payload); + RuntimeStage(const fb::RuntimeStage* runtime_stage, + const std::shared_ptr& payload); ~RuntimeStage(); RuntimeStage(RuntimeStage&&); RuntimeStage& operator=(RuntimeStage&&); @@ -38,26 +40,25 @@ class RuntimeStage { const std::shared_ptr& GetCodeMapping() const; - const std::shared_ptr& GetSkSLMapping() const; - bool IsDirty() const; void SetClean(); private: - RuntimeShaderStage stage_ = RuntimeShaderStage::kVertex; std::shared_ptr payload_; + RuntimeShaderStage stage_ = RuntimeShaderStage::kVertex; std::string entrypoint_; std::shared_ptr code_mapping_; - std::shared_ptr sksl_mapping_; std::vector uniforms_; bool is_valid_ = false; bool is_dirty_ = true; - void Setup(const fb::RuntimeStage* runtime_stage); - RuntimeStage(const RuntimeStage&) = delete; + static std::unique_ptr RuntimeStageIfPresent( + const fb::RuntimeStage* runtime_stage, + const std::shared_ptr& payload); + RuntimeStage& operator=(const RuntimeStage&) = delete; }; diff --git a/impeller/runtime_stage/runtime_stage_types.fbs b/impeller/runtime_stage/runtime_stage_types.fbs index b0f7baf152957..21e6f802717a6 100644 --- a/impeller/runtime_stage/runtime_stage_types.fbs +++ b/impeller/runtime_stage/runtime_stage_types.fbs @@ -10,13 +10,6 @@ enum Stage:byte { kCompute, } -enum TargetPlatform:byte { - kMetal, - kOpenGLES, - kSkSL, - kVulkan, -} - // The subset of impeller::ShaderType that may be used for uniform bindings. enum UniformDataType:uint32 { kBoolean, @@ -75,10 +68,15 @@ table StageInput { table RuntimeStage { stage: Stage; - target_platform: TargetPlatform; entrypoint: string; inputs: [StageInput]; uniforms: [UniformDescription]; shader: [ubyte]; - sksl: [ubyte]; +} + +table RuntimeStages { + sksl: RuntimeStage; + metal: RuntimeStage; + opengles: RuntimeStage; + vulkan: RuntimeStage; } diff --git a/impeller/runtime_stage/runtime_stage_unittests.cc b/impeller/runtime_stage/runtime_stage_unittests.cc index 9fd6fe2f1c406..f5b95aa7b25a0 100644 --- a/impeller/runtime_stage/runtime_stage_unittests.cc +++ b/impeller/runtime_stage/runtime_stage_unittests.cc @@ -24,18 +24,19 @@ using RuntimeStageTest = RuntimeStagePlayground; INSTANTIATE_PLAYGROUND_SUITE(RuntimeStageTest); TEST(RuntimeStageTest, CanReadValidBlob) { - auto fixture = + const std::shared_ptr fixture = flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr"); ASSERT_TRUE(fixture); ASSERT_GT(fixture->GetSize(), 0u); - RuntimeStage stage(std::move(fixture)); - ASSERT_TRUE(stage.IsValid()); - ASSERT_EQ(stage.GetShaderStage(), RuntimeShaderStage::kFragment); + auto stages = RuntimeStage::DecodeRuntimeStages(fixture); + auto stage = stages[RuntimeStageBackend::kMetal]; + ASSERT_TRUE(stage->IsValid()); + ASSERT_EQ(stage->GetShaderStage(), RuntimeShaderStage::kFragment); } TEST(RuntimeStageTest, CanRejectInvalidBlob) { ScopedValidationDisable disable_validation; - auto fixture = + const std::shared_ptr fixture = flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr"); ASSERT_TRUE(fixture); auto junk_allocation = std::make_shared(); @@ -43,20 +44,23 @@ TEST(RuntimeStageTest, CanRejectInvalidBlob) { // Not meant to be secure. Just reject obviously bad blobs using magic // numbers. ::memset(junk_allocation->GetBuffer(), 127, junk_allocation->GetLength()); - RuntimeStage stage(CreateMappingFromAllocation(junk_allocation)); - ASSERT_FALSE(stage.IsValid()); + auto stages = RuntimeStage::DecodeRuntimeStages( + CreateMappingFromAllocation(junk_allocation)); + ASSERT_FALSE(stages[RuntimeStageBackend::kMetal]); } TEST(RuntimeStageTest, CanReadUniforms) { - auto fixture = + const std::shared_ptr fixture = flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr"); ASSERT_TRUE(fixture); ASSERT_GT(fixture->GetSize(), 0u); - RuntimeStage stage(std::move(fixture)); - ASSERT_TRUE(stage.IsValid()); - ASSERT_EQ(stage.GetUniforms().size(), 17u); + auto stages = RuntimeStage::DecodeRuntimeStages(fixture); + auto stage = stages[RuntimeStageBackend::kMetal]; + + ASSERT_TRUE(stage->IsValid()); + ASSERT_EQ(stage->GetUniforms().size(), 17u); { - auto uni = stage.GetUniform("u_color"); + auto uni = stage->GetUniform("u_color"); ASSERT_NE(uni, nullptr); ASSERT_EQ(uni->dimensions.rows, 4u); ASSERT_EQ(uni->dimensions.cols, 1u); @@ -64,7 +68,7 @@ TEST(RuntimeStageTest, CanReadUniforms) { ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); } { - auto uni = stage.GetUniform("u_alpha"); + auto uni = stage->GetUniform("u_alpha"); ASSERT_NE(uni, nullptr); ASSERT_EQ(uni->dimensions.rows, 1u); ASSERT_EQ(uni->dimensions.cols, 1u); @@ -72,7 +76,7 @@ TEST(RuntimeStageTest, CanReadUniforms) { ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); } { - auto uni = stage.GetUniform("u_sparkle_color"); + auto uni = stage->GetUniform("u_sparkle_color"); ASSERT_NE(uni, nullptr); ASSERT_EQ(uni->dimensions.rows, 4u); ASSERT_EQ(uni->dimensions.cols, 1u); @@ -80,7 +84,7 @@ TEST(RuntimeStageTest, CanReadUniforms) { ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); } { - auto uni = stage.GetUniform("u_sparkle_alpha"); + auto uni = stage->GetUniform("u_sparkle_alpha"); ASSERT_NE(uni, nullptr); ASSERT_EQ(uni->dimensions.rows, 1u); ASSERT_EQ(uni->dimensions.cols, 1u); @@ -88,7 +92,7 @@ TEST(RuntimeStageTest, CanReadUniforms) { ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); } { - auto uni = stage.GetUniform("u_blur"); + auto uni = stage->GetUniform("u_blur"); ASSERT_NE(uni, nullptr); ASSERT_EQ(uni->dimensions.rows, 1u); ASSERT_EQ(uni->dimensions.cols, 1u); @@ -96,7 +100,7 @@ TEST(RuntimeStageTest, CanReadUniforms) { ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); } { - auto uni = stage.GetUniform("u_radius_scale"); + auto uni = stage->GetUniform("u_radius_scale"); ASSERT_NE(uni, nullptr); ASSERT_EQ(uni->dimensions.rows, 1u); ASSERT_EQ(uni->dimensions.cols, 1u); @@ -104,7 +108,7 @@ TEST(RuntimeStageTest, CanReadUniforms) { ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); } { - auto uni = stage.GetUniform("u_max_radius"); + auto uni = stage->GetUniform("u_max_radius"); ASSERT_NE(uni, nullptr); ASSERT_EQ(uni->dimensions.rows, 1u); ASSERT_EQ(uni->dimensions.cols, 1u); @@ -112,7 +116,7 @@ TEST(RuntimeStageTest, CanReadUniforms) { ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); } { - auto uni = stage.GetUniform("u_resolution_scale"); + auto uni = stage->GetUniform("u_resolution_scale"); ASSERT_NE(uni, nullptr); ASSERT_EQ(uni->dimensions.rows, 2u); ASSERT_EQ(uni->dimensions.cols, 1u); @@ -120,7 +124,7 @@ TEST(RuntimeStageTest, CanReadUniforms) { ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); } { - auto uni = stage.GetUniform("u_noise_scale"); + auto uni = stage->GetUniform("u_noise_scale"); ASSERT_NE(uni, nullptr); ASSERT_EQ(uni->dimensions.rows, 2u); ASSERT_EQ(uni->dimensions.cols, 1u); @@ -128,7 +132,7 @@ TEST(RuntimeStageTest, CanReadUniforms) { ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); } { - auto uni = stage.GetUniform("u_noise_phase"); + auto uni = stage->GetUniform("u_noise_phase"); ASSERT_NE(uni, nullptr); ASSERT_EQ(uni->dimensions.rows, 1u); ASSERT_EQ(uni->dimensions.cols, 1u); @@ -137,7 +141,7 @@ TEST(RuntimeStageTest, CanReadUniforms) { } { - auto uni = stage.GetUniform("u_circle1"); + auto uni = stage->GetUniform("u_circle1"); ASSERT_NE(uni, nullptr); ASSERT_EQ(uni->dimensions.rows, 2u); ASSERT_EQ(uni->dimensions.cols, 1u); @@ -145,7 +149,7 @@ TEST(RuntimeStageTest, CanReadUniforms) { ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); } { - auto uni = stage.GetUniform("u_circle2"); + auto uni = stage->GetUniform("u_circle2"); ASSERT_NE(uni, nullptr); ASSERT_EQ(uni->dimensions.rows, 2u); ASSERT_EQ(uni->dimensions.cols, 1u); @@ -153,7 +157,7 @@ TEST(RuntimeStageTest, CanReadUniforms) { ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); } { - auto uni = stage.GetUniform("u_circle3"); + auto uni = stage->GetUniform("u_circle3"); ASSERT_NE(uni, nullptr); ASSERT_EQ(uni->dimensions.rows, 2u); ASSERT_EQ(uni->dimensions.cols, 1u); @@ -161,7 +165,7 @@ TEST(RuntimeStageTest, CanReadUniforms) { ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); } { - auto uni = stage.GetUniform("u_rotation1"); + auto uni = stage->GetUniform("u_rotation1"); ASSERT_NE(uni, nullptr); ASSERT_EQ(uni->dimensions.rows, 2u); ASSERT_EQ(uni->dimensions.cols, 1u); @@ -169,7 +173,7 @@ TEST(RuntimeStageTest, CanReadUniforms) { ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); } { - auto uni = stage.GetUniform("u_rotation2"); + auto uni = stage->GetUniform("u_rotation2"); ASSERT_NE(uni, nullptr); ASSERT_EQ(uni->dimensions.rows, 2u); ASSERT_EQ(uni->dimensions.cols, 1u); @@ -177,7 +181,7 @@ TEST(RuntimeStageTest, CanReadUniforms) { ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); } { - auto uni = stage.GetUniform("u_rotation3"); + auto uni = stage->GetUniform("u_rotation3"); ASSERT_NE(uni, nullptr); ASSERT_EQ(uni->dimensions.rows, 2u); ASSERT_EQ(uni->dimensions.cols, 1u); @@ -190,35 +194,36 @@ TEST_P(RuntimeStageTest, CanRegisterStage) { if (GetParam() != PlaygroundBackend::kMetal) { GTEST_SKIP_("Skipped: https://github.com/flutter/flutter/issues/105538"); } - auto fixture = + const std::shared_ptr fixture = flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr"); ASSERT_TRUE(fixture); ASSERT_GT(fixture->GetSize(), 0u); - RuntimeStage stage(std::move(fixture)); - ASSERT_TRUE(stage.IsValid()); + auto stages = RuntimeStage::DecodeRuntimeStages(fixture); + auto stage = stages[RuntimeStageBackend::kMetal]; + ASSERT_TRUE(stage->IsValid()); std::promise registration; auto future = registration.get_future(); auto library = GetContext()->GetShaderLibrary(); library->RegisterFunction( - stage.GetEntrypoint(), // - ToShaderStage(stage.GetShaderStage()), // - stage.GetCodeMapping(), // + stage->GetEntrypoint(), // + ToShaderStage(stage->GetShaderStage()), // + stage->GetCodeMapping(), // fml::MakeCopyable([reg = std::move(registration)](bool result) mutable { reg.set_value(result); })); ASSERT_TRUE(future.get()); { auto function = - library->GetFunction(stage.GetEntrypoint(), ShaderStage::kFragment); + library->GetFunction(stage->GetEntrypoint(), ShaderStage::kFragment); ASSERT_NE(function, nullptr); } // Check if unregistering works. - library->UnregisterFunction(stage.GetEntrypoint(), ShaderStage::kFragment); + library->UnregisterFunction(stage->GetEntrypoint(), ShaderStage::kFragment); { auto function = - library->GetFunction(stage.GetEntrypoint(), ShaderStage::kFragment); + library->GetFunction(stage->GetEntrypoint(), ShaderStage::kFragment); ASSERT_EQ(function, nullptr); } } @@ -227,7 +232,10 @@ TEST_P(RuntimeStageTest, CanCreatePipelineFromRuntimeStage) { if (GetParam() != PlaygroundBackend::kMetal) { GTEST_SKIP_("Skipped: https://github.com/flutter/flutter/issues/105538"); } - auto stage = OpenAssetAsRuntimeStage("ink_sparkle.frag.iplr"); + auto stages = OpenAssetAsRuntimeStage("ink_sparkle.frag.iplr"); + auto stage = stages[RuntimeStageBackend::kMetal]; + + ASSERT_TRUE(stage); ASSERT_NE(stage, nullptr); ASSERT_TRUE(RegisterStage(*stage)); auto library = GetContext()->GetShaderLibrary(); diff --git a/impeller/shader_bundle/shader_bundle.fbs b/impeller/shader_bundle/shader_bundle.fbs index 1c0f4ce038967..ab1e245661342 100644 --- a/impeller/shader_bundle/shader_bundle.fbs +++ b/impeller/shader_bundle/shader_bundle.fbs @@ -8,7 +8,7 @@ namespace impeller.fb; table Shader { name: string; - shader: RuntimeStage; + shader: RuntimeStages; } table ShaderBundle { diff --git a/lib/gpu/shader_library.cc b/lib/gpu/shader_library.cc index 72a39a138824e..69d8caae85a57 100644 --- a/lib/gpu/shader_library.cc +++ b/lib/gpu/shader_library.cc @@ -123,8 +123,26 @@ fml::RefPtr ShaderLibrary::MakeFromFlatbuffer( ShaderLibrary::ShaderMap shader_map; for (const auto* bundled_shader : *bundle->shaders()) { - const impeller::fb::RuntimeStage* runtime_stage = bundled_shader->shader(); - impeller::RuntimeStage stage(runtime_stage); + auto* runtime_stages = bundled_shader->shader(); + + const impeller::fb::RuntimeStage* runtime_stage = nullptr; + auto backend = UIDartState::Current()->GetRuntimeStageBackend(); + switch (backend) { + case impeller::RuntimeStageBackend::kSkSL: + FML_LOG(ERROR) << "Cannot target SkSL"; + return nullptr; + case impeller::RuntimeStageBackend::kMetal: + runtime_stage = runtime_stages->metal(); + break; + case impeller::RuntimeStageBackend::kOpenGLES: + runtime_stage = runtime_stages->opengles(); + break; + case impeller::RuntimeStageBackend::kVulkan: + runtime_stage = runtime_stages->vulkan(); + break; + } + + impeller::RuntimeStage stage(runtime_stage, payload); std::shared_ptr vertex_descriptor = nullptr; if (stage.GetShaderStage() == impeller::RuntimeShaderStage::kVertex) { diff --git a/lib/ui/painting/fragment_program.cc b/lib/ui/painting/fragment_program.cc index 0d2a48364f058..b74f33ab2e7ab 100644 --- a/lib/ui/painting/fragment_program.cc +++ b/lib/ui/painting/fragment_program.cc @@ -15,6 +15,7 @@ #include "flutter/lib/ui/ui_dart_state.h" #include "flutter/lib/ui/window/platform_configuration.h" +#include "impeller/core/runtime_types.h" #include "third_party/skia/include/core/SkString.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_args.h" @@ -26,6 +27,20 @@ namespace flutter { IMPLEMENT_WRAPPERTYPEINFO(ui, FragmentProgram); +static std::string RuntimeStageBackendToString( + impeller::RuntimeStageBackend backend) { + switch (backend) { + case impeller::RuntimeStageBackend::kSkSL: + return "SkSL"; + case impeller::RuntimeStageBackend::kMetal: + return "Metal"; + case impeller::RuntimeStageBackend::kOpenGLES: + return "OpenGLES"; + case impeller::RuntimeStageBackend::kVulkan: + return "Vulkan"; + } +} + std::string FragmentProgram::initFromAsset(const std::string& asset_name) { FML_TRACE_EVENT("flutter", "FragmentProgram::initFromAsset", "asset", asset_name); @@ -38,15 +53,34 @@ std::string FragmentProgram::initFromAsset(const std::string& asset_name) { return std::string("Asset '") + asset_name + std::string("' not found"); } - auto runtime_stage = impeller::RuntimeStage(std::move(data)); - if (!runtime_stage.IsValid()) { + auto runtime_stages = + impeller::RuntimeStage::DecodeRuntimeStages(std::move(data)); + + if (runtime_stages.empty()) { return std::string("Asset '") + asset_name + - std::string("' does not contain valid shader data."); + std::string("' does not contain any shader data."); + } + + auto backend = UIDartState::Current()->GetRuntimeStageBackend(); + auto runtime_stage = runtime_stages[backend]; + if (!runtime_stage) { + std::ostringstream stream; + stream << "Asset '" << asset_name + << "' does not contain appropriate runtime stage data for current " + "backend (" + << RuntimeStageBackendToString(backend) << ")." << std::endl + << "Found stages: "; + for (const auto& kvp : runtime_stages) { + if (kvp.second) { + stream << RuntimeStageBackendToString(kvp.first) << " "; + } + } + return stream.str(); } int sampled_image_count = 0; size_t other_uniforms_bytes = 0; - for (const auto& uniform_description : runtime_stage.GetUniforms()) { + for (const auto& uniform_description : runtime_stage->GetUniforms()) { if (uniform_description.type == impeller::RuntimeUniformType::kSampledImage) { sampled_image_count++; @@ -56,10 +90,9 @@ std::string FragmentProgram::initFromAsset(const std::string& asset_name) { } if (UIDartState::Current()->IsImpellerEnabled()) { - runtime_effect_ = DlRuntimeEffect::MakeImpeller( - std::make_unique(std::move(runtime_stage))); + runtime_effect_ = DlRuntimeEffect::MakeImpeller(std::move(runtime_stage)); } else { - const auto& code_mapping = runtime_stage.GetSkSLMapping(); + const auto& code_mapping = runtime_stage->GetCodeMapping(); auto code_size = code_mapping->GetSize(); const char* sksl = reinterpret_cast(code_mapping->GetMapping()); diff --git a/lib/ui/ui_dart_state.cc b/lib/ui/ui_dart_state.cc index 4af7f0837d81e..8c95f6ab3b7ad 100644 --- a/lib/ui/ui_dart_state.cc +++ b/lib/ui/ui_dart_state.cc @@ -40,7 +40,8 @@ UIDartState::Context::Context( std::string advisory_script_entrypoint, std::shared_ptr volatile_path_tracker, std::shared_ptr concurrent_task_runner, - bool enable_impeller) + bool enable_impeller, + impeller::RuntimeStageBackend runtime_stage_backend) : task_runners(task_runners), snapshot_delegate(std::move(snapshot_delegate)), io_manager(std::move(io_manager)), @@ -51,7 +52,8 @@ UIDartState::Context::Context( advisory_script_entrypoint(std::move(advisory_script_entrypoint)), volatile_path_tracker(std::move(volatile_path_tracker)), concurrent_task_runner(std::move(concurrent_task_runner)), - enable_impeller(enable_impeller) {} + enable_impeller(enable_impeller), + runtime_stage_backend(runtime_stage_backend) {} UIDartState::UIDartState( TaskObserverAdd add_callback, @@ -85,6 +87,10 @@ bool UIDartState::IsImpellerEnabled() const { return context_.enable_impeller; } +impeller::RuntimeStageBackend UIDartState::GetRuntimeStageBackend() const { + return context_.runtime_stage_backend; +} + void UIDartState::DidSetIsolate() { main_port_ = Dart_GetMainPortId(); std::ostringstream debug_name; diff --git a/lib/ui/ui_dart_state.h b/lib/ui/ui_dart_state.h index 63824a552eee0..7c9019582d96e 100644 --- a/lib/ui/ui_dart_state.h +++ b/lib/ui/ui_dart_state.h @@ -20,6 +20,7 @@ #include "flutter/lib/ui/snapshot_delegate.h" #include "flutter/lib/ui/volatile_path_tracker.h" #include "flutter/shell/common/platform_message_handler.h" +#include "impeller/runtime_stage/runtime_stage.h" #include "third_party/dart/runtime/include/dart_api.h" #include "third_party/skia/include/gpu/GrDirectContext.h" #include "third_party/tonic/dart_microtask_queue.h" @@ -54,7 +55,8 @@ class UIDartState : public tonic::DartState { std::string advisory_script_entrypoint, std::shared_ptr volatile_path_tracker, std::shared_ptr concurrent_task_runner, - bool enable_impeller); + bool enable_impeller, + impeller::RuntimeStageBackend runtime_stage_backend); /// The task runners used by the shell hosting this runtime controller. This /// may be used by the isolate to scheduled asynchronous texture uploads or @@ -99,6 +101,9 @@ class UIDartState : public tonic::DartState { /// Whether Impeller is enabled or not. bool enable_impeller = false; + + /// The expected backend for runtime stage shaders. + impeller::RuntimeStageBackend runtime_stage_backend; }; Dart_Port main_port() const { return main_port_; } @@ -163,6 +168,9 @@ class UIDartState : public tonic::DartState { /// Whether Impeller is enabled for this application. bool IsImpellerEnabled() const; + /// The expected type for runtime stage shaders. + impeller::RuntimeStageBackend GetRuntimeStageBackend() const; + protected: UIDartState(TaskObserverAdd add_callback, TaskObserverRemove remove_callback, diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 8d43b263e4597..71ac779e881eb 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -63,7 +63,7 @@ std::unique_ptr RuntimeController::Spawn( std::move(image_decoder), std::move(image_generator_registry), std::move(advisory_script_uri), std::move(advisory_script_entrypoint), context_.volatile_path_tracker, context_.concurrent_task_runner, - context_.enable_impeller}; + context_.enable_impeller, context_.runtime_stage_backend}; auto result = std::make_unique(p_client, // vm_, // diff --git a/shell/common/engine.cc b/shell/common/engine.cc index ea0cb43d9be18..3cc83228feea8 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -18,6 +18,7 @@ #include "flutter/shell/common/animator.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/shell.h" +#include "impeller/runtime_stage/runtime_stage.h" #include "rapidjson/document.h" #include "third_party/dart/runtime/include/dart_tools_api.h" @@ -74,7 +75,8 @@ Engine::Engine(Delegate& delegate, fml::RefPtr unref_queue, fml::TaskRunnerAffineWeakPtr snapshot_delegate, std::shared_ptr volatile_path_tracker, - const std::shared_ptr& gpu_disabled_switch) + const std::shared_ptr& gpu_disabled_switch, + impeller::RuntimeStageBackend runtime_stage_type) : Engine(delegate, dispatcher_maker, vm.GetConcurrentWorkerTaskRunner(), @@ -106,6 +108,7 @@ Engine::Engine(Delegate& delegate, std::move(volatile_path_tracker), // volatile path tracker vm.GetConcurrentWorkerTaskRunner(), // concurrent task runner settings_.enable_impeller, // enable impeller + runtime_stage_type, // runtime stage type }); } diff --git a/shell/common/engine.h b/shell/common/engine.h index ec6f55e5d7673..4a4b3318b149a 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -399,7 +399,9 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { fml::RefPtr unref_queue, fml::TaskRunnerAffineWeakPtr snapshot_delegate, std::shared_ptr volatile_path_tracker, - const std::shared_ptr& gpu_disabled_switch); + const std::shared_ptr& gpu_disabled_switch, + impeller::RuntimeStageBackend runtime_stage_type = + impeller::RuntimeStageBackend::kSkSL); //---------------------------------------------------------------------------- /// @brief Create a Engine that shares as many resources as diff --git a/shell/common/fixtures/shell_test.dart b/shell/common/fixtures/shell_test.dart index f42f0aab0d399..19e91c63869e8 100644 --- a/shell/common/fixtures/shell_test.dart +++ b/shell/common/fixtures/shell_test.dart @@ -9,6 +9,11 @@ import 'dart:ui'; void main() {} +@pragma('vm:entry-point') +void mainNotifyNative() { + notifyNative(); +} + @pragma('vm:external-name', 'NativeReportTimingsCallback') external void nativeReportTimingsCallback(List timings); @pragma('vm:external-name', 'NativeOnBeginFrame') diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 697155869cade..354881d26fe07 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -28,6 +28,7 @@ #include "flutter/shell/common/skia_event_tracer_impl.h" #include "flutter/shell/common/switches.h" #include "flutter/shell/common/vsync_waiter.h" +#include "impeller/runtime_stage/runtime_stage.h" #include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" #include "third_party/dart/runtime/include/dart_tools_api.h" @@ -64,7 +65,8 @@ std::unique_ptr CreateEngine( const fml::RefPtr& unref_queue, const fml::TaskRunnerAffineWeakPtr& snapshot_delegate, const std::shared_ptr& volatile_path_tracker, - const std::shared_ptr& gpu_disabled_switch) { + const std::shared_ptr& gpu_disabled_switch, + impeller::RuntimeStageBackend runtime_stage_backend) { return std::make_unique(delegate, // dispatcher_maker, // vm, // @@ -77,7 +79,8 @@ std::unique_ptr CreateEngine( unref_queue, // snapshot_delegate, // volatile_path_tracker, // - gpu_disabled_switch); + gpu_disabled_switch, // + runtime_stage_backend); } void RegisterCodecsWithSkia() { @@ -191,6 +194,21 @@ std::unique_ptr Shell::Create( CreateEngine, is_gpu_disabled); } +static impeller::RuntimeStageBackend DetermineRuntimeStageBackend( + const std::shared_ptr& impeller_context) { + if (!impeller_context) { + return impeller::RuntimeStageBackend::kSkSL; + } + switch (impeller_context->GetBackendType()) { + case impeller::Context::BackendType::kMetal: + return impeller::RuntimeStageBackend::kMetal; + case impeller::Context::BackendType::kOpenGLES: + return impeller::RuntimeStageBackend::kOpenGLES; + case impeller::Context::BackendType::kVulkan: + return impeller::RuntimeStageBackend::kVulkan; + } +} + std::unique_ptr Shell::CreateShellOnPlatformThread( DartVMRef vm, fml::RefPtr parent_merger, @@ -312,7 +330,9 @@ std::unique_ptr Shell::CreateShellOnPlatformThread( &weak_io_manager_future, // &snapshot_delegate_future, // &unref_queue_future, // - &on_create_engine]() mutable { + &on_create_engine, + runtime_stage_backend = DetermineRuntimeStageBackend( + platform_view->GetImpellerContext())]() mutable { TRACE_EVENT0("flutter", "ShellSetupUISubsystem"); const auto& task_runners = shell->GetTaskRunners(); @@ -321,20 +341,22 @@ std::unique_ptr Shell::CreateShellOnPlatformThread( auto animator = std::make_unique(*shell, task_runners, std::move(vsync_waiter)); - engine_promise.set_value( - on_create_engine(*shell, // - dispatcher_maker, // - *shell->GetDartVM(), // - std::move(isolate_snapshot), // - task_runners, // - platform_data, // - shell->GetSettings(), // - std::move(animator), // - weak_io_manager_future.get(), // - unref_queue_future.get(), // - snapshot_delegate_future.get(), // - shell->volatile_path_tracker_, - shell->is_gpu_disabled_sync_switch_)); + engine_promise.set_value(on_create_engine( + *shell, // + dispatcher_maker, // + *shell->GetDartVM(), // + std::move(isolate_snapshot), // + task_runners, // + platform_data, // + shell->GetSettings(), // + std::move(animator), // + weak_io_manager_future.get(), // + unref_queue_future.get(), // + snapshot_delegate_future.get(), // + shell->volatile_path_tracker_, // + shell->is_gpu_disabled_sync_switch_, // + runtime_stage_backend // + )); })); if (!shell->Setup(std::move(platform_view), // @@ -574,7 +596,8 @@ std::unique_ptr Shell::Spawn( const fml::RefPtr& unref_queue, fml::TaskRunnerAffineWeakPtr snapshot_delegate, const std::shared_ptr& volatile_path_tracker, - const std::shared_ptr& is_gpu_disabled_sync_switch) { + const std::shared_ptr& is_gpu_disabled_sync_switch, + impeller::RuntimeStageBackend runtime_stage_backend) { return engine->Spawn( /*delegate=*/delegate, /*dispatcher_maker=*/dispatcher_maker, diff --git a/shell/common/shell.h b/shell/common/shell.h index 2c2ead74cf46a..14d3dae22a5b1 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -40,6 +40,7 @@ #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/resource_cache_limit_calculator.h" #include "flutter/shell/common/shell_io_manager.h" +#include "impeller/runtime_stage/runtime_stage.h" namespace flutter { @@ -129,7 +130,8 @@ class Shell final : public PlatformView::Delegate, fml::RefPtr unref_queue, fml::TaskRunnerAffineWeakPtr snapshot_delegate, std::shared_ptr volatile_path_tracker, - const std::shared_ptr& gpu_disabled_switch)> + const std::shared_ptr& gpu_disabled_switch, + impeller::RuntimeStageBackend runtime_stage_type)> EngineCreateCallback; //---------------------------------------------------------------------------- diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 753381a8ff22a..556d0ae422466 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include +#include "impeller/core/runtime_types.h" #define FML_USED_ON_EMBEDDER #include @@ -4564,6 +4565,96 @@ TEST_F(ShellTest, ShellFlushesPlatformStatesByMain) { DestroyShell(std::move(shell), task_runners); } +TEST_F(ShellTest, RuntimeStageBackendDefaultsToSkSLWithoutImpeller) { + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); + Settings settings = CreateSettingsForFixture(); + settings.enable_impeller = false; + ThreadHost thread_host(ThreadHost::ThreadHostConfig( + "io.flutter.test." + GetCurrentTestName() + ".", + ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster | + ThreadHost::Type::kIo | ThreadHost::Type::kUi)); + TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(), + thread_host.raster_thread->GetTaskRunner(), + thread_host.ui_thread->GetTaskRunner(), + thread_host.io_thread->GetTaskRunner()); + std::unique_ptr shell = CreateShell(settings, task_runners); + ASSERT_TRUE(shell); + + fml::AutoResetWaitableEvent latch; + AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&latch](auto args) { + auto backend = + UIDartState::Current()->GetRuntimeStageBackend(); + EXPECT_EQ(backend, impeller::RuntimeStageBackend::kSkSL); + latch.Signal(); + })); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("mainNotifyNative"); + RunEngine(shell.get(), std::move(configuration)); + + latch.Wait(); + + DestroyShell(std::move(shell), task_runners); +} + +// TODO(dnfield): Enable GL and Vulkan after +// https://github.com/flutter/flutter/issues/140419 +#if SHELL_ENABLE_METAL +TEST_F(ShellTest, RuntimeStageBackendWithImpeller) { + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); + Settings settings = CreateSettingsForFixture(); + settings.enable_impeller = true; + ThreadHost thread_host(ThreadHost::ThreadHostConfig( + "io.flutter.test." + GetCurrentTestName() + ".", + ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster | + ThreadHost::Type::kIo | ThreadHost::Type::kUi)); + TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(), + thread_host.raster_thread->GetTaskRunner(), + thread_host.ui_thread->GetTaskRunner(), + thread_host.io_thread->GetTaskRunner()); + std::unique_ptr shell = CreateShell(settings, task_runners); + ASSERT_TRUE(shell); + + fml::AutoResetWaitableEvent latch; + + impeller::Context::BackendType impeller_backend; + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetPlatformTaskRunner(), + [platform_view = shell->GetPlatformView(), &latch, &impeller_backend]() { + auto impeller_context = platform_view->GetImpellerContext(); + EXPECT_TRUE(impeller_context); + impeller_backend = impeller_context->GetBackendType(); + latch.Signal(); + }); + latch.Wait(); + + AddNativeCallback( + "NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) { + auto backend = UIDartState::Current()->GetRuntimeStageBackend(); + switch (impeller_backend) { + case impeller::Context::BackendType::kMetal: + EXPECT_EQ(backend, impeller::RuntimeStageBackend::kMetal); + break; + case impeller::Context::BackendType::kOpenGLES: + EXPECT_EQ(backend, impeller::RuntimeStageBackend::kOpenGLES); + break; + case impeller::Context::BackendType::kVulkan: + EXPECT_EQ(backend, impeller::RuntimeStageBackend::kVulkan); + break; + } + latch.Signal(); + })); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("mainNotifyNative"); + RunEngine(shell.get(), std::move(configuration)); + + latch.Wait(); + + DestroyShell(std::move(shell), task_runners); +} +#endif // SHELL_ENABLE_METAL + } // namespace testing } // namespace flutter diff --git a/testing/dart/fragment_shader_test.dart b/testing/dart/fragment_shader_test.dart index 7da7dfc2de51c..386c83f7331bb 100644 --- a/testing/dart/fragment_shader_test.dart +++ b/testing/dart/fragment_shader_test.dart @@ -30,10 +30,13 @@ void main() async { expect(rawData is Map, true); final Map data = rawData! as Map; - expect(data['sksl'] is String, true); - expect(data['uniforms'] is List, true); + expect(data.keys.toList(), ['sksl']); + expect(data['sksl'] is Map, true); - final Object? rawUniformData = (data['uniforms']! as List)[0]; + final Map skslData = data['sksl']! as Map; + expect(skslData['uniforms'] is List, true); + + final Object? rawUniformData = (skslData['uniforms']! as List)[0]; expect(rawUniformData is Map, true); @@ -42,6 +45,11 @@ void main() async { expect(uniformData['location'] is int, true); }); + if (impellerEnabled) { + // https://github.com/flutter/flutter/issues/122823 + return; + } + test('FragmentShader setSampler throws with out-of-bounds index', () async { final FragmentProgram program = await FragmentProgram.fromAsset( 'blue_green_sampler.frag.iplr', diff --git a/testing/dart/observatory/shader_reload_test.dart b/testing/dart/observatory/shader_reload_test.dart index ff2280ed271f0..734b36f559ea1 100644 --- a/testing/dart/observatory/shader_reload_test.dart +++ b/testing/dart/observatory/shader_reload_test.dart @@ -9,8 +9,14 @@ import 'package:litetest/litetest.dart'; import 'package:vm_service/vm_service.dart' as vms; import 'package:vm_service/vm_service_io.dart'; +import '../impeller_enabled.dart'; + void main() { test('simple iplr shader can be re-initialized', () async { + if (impellerEnabled) { + // Needs https://github.com/flutter/flutter/issues/129659 + return; + } vms.VmService? vmService; FragmentShader? shader; try {