From 4884d227b6fc0777c7b3c16545a78f6df0b89e0b Mon Sep 17 00:00:00 2001 From: Dan Field Date: Mon, 18 Dec 2023 13:49:54 -0800 Subject: [PATCH 01/10] [Impeller] Make IPLR files multi-platform This is part of the work towards supporting OpenGLES and Vulkan for runtime stage shaders. Removes some redundant work we had around SkSL. Now only bundles the shaders we actually ask for from the command line. @bdero, we should figure out if this is the right approach for flutter_gpu. --- impeller/aiks/aiks_unittests.cc | 8 +- impeller/compiler/impellerc_main.cc | 50 ++- impeller/compiler/reflector.cc | 26 +- impeller/compiler/reflector.h | 6 +- impeller/compiler/runtime_stage_data.cc | 355 ++++++++---------- impeller/compiler/runtime_stage_data.h | 37 +- impeller/compiler/shader_bundle.cc | 28 +- impeller/compiler/shader_bundle_unittests.cc | 26 +- impeller/core/runtime_types.h | 7 + impeller/entity/entity_unittests.cc | 4 +- .../golden_tests/golden_playground_test.h | 3 +- .../golden_playground_test_mac.cc | 13 +- .../golden_playground_test_stub.cc | 5 +- .../playground/compute_playground_test.cc | 13 - impeller/playground/compute_playground_test.h | 3 - impeller/playground/playground_test.cc | 13 +- impeller/playground/playground_test.h | 3 +- impeller/runtime_stage/runtime_stage.cc | 61 +-- impeller/runtime_stage/runtime_stage.fbs | 2 +- impeller/runtime_stage/runtime_stage.h | 19 +- .../runtime_stage/runtime_stage_types.fbs | 16 +- .../runtime_stage/runtime_stage_unittests.cc | 82 ++-- impeller/shader_bundle/shader_bundle.fbs | 2 +- lib/gpu/shader_library.cc | 22 +- lib/ui/painting/fragment_program.cc | 24 +- lib/ui/ui_dart_state.cc | 10 +- lib/ui/ui_dart_state.h | 10 +- runtime/runtime_controller.cc | 2 +- shell/common/engine.cc | 5 +- shell/common/engine.h | 4 +- shell/common/fixtures/shell_test.dart | 5 + shell/common/shell.cc | 59 ++- shell/common/shell.h | 4 +- shell/common/shell_unittests.cc | 73 ++++ testing/dart/fragment_shader_test.dart | 9 +- 35 files changed, 591 insertions(+), 418 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 997d5ffe30f76..d62acd9389819 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 5763d5561ae36..7cd81b4e55c28 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -2127,8 +2127,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 befe93b0a4cec..c9788148d133f 100644 --- a/impeller/golden_tests/golden_playground_test_mac.cc +++ b/impeller/golden_tests/golden_playground_test_mac.cc @@ -165,17 +165,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..b7dfbfb9cce41 100644 --- a/lib/ui/painting/fragment_program.cc +++ b/lib/ui/painting/fragment_program.cc @@ -38,15 +38,26 @@ 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 any shader data."); + } + + auto backend = UIDartState::Current()->GetRuntimeStageBackend(); + auto runtime_stage = std::move(runtime_stages[backend]); + if (!runtime_stage) { return std::string("Asset '") + asset_name + - std::string("' does not contain valid shader data."); + std::string( + "' does not contain appropriate runtime stage data for current " + "backend."); } 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 +67,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 be5cdabad5195..5e65dd3e3d41b 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 1dcf9d4b128ee..038df0a093746 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 3922d4a56e84c..f5c8524bec75d 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 8d96938fe6031..5cc2df586b981 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 9d04138566306..fb7733ec00f69 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 @@ -4590,6 +4591,78 @@ 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); +} + +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; + AddNativeCallback( + "NotifyNative", CREATE_NATIVE_ENTRY([&latch](auto args) { + auto backend = UIDartState::Current()->GetRuntimeStageBackend(); +#ifdef SHELL_ENABLE_GL + EXPECT_EQ(backend, impeller::RuntimeStageBackend::kOpenGLES); +#endif // SHELL_ENABLE_GL +#ifdef SHELL_ENABLE_VULKAN + EXPECT_EQ(backend, impeller::RuntimeStageBackend::kVulkan); +#endif // SHELL_ENABLE_VULKAN +#ifdef SHELL_ENABLE_METAL + EXPECT_EQ(backend, impeller::RuntimeStageBackend::kMetal); +#endif // SHELL_ENABLE_METAL + latch.Signal(); + })); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("mainNotifyNative"); + RunEngine(shell.get(), std::move(configuration)); + + latch.Wait(); + + DestroyShell(std::move(shell), task_runners); +} + } // namespace testing } // namespace flutter diff --git a/testing/dart/fragment_shader_test.dart b/testing/dart/fragment_shader_test.dart index da34695df7814..1b69f984684b0 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 skslData = data['sksl'] as Map; + expect(skslData['uniforms'] is List, true); + + final Object? rawUniformData = (skslData['uniforms']! as List)[0]; expect(rawUniformData is Map, true); From 70bcea2f5ac71f8474264efa74d33e10e5a4c7ff Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 19 Dec 2023 13:15:48 -0800 Subject: [PATCH 02/10] improve error message, skip for Vulkan --- lib/ui/painting/fragment_program.cc | 33 ++++++++++++++++--- shell/common/shell_unittests.cc | 5 ++- .../dart/observatory/shader_reload_test.dart | 4 ++- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/lib/ui/painting/fragment_program.cc b/lib/ui/painting/fragment_program.cc index b7dfbfb9cce41..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); @@ -47,12 +62,20 @@ std::string FragmentProgram::initFromAsset(const std::string& asset_name) { } auto backend = UIDartState::Current()->GetRuntimeStageBackend(); - auto runtime_stage = std::move(runtime_stages[backend]); + auto runtime_stage = runtime_stages[backend]; if (!runtime_stage) { - return std::string("Asset '") + asset_name + - std::string( - "' does not contain appropriate runtime stage data for current " - "backend."); + 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; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index fb7733ec00f69..25e4bb600fe66 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -4646,7 +4646,10 @@ TEST_F(ShellTest, RuntimeStageBackendWithImpeller) { EXPECT_EQ(backend, impeller::RuntimeStageBackend::kOpenGLES); #endif // SHELL_ENABLE_GL #ifdef SHELL_ENABLE_VULKAN - EXPECT_EQ(backend, impeller::RuntimeStageBackend::kVulkan); +// TODO(dnfield): Enable this after +// https://github.com/flutter/flutter/issues/129659 + +// EXPECT_EQ(backend, impeller::RuntimeStageBackend::kVulkan); #endif // SHELL_ENABLE_VULKAN #ifdef SHELL_ENABLE_METAL EXPECT_EQ(backend, impeller::RuntimeStageBackend::kMetal); diff --git a/testing/dart/observatory/shader_reload_test.dart b/testing/dart/observatory/shader_reload_test.dart index ff2280ed271f0..cea5fad194ded 100644 --- a/testing/dart/observatory/shader_reload_test.dart +++ b/testing/dart/observatory/shader_reload_test.dart @@ -9,6 +9,8 @@ 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 { vms.VmService? vmService; @@ -46,7 +48,7 @@ void main() { await vmService?.dispose(); shader?.dispose(); } - }); + }, skip: impellerEnabled); // Needs https://github.com/flutter/flutter/issues/129659 } void _use(Shader shader) { From 263c2eb069be6ac2682e5b9f15897c018b54f2be Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 19 Dec 2023 14:32:32 -0800 Subject: [PATCH 03/10] Only metal for now --- shell/common/shell_unittests.cc | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 25e4bb600fe66..a76474d630056 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -4639,23 +4639,22 @@ TEST_F(ShellTest, RuntimeStageBackendWithImpeller) { ASSERT_TRUE(shell); fml::AutoResetWaitableEvent latch; - AddNativeCallback( - "NotifyNative", CREATE_NATIVE_ENTRY([&latch](auto args) { - auto backend = UIDartState::Current()->GetRuntimeStageBackend(); + AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&latch](auto args) { + auto backend = + UIDartState::Current()->GetRuntimeStageBackend(); + // TODO(dnfield): Enable GL and Vulkan after + // https://github.com/flutter/flutter/issues/140419 #ifdef SHELL_ENABLE_GL - EXPECT_EQ(backend, impeller::RuntimeStageBackend::kOpenGLES); + // EXPECT_EQ(backend, impeller::RuntimeStageBackend::kOpenGLES); #endif // SHELL_ENABLE_GL #ifdef SHELL_ENABLE_VULKAN -// TODO(dnfield): Enable this after -// https://github.com/flutter/flutter/issues/129659 - -// EXPECT_EQ(backend, impeller::RuntimeStageBackend::kVulkan); + // EXPECT_EQ(backend, impeller::RuntimeStageBackend::kVulkan); #endif // SHELL_ENABLE_VULKAN #ifdef SHELL_ENABLE_METAL - EXPECT_EQ(backend, impeller::RuntimeStageBackend::kMetal); + EXPECT_EQ(backend, impeller::RuntimeStageBackend::kMetal); #endif // SHELL_ENABLE_METAL - latch.Signal(); - })); + latch.Signal(); + })); auto configuration = RunConfiguration::InferFromSettings(settings); configuration.SetEntrypoint("mainNotifyNative"); From d1a309ea03f11d441ab4d7737055c82fcaf13ef5 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 19 Dec 2023 19:25:27 -0800 Subject: [PATCH 04/10] fix test --- shell/common/shell_unittests.cc | 46 +++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index a76474d630056..02ac82f69e37f 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -4639,22 +4639,40 @@ TEST_F(ShellTest, RuntimeStageBackendWithImpeller) { ASSERT_TRUE(shell); fml::AutoResetWaitableEvent latch; - AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY([&latch](auto args) { - auto backend = - UIDartState::Current()->GetRuntimeStageBackend(); + + std::optional impeller_backend; + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetPlatformTaskRunner(), + [platform_view = shell->GetPlatformView(), &latch, &impeller_backend]() { + auto impeller_context = platform_view->GetImpellerContext(); // TODO(dnfield): Enable GL and Vulkan after // https://github.com/flutter/flutter/issues/140419 -#ifdef SHELL_ENABLE_GL - // EXPECT_EQ(backend, impeller::RuntimeStageBackend::kOpenGLES); -#endif // SHELL_ENABLE_GL -#ifdef SHELL_ENABLE_VULKAN - // EXPECT_EQ(backend, impeller::RuntimeStageBackend::kVulkan); -#endif // SHELL_ENABLE_VULKAN -#ifdef SHELL_ENABLE_METAL - EXPECT_EQ(backend, impeller::RuntimeStageBackend::kMetal); -#endif // SHELL_ENABLE_METAL - latch.Signal(); - })); +#if SHELL_ENABLE_METAL + EXPECT_TRUE(impeller_context); + impeller_backend = impeller_context->GetBackendType(); +#endif + latch.Signal(); + }); + latch.Wait(); + + AddNativeCallback( + "NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) { + auto backend = UIDartState::Current()->GetRuntimeStageBackend(); + if (impeller_backend.has_value()) { + switch (impeller_backend.value()) { + 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"); From 7398e603b5636a1d85649db9998cec836dbe45fb Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 19 Dec 2023 19:47:35 -0800 Subject: [PATCH 05/10] fix build --- shell/common/shell_unittests.cc | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 02ac82f69e37f..2aa7872977e79 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -4623,6 +4623,9 @@ TEST_F(ShellTest, RuntimeStageBackendDefaultsToSkSLWithoutImpeller) { 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(); @@ -4640,17 +4643,13 @@ TEST_F(ShellTest, RuntimeStageBackendWithImpeller) { fml::AutoResetWaitableEvent latch; - std::optional impeller_backend; + impeller::Context::BackendType impeller_backend; fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [platform_view = shell->GetPlatformView(), &latch, &impeller_backend]() { auto impeller_context = platform_view->GetImpellerContext(); - // TODO(dnfield): Enable GL and Vulkan after - // https://github.com/flutter/flutter/issues/140419 -#if SHELL_ENABLE_METAL EXPECT_TRUE(impeller_context); impeller_backend = impeller_context->GetBackendType(); -#endif latch.Signal(); }); latch.Wait(); @@ -4658,18 +4657,16 @@ TEST_F(ShellTest, RuntimeStageBackendWithImpeller) { AddNativeCallback( "NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) { auto backend = UIDartState::Current()->GetRuntimeStageBackend(); - if (impeller_backend.has_value()) { - switch (impeller_backend.value()) { - 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; - } + 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(); })); @@ -4682,6 +4679,7 @@ TEST_F(ShellTest, RuntimeStageBackendWithImpeller) { DestroyShell(std::move(shell), task_runners); } +#endif // SHELL_ENABLE_METAL } // namespace testing } // namespace flutter From b763666e69df82d3fab589042a30cdc140f89cc5 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 20 Dec 2023 12:42:24 -0800 Subject: [PATCH 06/10] skip some more tests now that FragmentProgram.fromAsset does more validation --- testing/dart/fragment_shader_test.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/testing/dart/fragment_shader_test.dart b/testing/dart/fragment_shader_test.dart index 1b69f984684b0..c1003caeadb47 100644 --- a/testing/dart/fragment_shader_test.dart +++ b/testing/dart/fragment_shader_test.dart @@ -45,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', From e124c62a0fa3465f67407276d30a8a3d47cfa8fd Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 20 Dec 2023 14:47:32 -0800 Subject: [PATCH 07/10] Safe skip --- testing/dart/observatory/shader_reload_test.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/testing/dart/observatory/shader_reload_test.dart b/testing/dart/observatory/shader_reload_test.dart index cea5fad194ded..734b36f559ea1 100644 --- a/testing/dart/observatory/shader_reload_test.dart +++ b/testing/dart/observatory/shader_reload_test.dart @@ -13,6 +13,10 @@ 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 { @@ -48,7 +52,7 @@ void main() { await vmService?.dispose(); shader?.dispose(); } - }, skip: impellerEnabled); // Needs https://github.com/flutter/flutter/issues/129659 + }); } void _use(Shader shader) { From ef099534f21971459f6405a398b37857a84f0ae0 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 20 Dec 2023 17:27:11 -0800 Subject: [PATCH 08/10] more --- testing/dart/fragment_shader_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/dart/fragment_shader_test.dart b/testing/dart/fragment_shader_test.dart index 727fce2bde79e..be3c07e42f312 100644 --- a/testing/dart/fragment_shader_test.dart +++ b/testing/dart/fragment_shader_test.dart @@ -33,7 +33,7 @@ void main() async { expect(data.keys.toList(), ['sksl']); expect(data['sksl'] is Map, true); - final skslData = data['sksl'] as Map; + final Map skslData = data['sksl'] as Map!; expect(skslData['uniforms'] is List, true); final Object? rawUniformData = (skslData['uniforms']! as List)[0]; From e9239577d69e093c764891a1bcfef3ed31fd324b Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 20 Dec 2023 20:54:51 -0800 Subject: [PATCH 09/10] I can dart... --- testing/dart/fragment_shader_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/dart/fragment_shader_test.dart b/testing/dart/fragment_shader_test.dart index be3c07e42f312..b662bb45976ec 100644 --- a/testing/dart/fragment_shader_test.dart +++ b/testing/dart/fragment_shader_test.dart @@ -33,7 +33,7 @@ void main() async { expect(data.keys.toList(), ['sksl']); expect(data['sksl'] is Map, true); - final Map skslData = data['sksl'] as Map!; + final Map skslData = (data['sksl'] as Map)!; expect(skslData['uniforms'] is List, true); final Object? rawUniformData = (skslData['uniforms']! as List)[0]; From c0b17b76e2682b9aeb43c3e85db036f1f70b4541 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 20 Dec 2023 20:55:25 -0800 Subject: [PATCH 10/10] more darty --- testing/dart/fragment_shader_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/dart/fragment_shader_test.dart b/testing/dart/fragment_shader_test.dart index b662bb45976ec..386c83f7331bb 100644 --- a/testing/dart/fragment_shader_test.dart +++ b/testing/dart/fragment_shader_test.dart @@ -33,7 +33,7 @@ void main() async { expect(data.keys.toList(), ['sksl']); expect(data['sksl'] is Map, true); - final Map skslData = (data['sksl'] as Map)!; + final Map skslData = data['sksl']! as Map; expect(skslData['uniforms'] is List, true); final Object? rawUniformData = (skslData['uniforms']! as List)[0];