diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 9a611a744c074..b2e65c0317906 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1214,6 +1214,8 @@ FILE: ../../../flutter/impeller/entity/contents/radial_gradient_contents.cc FILE: ../../../flutter/impeller/entity/contents/radial_gradient_contents.h FILE: ../../../flutter/impeller/entity/contents/rrect_shadow_contents.cc FILE: ../../../flutter/impeller/entity/contents/rrect_shadow_contents.h +FILE: ../../../flutter/impeller/entity/contents/runtime_effect_contents.cc +FILE: ../../../flutter/impeller/entity/contents/runtime_effect_contents.h FILE: ../../../flutter/impeller/entity/contents/solid_color_contents.cc FILE: ../../../flutter/impeller/entity/contents/solid_color_contents.h FILE: ../../../flutter/impeller/entity/contents/solid_stroke_contents.cc @@ -1280,6 +1282,7 @@ FILE: ../../../flutter/impeller/entity/shaders/morphology_filter.frag FILE: ../../../flutter/impeller/entity/shaders/morphology_filter.vert FILE: ../../../flutter/impeller/entity/shaders/position.vert FILE: ../../../flutter/impeller/entity/shaders/position_color.vert +FILE: ../../../flutter/impeller/entity/shaders/position_no_color.vert FILE: ../../../flutter/impeller/entity/shaders/position_uv.vert FILE: ../../../flutter/impeller/entity/shaders/radial_gradient_fill.frag FILE: ../../../flutter/impeller/entity/shaders/rrect_blur.frag @@ -1555,6 +1558,7 @@ FILE: ../../../flutter/impeller/runtime_stage/runtime_stage.h FILE: ../../../flutter/impeller/runtime_stage/runtime_stage_playground.cc FILE: ../../../flutter/impeller/runtime_stage/runtime_stage_playground.h FILE: ../../../flutter/impeller/runtime_stage/runtime_stage_unittests.cc +FILE: ../../../flutter/impeller/runtime_stage/runtime_types.cc FILE: ../../../flutter/impeller/runtime_stage/runtime_types.h FILE: ../../../flutter/impeller/tessellator/c/tessellator.cc FILE: ../../../flutter/impeller/tessellator/c/tessellator.h diff --git a/impeller/display_list/display_list_dispatcher.cc b/impeller/display_list/display_list_dispatcher.cc index eb0e292ff6aa3..3d914a50bad74 100644 --- a/impeller/display_list/display_list_dispatcher.cc +++ b/impeller/display_list/display_list_dispatcher.cc @@ -5,11 +5,14 @@ #include "impeller/display_list/display_list_dispatcher.h" #include +#include +#include #include #include #include "display_list/display_list_blend_mode.h" #include "display_list/display_list_color_filter.h" +#include "display_list/display_list_color_source.h" #include "display_list/display_list_path_effect.h" #include "display_list/display_list_tile_mode.h" #include "flutter/fml/logging.h" @@ -21,6 +24,7 @@ #include "impeller/entity/contents/filters/inputs/filter_input.h" #include "impeller/entity/contents/linear_gradient_contents.h" #include "impeller/entity/contents/radial_gradient_contents.h" +#include "impeller/entity/contents/runtime_effect_contents.h" #include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/contents/sweep_gradient_contents.h" #include "impeller/entity/contents/tiled_texture_contents.h" @@ -425,8 +429,28 @@ void DisplayListDispatcher::setColorSource( }; return; } + case flutter::DlColorSourceType::kRuntimeEffect: { + const flutter::DlRuntimeEffectColorSource* runtime_effect_color_source = + source->asRuntimeEffect(); + auto runtime_stage = + runtime_effect_color_source->runtime_effect()->runtime_stage(); + auto uniform_data_sk = runtime_effect_color_source->uniform_data(); + + paint_.color_source = [runtime_stage, uniform_data_sk]() { + // TODO(113714): Get rid of the allocation + copy for uniform data. + std::vector uniform_data; + uniform_data.resize(uniform_data_sk->size()); + memcpy(uniform_data.data(), uniform_data_sk->bytes(), + uniform_data.size()); + + auto contents = std::make_shared(); + contents->SetRuntimeStage(runtime_stage); + contents->SetUniformData(std::move(uniform_data)); + return contents; + }; + return; + } case flutter::DlColorSourceType::kConicalGradient: - case flutter::DlColorSourceType::kRuntimeEffect: case flutter::DlColorSourceType::kUnknown: UNIMPLEMENTED; break; diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 8a417d30fbd7c..3c6c42e07c09e 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -44,6 +44,10 @@ impeller_shaders("entity_shaders") { "shaders/linear_gradient_fill.frag", "shaders/morphology_filter.frag", "shaders/morphology_filter.vert", + "shaders/position_color.vert", + "shaders/position_no_color.vert", + "shaders/position_uv.vert", + "shaders/position.vert", "shaders/radial_gradient_fill.frag", "shaders/rrect_blur.vert", "shaders/rrect_blur.frag", @@ -57,9 +61,6 @@ impeller_shaders("entity_shaders") { "shaders/tiled_texture_fill.frag", "shaders/tiled_texture_fill.vert", "shaders/vertices.frag", - "shaders/position_color.vert", - "shaders/position.vert", - "shaders/position_uv.vert", ] } @@ -113,6 +114,8 @@ impeller_component("entity") { "contents/radial_gradient_contents.h", "contents/rrect_shadow_contents.cc", "contents/rrect_shadow_contents.h", + "contents/runtime_effect_contents.cc", + "contents/runtime_effect_contents.h", "contents/solid_color_contents.cc", "contents/solid_color_contents.h", "contents/solid_stroke_contents.cc", diff --git a/impeller/entity/contents/runtime_effect_contents.cc b/impeller/entity/contents/runtime_effect_contents.cc new file mode 100644 index 0000000000000..b0c06a00e97ab --- /dev/null +++ b/impeller/entity/contents/runtime_effect_contents.cc @@ -0,0 +1,167 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents/runtime_effect_contents.h" + +#include +#include + +#include "flutter/fml/logging.h" +#include "flutter/fml/make_copyable.h" +#include "impeller/base/validation.h" +#include "impeller/entity/contents/clip_contents.h" +#include "impeller/entity/contents/content_context.h" +#include "impeller/entity/position_no_color.vert.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/pipeline_library.h" +#include "impeller/renderer/render_pass.h" +#include "impeller/renderer/shader_function.h" +#include "impeller/renderer/shader_types.h" + +namespace impeller { + +void RuntimeEffectContents::SetRuntimeStage( + std::shared_ptr runtime_stage) { + runtime_stage_ = std::move(runtime_stage); +} + +void RuntimeEffectContents::SetUniformData(std::vector uniform_data) { + uniform_data_ = std::move(uniform_data); +} + +bool RuntimeEffectContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + auto context = renderer.GetContext(); + auto library = context->GetShaderLibrary(); + + //-------------------------------------------------------------------------- + /// Get or register shader. + /// + + // TODO(113719): Register the shader function earlier. + + std::shared_ptr function = library->GetFunction( + runtime_stage_->GetEntrypoint(), ShaderStage::kFragment); + + if (!function) { + std::promise promise; + auto future = promise.get_future(); + + library->RegisterFunction( + runtime_stage_->GetEntrypoint(), + ToShaderStage(runtime_stage_->GetShaderStage()), + runtime_stage_->GetCodeMapping(), + fml::MakeCopyable([promise = std::move(promise)](bool result) mutable { + promise.set_value(result); + })); + + if (!future.get()) { + VALIDATION_LOG << "Failed to build runtime effect (entry point: " + << runtime_stage_->GetEntrypoint() << ")"; + return false; + } + + function = library->GetFunction(runtime_stage_->GetEntrypoint(), + ShaderStage::kFragment); + if (!function) { + VALIDATION_LOG + << "Failed to fetch runtime effect function immediately after " + "registering it (entry point: " + << runtime_stage_->GetEntrypoint() << ")"; + return false; + } + } + + //-------------------------------------------------------------------------- + /// Resolve geometry. + /// + + auto geometry_result = GetGeometry()->GetPositionBuffer( + context->GetResourceAllocator(), pass.GetTransientsBuffer(), + renderer.GetTessellator(), pass.GetRenderTargetSize(), + entity.GetTransformation().GetMaxBasisLength()); + + //-------------------------------------------------------------------------- + /// Get or create runtime stage pipeline. + /// + + using VS = PositionNoColorVertexShader; + PipelineDescriptor desc; + desc.SetLabel("Runtime Stage"); + desc.AddStageEntrypoint( + library->GetFunction(VS::kEntrypointName, ShaderStage::kVertex)); + desc.AddStageEntrypoint(library->GetFunction(runtime_stage_->GetEntrypoint(), + ShaderStage::kFragment)); + auto vertex_descriptor = std::make_shared(); + if (!vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs)) { + VALIDATION_LOG << "Failed to set stage inputs for runtime effect pipeline."; + } + desc.SetVertexDescriptor(std::move(vertex_descriptor)); + desc.SetColorAttachmentDescriptor(0u, {.format = PixelFormat::kDefaultColor}); + desc.SetStencilAttachmentDescriptors({}); + desc.SetStencilPixelFormat(PixelFormat::kDefaultStencil); + + auto options = OptionsFromPassAndEntity(pass, entity); + if (geometry_result.prevent_overdraw) { + options.stencil_compare = CompareFunction::kEqual; + options.stencil_operation = StencilOperation::kIncrementClamp; + } + options.ApplyToPipelineDescriptor(desc); + + auto pipeline = context->GetPipelineLibrary()->GetPipeline(desc).get(); + if (!pipeline) { + VALIDATION_LOG << "Failed to get or create runtime effect pipeline."; + return false; + } + + Command cmd; + cmd.label = "RuntimeEffectContents"; + cmd.pipeline = pipeline; + cmd.stencil_reference = entity.GetStencilDepth(); + cmd.BindVertices(geometry_result.vertex_buffer); + cmd.primitive_type = geometry_result.type; + + //-------------------------------------------------------------------------- + /// Vertex stage uniforms. + /// + + VS::VertInfo frame_info; + frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + entity.GetTransformation(); + VS::BindVertInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); + + //-------------------------------------------------------------------------- + /// Fragment stage uniforms. + /// + + size_t buffer_index = 0; + for (auto uniform : runtime_stage_->GetUniforms()) { + // TODO(113715): Populate this metadata once GLES is able to handle + // non-struct uniform names. + ShaderMetadata metadata; + + size_t alignment = + std::max(uniform.bit_width / 8, DefaultUniformAlignment()); + auto buffer_view = pass.GetTransientsBuffer().Emplace( + &uniform_data_[uniform.location * sizeof(float)], uniform.GetSize(), + alignment); + + ShaderUniformSlot slot; + slot.name = uniform.name.c_str(); + slot.ext_res_0 = buffer_index; + cmd.BindResource(ShaderStage::kFragment, slot, metadata, buffer_view); + + buffer_index++; + } + + pass.AddCommand(std::move(cmd)); + + if (geometry_result.prevent_overdraw) { + return ClipRestoreContents().Render(renderer, entity, pass); + } + return true; +} + +} // namespace impeller diff --git a/impeller/entity/contents/runtime_effect_contents.h b/impeller/entity/contents/runtime_effect_contents.h new file mode 100644 index 0000000000000..7a53e7f138bd6 --- /dev/null +++ b/impeller/entity/contents/runtime_effect_contents.h @@ -0,0 +1,29 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "impeller/entity/contents/color_source_contents.h" +#include "impeller/runtime_stage/runtime_stage.h" + +namespace impeller { + +class RuntimeEffectContents final : public ColorSourceContents { + public: + void SetRuntimeStage(std::shared_ptr runtime_stage); + + void SetUniformData(std::vector uniform_data); + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + private: + std::shared_ptr runtime_stage_; + std::vector uniform_data_; +}; + +} // namespace impeller diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index c56dba23e4afd..e80cc19c2e225 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include +#include #include #include #include @@ -10,6 +11,7 @@ #include "flutter/testing/testing.h" #include "fml/logging.h" +#include "fml/time/time_point.h" #include "gtest/gtest.h" #include "impeller/entity/contents/atlas_contents.h" #include "impeller/entity/contents/clip_contents.h" @@ -19,6 +21,7 @@ #include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/filters/inputs/filter_input.h" #include "impeller/entity/contents/rrect_shadow_contents.h" +#include "impeller/entity/contents/runtime_effect_contents.h" #include "impeller/entity/contents/solid_color_contents.h" #include "impeller/entity/contents/solid_stroke_contents.h" #include "impeller/entity/contents/text_contents.h" @@ -37,6 +40,7 @@ #include "impeller/playground/widgets.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/vertex_buffer_builder.h" +#include "impeller/runtime_stage/runtime_stage.h" #include "impeller/tessellator/tessellator.h" #include "impeller/typographer/backends/skia/text_frame_skia.h" #include "impeller/typographer/backends/skia/text_render_context_skia.h" @@ -2026,5 +2030,38 @@ TEST_P(EntityTest, SdfText) { ASSERT_TRUE(OpenPlaygroundHere(callback)); } +TEST_P(EntityTest, RuntimeEffect) { + if (GetParam() != PlaygroundBackend::kMetal) { + GTEST_SKIP_("This test only has a Metal fixture at the moment."); + } + + auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { + auto contents = std::make_shared(); + contents->SetGeometry(Geometry::MakeCover()); + + auto runtime_stage = + LoadFixtureRuntimeStage("runtime_stage_example.frag.iplr"); + contents->SetRuntimeStage(runtime_stage); + + struct FragUniforms { + Scalar iTime; + Vector2 iResolution; + } frag_uniforms = { + .iTime = static_cast( + fml::TimePoint::Now().ToEpochDelta().ToSecondsF()), + .iResolution = Vector2(GetWindowSize().width, GetWindowSize().height), + }; + std::vector uniform_data; + uniform_data.resize(sizeof(FragUniforms)); + memcpy(uniform_data.data(), &frag_uniforms, sizeof(FragUniforms)); + contents->SetUniformData(uniform_data); + + Entity entity; + entity.SetContents(contents); + return contents->Render(context, entity, pass); + }; + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + } // namespace testing } // namespace impeller diff --git a/impeller/entity/shaders/position_no_color.vert b/impeller/entity/shaders/position_no_color.vert new file mode 100644 index 0000000000000..16007d94bb6dd --- /dev/null +++ b/impeller/entity/shaders/position_no_color.vert @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +uniform VertInfo { + mat4 mvp; +} vert_info; + +in vec2 position; +out vec2 v_position; + +void main() { + gl_Position = vert_info.mvp * vec4(position, 0.0, 1.0); + v_position = IPVec2TransformPosition(vert_info.mvp, position); +} diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index 19e4f2de68bac..ca6984e0f5b57 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -34,7 +34,10 @@ impeller_shaders("shader_fixtures") { } impellerc("runtime_stages") { - shaders = [ "ink_sparkle.frag" ] + shaders = [ + "ink_sparkle.frag", + "runtime_stage_example.frag", + ] sl_file_extension = "iplr" shader_target_flag = "--runtime-stage-metal" iplr = true diff --git a/impeller/fixtures/runtime_stage_example.frag b/impeller/fixtures/runtime_stage_example.frag new file mode 100644 index 0000000000000..678cb95c263be --- /dev/null +++ b/impeller/fixtures/runtime_stage_example.frag @@ -0,0 +1,15 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +layout(location = 0) uniform float iTime; +layout(location = 1) uniform vec2 iResolution; + +layout(location = 0) out vec4 fragColor; + +void main() { + vec2 uv = gl_FragCoord.xy/iResolution; + float t = 4 * iTime; + vec3 col = 0.5 + 0.5*cos(t + uv.xyx + vec3(0,1,4)); + fragColor = vec4(col,1.0); +} diff --git a/impeller/playground/playground.cc b/impeller/playground/playground.cc index 1d71b4e020656..adfce380cc838 100644 --- a/impeller/playground/playground.cc +++ b/impeller/playground/playground.cc @@ -9,6 +9,7 @@ #include "impeller/image/decompressed_image.h" #include "impeller/renderer/command_buffer.h" +#include "impeller/runtime_stage/runtime_stage.h" #define GLFW_INCLUDE_NONE #include "third_party/glfw/include/GLFW/glfw3.h" @@ -436,6 +437,22 @@ std::shared_ptr Playground::CreateTextureCubeForFixture( return texture; } +std::shared_ptr Playground::LoadFixtureRuntimeStage( + const char* fixture_name) const { + if (fixture_name == nullptr) { + return nullptr; + } + + auto runtime_stage = + std::make_shared(OpenAssetAsMapping(fixture_name)); + + if (!runtime_stage->IsValid()) { + VALIDATION_LOG << "Could not load valid runtime stage."; + return nullptr; + } + return runtime_stage; +} + void Playground::SetWindowSize(ISize size) { window_size_ = size; } diff --git a/impeller/playground/playground.h b/impeller/playground/playground.h index d2feb0ef1df7f..255c87d7b4784 100644 --- a/impeller/playground/playground.h +++ b/impeller/playground/playground.h @@ -8,9 +8,11 @@ #include "flutter/fml/closure.h" #include "flutter/fml/macros.h" + #include "impeller/geometry/point.h" #include "impeller/renderer/renderer.h" #include "impeller/renderer/texture.h" +#include "impeller/runtime_stage/runtime_stage.h" namespace impeller { @@ -62,6 +64,9 @@ class Playground { std::shared_ptr CreateTextureCubeForFixture( std::array fixture_names) const; + std::shared_ptr LoadFixtureRuntimeStage( + const char* fixture_name) const; + static bool SupportsBackend(PlaygroundBackend backend); virtual std::unique_ptr OpenAssetAsMapping( diff --git a/impeller/renderer/backend/metal/shader_library_mtl.mm b/impeller/renderer/backend/metal/shader_library_mtl.mm index abb4d123b35f1..57ab6d9bec842 100644 --- a/impeller/renderer/backend/metal/shader_library_mtl.mm +++ b/impeller/renderer/backend/metal/shader_library_mtl.mm @@ -71,7 +71,6 @@ static MTLFunctionType ToMTLFunctionType(ShaderStage stage) { } if (function == nil) { - VALIDATION_LOG << "No library function found for name: " << name; return nullptr; } diff --git a/impeller/runtime_stage/BUILD.gn b/impeller/runtime_stage/BUILD.gn index b36a7f9017c88..5756887562edb 100644 --- a/impeller/runtime_stage/BUILD.gn +++ b/impeller/runtime_stage/BUILD.gn @@ -20,6 +20,7 @@ impeller_component("runtime_stage") { sources = [ "runtime_stage.cc", "runtime_stage.h", + "runtime_types.cc", "runtime_types.h", ] public_deps = [ diff --git a/impeller/runtime_stage/runtime_types.cc b/impeller/runtime_stage/runtime_types.cc new file mode 100644 index 0000000000000..4236eae89bbfc --- /dev/null +++ b/impeller/runtime_stage/runtime_types.cc @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/runtime_stage/runtime_types.h" + +namespace impeller { + +size_t RuntimeUniformDescription::GetSize() const { + size_t size = dimensions.rows * dimensions.cols * bit_width / 8u; + if (array_elements.value_or(0) > 0) { + size *= array_elements.value(); + } + return size; +} + +} // namespace impeller diff --git a/impeller/runtime_stage/runtime_types.h b/impeller/runtime_stage/runtime_types.h index 2ccf6ca3e0f24..2ef8512dbd8b4 100644 --- a/impeller/runtime_stage/runtime_types.h +++ b/impeller/runtime_stage/runtime_types.h @@ -46,6 +46,9 @@ struct RuntimeUniformDescription { RuntimeUniformDimensions dimensions; size_t bit_width; std::optional array_elements; + + /// @brief Computes the total number of bytes that this uniform requires. + size_t GetSize() const; }; } // namespace impeller diff --git a/lib/ui/painting/fragment_program.cc b/lib/ui/painting/fragment_program.cc index 21bbd83c70421..2cadcacd5eb0b 100644 --- a/lib/ui/painting/fragment_program.cc +++ b/lib/ui/painting/fragment_program.cc @@ -4,6 +4,7 @@ #include #include +#include #include "display_list/display_list_runtime_effect.h" #include "flutter/lib/ui/painting/fragment_program.h" @@ -51,13 +52,7 @@ std::string FragmentProgram::initFromAsset(const std::string& asset_name) { impeller::RuntimeUniformType::kSampledImage) { sampled_image_count++; } else { - size_t size = uniform_description.dimensions.rows * - uniform_description.dimensions.cols * - uniform_description.bit_width / 8u; - if (uniform_description.array_elements.value_or(0) > 0) { - size *= uniform_description.array_elements.value(); - } - other_uniforms_bytes += size; + other_uniforms_bytes += uniform_description.GetSize(); } }