From 84cd38468222c6a186dfa00bb41e5a65c21dfbcc Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Mon, 24 Mar 2025 10:32:11 -0700 Subject: [PATCH] Implements anti-aliased lines (#164734) issue: https://github.com/flutter/flutter/issues/138682 design doc: https://docs.google.com/document/d/19I6ToHCMlSgSava-niFWzMLGJEAd-rYiBQEGOMu8IJg/edit?tab=t.0 This puts an experimental line drawing approach behind the following flags: - iOS: `FLTAntialiasLines` boolean, default NO - Android: `io.flutter.embedding.android.ImpellerAntialiasLines` boolean, default false Right now they just support DrawLines and don't support line caps. A test was added that works as a playground for vulkan, opengles and metal. Only the Metal version was turned into a golden test though here. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --- .../flutter/ci/licenses_golden/excluded_files | 1 + .../ci/licenses_golden/licenses_flutter | 16 +- engine/src/flutter/common/settings.h | 3 + .../testing/dl_test_surface_metal.mm | 4 +- engine/src/flutter/impeller/base/flags.h | 2 + .../display_list/aiks_dl_path_unittests.cc | 41 ++ .../flutter/impeller/display_list/canvas.cc | 17 +- engine/src/flutter/impeller/entity/BUILD.gn | 5 + .../entity/contents/color_source_contents.h | 98 ++-- .../entity/contents/content_context.cc | 7 +- .../entity/contents/content_context.h | 5 + .../entity/contents/gradient_generator.cc | 25 +- .../impeller/entity/contents/line_contents.cc | 188 ++++++++ .../impeller/entity/contents/line_contents.h | 34 ++ .../contents/line_contents_unittests.cc | 33 ++ .../impeller/entity/geometry/geometry.h | 1 - .../impeller/entity/geometry/line_geometry.cc | 30 +- .../impeller/entity/geometry/line_geometry.h | 31 +- .../impeller/entity/inline_pass_context.cc | 2 +- .../flutter/impeller/entity/shaders/line.frag | 44 ++ .../flutter/impeller/entity/shaders/line.vert | 32 ++ .../golden_playground_test_mac.cc | 20 +- .../impeller/golden_tests/golden_tests.cc | 7 +- .../golden_tests/metal_screenshotter.h | 2 +- .../golden_tests/metal_screenshotter.mm | 4 +- .../backend/gles/playground_impl_gles.cc | 5 +- .../backend/metal/playground_impl_mtl.mm | 2 +- .../backend/vulkan/playground_impl_vk.cc | 3 +- .../impeller/playground/playground_test.cc | 3 + .../flutter/impeller/playground/switches.h | 3 + engine/src/flutter/impeller/renderer/BUILD.gn | 4 +- .../ahb_texture_source_vk_unittests.cc | 2 +- .../renderer/backend/vulkan/context_vk.cc | 5 +- .../renderer/backend/vulkan/context_vk.h | 4 +- .../backend/vulkan/test/mock_vulkan.cc | 3 +- .../impeller/renderer/texture_mipmap.cc | 27 -- .../flutter/impeller/renderer/texture_util.cc | 53 ++ .../{texture_mipmap.h => texture_util.h} | 12 +- .../interop/backend/vulkan/context_vk.cc | 5 +- engine/src/flutter/impeller/tools/malioc.json | 454 ++++++++++++++++++ .../common/shell_test_platform_view_metal.mm | 3 +- engine/src/flutter/shell/common/switches.cc | 2 + engine/src/flutter/shell/common/switches.h | 3 + .../android/android_context_vk_impeller.cc | 7 +- .../android/context/android_context.h | 3 +- .../engine/loader/FlutterLoader.java | 5 + .../platform/android/platform_view_android.cc | 4 +- .../FlutterDarwinContextMetalImpeller.h | 4 +- .../FlutterDarwinContextMetalImpeller.mm | 11 +- .../framework/Source/FlutterDartProject.mm | 3 + .../shell/platform/darwin/ios/ios_context.h | 4 +- .../shell/platform/darwin/ios/ios_context.mm | 5 +- .../darwin/ios/ios_context_metal_impeller.h | 1 + .../darwin/ios/ios_context_metal_impeller.mm | 13 +- .../platform/darwin/ios/platform_view_ios.mm | 3 +- .../embedder_surface_vulkan_impeller.cc | 3 +- .../src/flutter/shell/testing/tester_main.cc | 8 +- .../testing/impeller_golden_tests_output.txt | 1 + 58 files changed, 1149 insertions(+), 171 deletions(-) create mode 100644 engine/src/flutter/impeller/entity/contents/line_contents.cc create mode 100644 engine/src/flutter/impeller/entity/contents/line_contents.h create mode 100644 engine/src/flutter/impeller/entity/contents/line_contents_unittests.cc create mode 100644 engine/src/flutter/impeller/entity/shaders/line.frag create mode 100644 engine/src/flutter/impeller/entity/shaders/line.vert delete mode 100644 engine/src/flutter/impeller/renderer/texture_mipmap.cc create mode 100644 engine/src/flutter/impeller/renderer/texture_util.cc rename engine/src/flutter/impeller/renderer/{texture_mipmap.h => texture_util.h} (62%) diff --git a/engine/src/flutter/ci/licenses_golden/excluded_files b/engine/src/flutter/ci/licenses_golden/excluded_files index efc4e89e15..a7b219ad06 100644 --- a/engine/src/flutter/ci/licenses_golden/excluded_files +++ b/engine/src/flutter/ci/licenses_golden/excluded_files @@ -161,6 +161,7 @@ ../../../flutter/impeller/entity/contents/filters/inputs/filter_input_unittests.cc ../../../flutter/impeller/entity/contents/filters/matrix_filter_contents_unittests.cc ../../../flutter/impeller/entity/contents/host_buffer_unittests.cc +../../../flutter/impeller/entity/contents/line_contents_unittests.cc ../../../flutter/impeller/entity/contents/test ../../../flutter/impeller/entity/contents/text_contents_unittests.cc ../../../flutter/impeller/entity/contents/tiled_texture_contents_unittests.cc diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index f4ec6aeeb1..000285ab84 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -51252,6 +51252,8 @@ ORIGIN: ../../../flutter/impeller/entity/contents/framebuffer_blend_contents.cc ORIGIN: ../../../flutter/impeller/entity/contents/framebuffer_blend_contents.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/gradient_generator.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/gradient_generator.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/contents/line_contents.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/contents/line_contents.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/linear_gradient_contents.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/linear_gradient_contents.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/radial_gradient_contents.cc + ../../../flutter/LICENSE @@ -51357,6 +51359,8 @@ ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/radial_gradient_unifo ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_fill.frag + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_ssbo_fill.frag + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_uniform_fill.frag + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/shaders/line.frag + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/shaders/line.vert + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/rrect_blur.frag + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/rrect_blur.vert + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/runtime_effect.vert + ../../../flutter/LICENSE @@ -51689,8 +51693,8 @@ ORIGIN: ../../../flutter/impeller/renderer/snapshot.cc + ../../../flutter/LICENS ORIGIN: ../../../flutter/impeller/renderer/snapshot.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/surface.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/surface.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/renderer/texture_mipmap.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/renderer/texture_mipmap.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/texture_util.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/texture_util.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/threadgroup_sizing_test.comp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/vertex_buffer_builder.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/vertex_buffer_builder.h + ../../../flutter/LICENSE @@ -54225,6 +54229,8 @@ FILE: ../../../flutter/impeller/entity/contents/framebuffer_blend_contents.cc FILE: ../../../flutter/impeller/entity/contents/framebuffer_blend_contents.h FILE: ../../../flutter/impeller/entity/contents/gradient_generator.cc FILE: ../../../flutter/impeller/entity/contents/gradient_generator.h +FILE: ../../../flutter/impeller/entity/contents/line_contents.cc +FILE: ../../../flutter/impeller/entity/contents/line_contents.h FILE: ../../../flutter/impeller/entity/contents/linear_gradient_contents.cc FILE: ../../../flutter/impeller/entity/contents/linear_gradient_contents.h FILE: ../../../flutter/impeller/entity/contents/radial_gradient_contents.cc @@ -54330,6 +54336,8 @@ FILE: ../../../flutter/impeller/entity/shaders/gradients/radial_gradient_uniform FILE: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_fill.frag FILE: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_ssbo_fill.frag FILE: ../../../flutter/impeller/entity/shaders/gradients/sweep_gradient_uniform_fill.frag +FILE: ../../../flutter/impeller/entity/shaders/line.frag +FILE: ../../../flutter/impeller/entity/shaders/line.vert FILE: ../../../flutter/impeller/entity/shaders/rrect_blur.frag FILE: ../../../flutter/impeller/entity/shaders/rrect_blur.vert FILE: ../../../flutter/impeller/entity/shaders/runtime_effect.vert @@ -54663,8 +54671,8 @@ FILE: ../../../flutter/impeller/renderer/snapshot.cc FILE: ../../../flutter/impeller/renderer/snapshot.h FILE: ../../../flutter/impeller/renderer/surface.cc FILE: ../../../flutter/impeller/renderer/surface.h -FILE: ../../../flutter/impeller/renderer/texture_mipmap.cc -FILE: ../../../flutter/impeller/renderer/texture_mipmap.h +FILE: ../../../flutter/impeller/renderer/texture_util.cc +FILE: ../../../flutter/impeller/renderer/texture_util.h FILE: ../../../flutter/impeller/renderer/threadgroup_sizing_test.comp FILE: ../../../flutter/impeller/renderer/vertex_buffer_builder.cc FILE: ../../../flutter/impeller/renderer/vertex_buffer_builder.h diff --git a/engine/src/flutter/common/settings.h b/engine/src/flutter/common/settings.h index f25d302dce..8f41eb29e5 100644 --- a/engine/src/flutter/common/settings.h +++ b/engine/src/flutter/common/settings.h @@ -231,6 +231,9 @@ struct Settings { // Whether to lazily initialize impeller PSO state. bool impeller_enable_lazy_shader_mode = false; + // An experimental mode that antialiases lines. + bool impeller_antialiased_lines = false; + // Log a warning during shell initialization if Impeller is not enabled. bool warn_on_impeller_opt_out = false; diff --git a/engine/src/flutter/display_list/testing/dl_test_surface_metal.mm b/engine/src/flutter/display_list/testing/dl_test_surface_metal.mm index fd3201cb8d..006952d955 100644 --- a/engine/src/flutter/display_list/testing/dl_test_surface_metal.mm +++ b/engine/src/flutter/display_list/testing/dl_test_surface_metal.mm @@ -91,7 +91,9 @@ sk_sp DlMetalSurfaceProvider::MakeImpellerImage(const sk_spGetPlayground().GetContext(), typographer)); diff --git a/engine/src/flutter/impeller/base/flags.h b/engine/src/flutter/impeller/base/flags.h index 258a8331e2..cb581c34fe 100644 --- a/engine/src/flutter/impeller/base/flags.h +++ b/engine/src/flutter/impeller/base/flags.h @@ -10,6 +10,8 @@ struct Flags { /// Whether to defer PSO construction until first use. Usage Will introduce /// raster jank. bool lazy_shader_mode = false; + /// When turned on DrawLine will use the experimental antialiased path. + bool antialiased_lines = false; }; } // namespace impeller diff --git a/engine/src/flutter/impeller/display_list/aiks_dl_path_unittests.cc b/engine/src/flutter/impeller/display_list/aiks_dl_path_unittests.cc index be8cb9ae0f..0f4729c7ea 100644 --- a/engine/src/flutter/impeller/display_list/aiks_dl_path_unittests.cc +++ b/engine/src/flutter/impeller/display_list/aiks_dl_path_unittests.cc @@ -382,6 +382,47 @@ TEST_P(AiksTest, DrawLinesRenderCorrectly) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } +TEST_P(AiksTest, SimpleExperimentAntialiasLines) { + DisplayListBuilder builder; + builder.Scale(GetContentScale().x, GetContentScale().y); + + builder.DrawPaint(DlPaint(DlColor(0xff111111))); + + DlPaint paint; + paint.setColor(DlColor::kGreenYellow()); + paint.setStrokeWidth(10); + + auto draw = [&builder](DlPaint& paint) { + for (auto cap : + {DlStrokeCap::kButt, DlStrokeCap::kSquare, DlStrokeCap::kRound}) { + paint.setStrokeCap(cap); + DlPoint origin = {100, 100}; + builder.DrawLine(DlPoint(150, 100), DlPoint(250, 100), paint); + for (int d = 15; d < 90; d += 15) { + Matrix m = Matrix::MakeRotationZ(Degrees(d)); + Point origin = {100, 100}; + Point p0 = {50, 0}; + Point p1 = {150, 0}; + auto a = origin + m * p0; + auto b = origin + m * p1; + + builder.DrawLine(a, b, paint); + } + builder.DrawLine(DlPoint(100, 150), DlPoint(100, 250), paint); + builder.DrawCircle(origin, 35, paint); + + builder.DrawLine(DlPoint(250, 250), DlPoint(250, 250), paint); + + builder.Translate(250, 0); + } + builder.Translate(-750, 250); + }; + + draw(paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + TEST_P(AiksTest, DrawRectStrokesRenderCorrectly) { DisplayListBuilder builder; DlPaint paint; diff --git a/engine/src/flutter/impeller/display_list/canvas.cc b/engine/src/flutter/impeller/display_list/canvas.cc index c18ebc8a85..9c3380db23 100644 --- a/engine/src/flutter/impeller/display_list/canvas.cc +++ b/engine/src/flutter/impeller/display_list/canvas.cc @@ -24,6 +24,7 @@ #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/filters/filter_contents.h" #include "impeller/entity/contents/framebuffer_blend_contents.h" +#include "impeller/entity/contents/line_contents.h" #include "impeller/entity/contents/solid_rrect_blur_contents.h" #include "impeller/entity/contents/text_contents.h" #include "impeller/entity/contents/texture_contents.h" @@ -459,9 +460,19 @@ void Canvas::DrawLine(const Point& p0, entity.SetTransform(GetCurrentTransform()); entity.SetBlendMode(paint.blend_mode); - LineGeometry geom(p0, p1, paint.stroke_width, paint.stroke_cap); - AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint, - /*reuse_depth=*/reuse_depth); + auto geometry = std::make_unique(p0, p1, paint.stroke_width, + paint.stroke_cap); + + if (renderer_.GetContext()->GetFlags().antialiased_lines && + !paint.color_filter && !paint.invert_colors && !paint.image_filter && + !paint.mask_blur_descriptor.has_value() && !paint.color_source) { + auto contents = LineContents::Make(std::move(geometry), paint.color); + entity.SetContents(std::move(contents)); + AddRenderEntityToCurrentPass(entity, reuse_depth); + } else { + AddRenderEntityWithFiltersToCurrentPass(entity, geometry.get(), paint, + /*reuse_depth=*/reuse_depth); + } } void Canvas::DrawRect(const Rect& rect, const Paint& paint) { diff --git a/engine/src/flutter/impeller/entity/BUILD.gn b/engine/src/flutter/impeller/entity/BUILD.gn index fe25c779a9..688cf7a9c2 100644 --- a/engine/src/flutter/impeller/entity/BUILD.gn +++ b/engine/src/flutter/impeller/entity/BUILD.gn @@ -35,6 +35,8 @@ impeller_shaders("entity_shaders") { "shaders/gradients/radial_gradient_uniform_fill.frag", "shaders/gradients/sweep_gradient_fill.frag", "shaders/gradients/sweep_gradient_uniform_fill.frag", + "shaders/line.frag", + "shaders/line.vert", "shaders/rrect_blur.vert", "shaders/rrect_blur.frag", "shaders/runtime_effect.vert", @@ -159,6 +161,8 @@ impeller_component("entity") { "contents/framebuffer_blend_contents.h", "contents/gradient_generator.cc", "contents/gradient_generator.h", + "contents/line_contents.cc", + "contents/line_contents.h", "contents/linear_gradient_contents.cc", "contents/linear_gradient_contents.h", "contents/radial_gradient_contents.cc", @@ -254,6 +258,7 @@ impeller_component("entity_unittests") { "contents/filters/inputs/filter_input_unittests.cc", "contents/filters/matrix_filter_contents_unittests.cc", "contents/host_buffer_unittests.cc", + "contents/line_contents_unittests.cc", "contents/text_contents_unittests.cc", "contents/tiled_texture_contents_unittests.cc", "draw_order_resolver_unittests.cc", diff --git a/engine/src/flutter/impeller/entity/contents/color_source_contents.h b/engine/src/flutter/impeller/entity/contents/color_source_contents.h index 96bfd62928..6d58ab07ab 100644 --- a/engine/src/flutter/impeller/entity/contents/color_source_contents.h +++ b/engine/src/flutter/impeller/entity/contents/color_source_contents.h @@ -36,6 +36,15 @@ namespace impeller { /// class ColorSourceContents : public Contents { public: + using BindFragmentCallback = std::function; + using PipelineBuilderCallback = + std::function; + using CreateGeometryCallback = + std::function; + ColorSourceContents(); ~ColorSourceContents() override; @@ -99,41 +108,21 @@ class ColorSourceContents : public Contents { // |Contents| void SetInheritedOpacity(Scalar opacity) override; - protected: - using BindFragmentCallback = std::function; - using PipelineBuilderCallback = - std::function; - using CreateGeometryCallback = - std::function; - - static GeometryResult DefaultCreateGeometryCallback( - const ContentContext& renderer, - const Entity& entity, - RenderPass& pass, - const Geometry* geom) { - return geom->GetPositionBuffer(renderer, entity, pass); - } - - /// @brief Whether the entity should be treated as non-opaque due to stroke - /// geometry requiring alpha for coverage. - bool AppliesAlphaForStrokeCoverage(const Matrix& transform) const; - template - bool DrawGeometry(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass, - const PipelineBuilderCallback& pipeline_callback, - typename VertexShaderT::FrameInfo frame_info, - const BindFragmentCallback& bind_fragment_callback, - bool force_stencil = false, - const CreateGeometryCallback& create_geom_callback = - DefaultCreateGeometryCallback) const { + static bool DrawGeometry(const Contents* contents, + const Geometry* geometry, + const ContentContext& renderer, + const Entity& entity, + RenderPass& pass, + const PipelineBuilderCallback& pipeline_callback, + typename VertexShaderT::FrameInfo frame_info, + const BindFragmentCallback& bind_fragment_callback, + bool force_stencil = false, + const CreateGeometryCallback& create_geom_callback = + DefaultCreateGeometryCallback) { auto options = OptionsFromPassAndEntity(pass, entity); - GeometryResult::Mode geometry_mode = GetGeometry()->GetResultMode(); + GeometryResult::Mode geometry_mode = geometry->GetResultMode(); bool do_cover_draw = false; Rect cover_area = {}; @@ -151,7 +140,7 @@ class ColorSourceContents : public Contents { /// Stencil preparation draw. GeometryResult stencil_geometry_result = - GetGeometry()->GetPositionBuffer(renderer, entity, pass); + geometry->GetPositionBuffer(renderer, entity, pass); if (stencil_geometry_result.vertex_buffer.vertex_count == 0u) { return true; } @@ -194,7 +183,7 @@ class ColorSourceContents : public Contents { options.blend_mode = entity.GetBlendMode(); options.stencil_mode = ContentContextOptions::StencilMode::kCoverCompare; - std::optional maybe_cover_area = GetGeometry()->GetCoverage({}); + std::optional maybe_cover_area = geometry->GetCoverage({}); if (!maybe_cover_area.has_value()) { return true; } @@ -207,8 +196,7 @@ class ColorSourceContents : public Contents { RectGeometry geom(cover_area); geometry_result = create_geom_callback(renderer, entity, pass, &geom); } else { - geometry_result = - create_geom_callback(renderer, entity, pass, GetGeometry()); + geometry_result = create_geom_callback(renderer, entity, pass, geometry); } if (geometry_result.vertex_buffer.vertex_count == 0u) { @@ -261,11 +249,47 @@ class ColorSourceContents : public Contents { if (geometry_result.mode == GeometryResult::Mode::kPreventOverdraw && options.blend_mode != BlendMode::kSrc) { return RenderClipRestore(renderer, pass, entity.GetClipDepth(), - GetCoverage(entity)); + contents->GetCoverage(entity)); } return true; } + protected: + static GeometryResult DefaultCreateGeometryCallback( + const ContentContext& renderer, + const Entity& entity, + RenderPass& pass, + const Geometry* geom) { + return geom->GetPositionBuffer(renderer, entity, pass); + } + + /// @brief Whether the entity should be treated as non-opaque due to stroke + /// geometry requiring alpha for coverage. + bool AppliesAlphaForStrokeCoverage(const Matrix& transform) const; + + template + bool DrawGeometry(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass, + const PipelineBuilderCallback& pipeline_callback, + typename VertexShaderT::FrameInfo frame_info, + const BindFragmentCallback& bind_fragment_callback, + bool force_stencil = false, + const CreateGeometryCallback& create_geom_callback = + DefaultCreateGeometryCallback) const { + // + return DrawGeometry(this, // + GetGeometry(), // + renderer, // + entity, // + pass, // + pipeline_callback, // + frame_info, // + bind_fragment_callback, // + force_stencil, // + create_geom_callback); + } + private: const Geometry* geometry_ = nullptr; Matrix inverse_matrix_; diff --git a/engine/src/flutter/impeller/entity/contents/content_context.cc b/engine/src/flutter/impeller/entity/contents/content_context.cc index 376836fe8f..41383a7384 100644 --- a/engine/src/flutter/impeller/entity/contents/content_context.cc +++ b/engine/src/flutter/impeller/entity/contents/content_context.cc @@ -18,7 +18,7 @@ #include "impeller/renderer/pipeline_descriptor.h" #include "impeller/renderer/pipeline_library.h" #include "impeller/renderer/render_target.h" -#include "impeller/renderer/texture_mipmap.h" +#include "impeller/renderer/texture_util.h" #include "impeller/tessellator/tessellator.h" #include "impeller/typographer/typographer_context.h" @@ -329,6 +329,7 @@ ContentContext::ContentContext( solid_fill_pipelines_.CreateDefault(*context_, options); texture_pipelines_.CreateDefault(*context_, options); fast_gradient_pipelines_.CreateDefault(*context_, options); + line_pipelines_.CreateDefault(*context_, options); if (context_->GetCapabilities()->SupportsSSBO()) { linear_gradient_ssbo_fill_pipelines_.CreateDefault(*context_, options); @@ -1159,6 +1160,10 @@ PipelineRef ContentContext::GetDrawVerticesUberShader( return GetPipeline(vertices_uber_shader_, opts); } +PipelineRef ContentContext::GetLinePipeline(ContentContextOptions opts) const { + return GetPipeline(line_pipelines_, opts); +} + #ifdef IMPELLER_ENABLE_OPENGLES PipelineRef ContentContext::GetDownsampleTextureGlesPipeline( ContentContextOptions opts) const { diff --git a/engine/src/flutter/impeller/entity/contents/content_context.h b/engine/src/flutter/impeller/entity/contents/content_context.h index 5daf9c1930..f24ae29e89 100644 --- a/engine/src/flutter/impeller/entity/contents/content_context.h +++ b/engine/src/flutter/impeller/entity/contents/content_context.h @@ -41,6 +41,8 @@ #include "impeller/entity/glyph_atlas.frag.h" #include "impeller/entity/glyph_atlas.vert.h" #include "impeller/entity/gradient_fill.vert.h" +#include "impeller/entity/line.frag.h" +#include "impeller/entity/line.vert.h" #include "impeller/entity/linear_gradient_fill.frag.h" #include "impeller/entity/linear_to_srgb_filter.frag.h" #include "impeller/entity/morphology_filter.frag.h" @@ -147,6 +149,7 @@ using FramebufferBlendScreenPipeline = FramebufferBlendPipelineHandle; using FramebufferBlendSoftLightPipeline = FramebufferBlendPipelineHandle; using GaussianBlurPipeline = RenderPipelineHandle; using GlyphAtlasPipeline = RenderPipelineHandle; +using LinePipeline = RenderPipelineHandle; using LinearGradientFillPipeline = GradientPipelineHandle; using LinearGradientSSBOFillPipeline = GradientPipelineHandle; using LinearGradientUniformFillPipeline = GradientPipelineHandle; @@ -352,6 +355,7 @@ class ContentContext { PipelineRef GetFramebufferBlendSoftLightPipeline(ContentContextOptions opts) const; PipelineRef GetGaussianBlurPipeline(ContentContextOptions opts) const; PipelineRef GetGlyphAtlasPipeline(ContentContextOptions opts) const; + PipelineRef GetLinePipeline(ContentContextOptions opts) const; PipelineRef GetLinearGradientFillPipeline(ContentContextOptions opts) const; PipelineRef GetLinearGradientSSBOFillPipeline(ContentContextOptions opts) const; PipelineRef GetLinearGradientUniformFillPipeline(ContentContextOptions opts) const; @@ -645,6 +649,7 @@ class ContentContext { mutable Variants framebuffer_blend_softlight_pipelines_; mutable Variants gaussian_blur_pipelines_; mutable Variants glyph_atlas_pipelines_; + mutable Variants line_pipelines_; mutable Variants linear_gradient_fill_pipelines_; mutable Variants linear_gradient_ssbo_fill_pipelines_; mutable Variants linear_gradient_uniform_fill_pipelines_; diff --git a/engine/src/flutter/impeller/entity/contents/gradient_generator.cc b/engine/src/flutter/impeller/entity/contents/gradient_generator.cc index 440599dfea..f83c1424d9 100644 --- a/engine/src/flutter/impeller/entity/contents/gradient_generator.cc +++ b/engine/src/flutter/impeller/entity/contents/gradient_generator.cc @@ -10,6 +10,7 @@ #include "impeller/core/formats.h" #include "impeller/core/texture.h" #include "impeller/renderer/context.h" +#include "impeller/renderer/texture_util.h" namespace impeller { @@ -25,29 +26,9 @@ std::shared_ptr CreateGradientTexture( texture_descriptor.storage_mode = impeller::StorageMode::kHostVisible; texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt; texture_descriptor.size = {gradient_data.texture_size, 1}; - auto texture = - context->GetResourceAllocator()->CreateTexture(texture_descriptor); - if (!texture) { - FML_DLOG(ERROR) << "Could not create Impeller texture."; - return nullptr; - } - auto data_mapping = - std::make_shared(gradient_data.color_bytes); - auto buffer = - context->GetResourceAllocator()->CreateBufferWithCopy(*data_mapping); - - auto cmd_buffer = context->CreateCommandBuffer(); - auto blit_pass = cmd_buffer->CreateBlitPass(); - blit_pass->AddCopy(DeviceBuffer::AsBufferView(std::move(buffer)), texture); - - if (!blit_pass->EncodeCommands() || - !context->GetCommandQueue()->Submit({std::move(cmd_buffer)}).ok()) { - return nullptr; - } - - texture->SetLabel(impeller::SPrintF("Gradient(%p)", texture.get()).c_str()); - return texture; + return CreateTexture(texture_descriptor, gradient_data.color_bytes, context, + "Gradient"); } std::vector CreateGradientColors(const std::vector& colors, diff --git a/engine/src/flutter/impeller/entity/contents/line_contents.cc b/engine/src/flutter/impeller/entity/contents/line_contents.cc new file mode 100644 index 0000000000..165645f97d --- /dev/null +++ b/engine/src/flutter/impeller/entity/contents/line_contents.cc @@ -0,0 +1,188 @@ +// 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/line_contents.h" +#include "impeller/entity/contents/clip_contents.h" +#include "impeller/entity/contents/color_source_contents.h" +#include "impeller/entity/geometry/rect_geometry.h" +#include "impeller/renderer/texture_util.h" + +namespace impeller { + +using VS = LinePipeline::VertexShader; +using FS = LinePipeline::FragmentShader; + +namespace { +using BindFragmentCallback = std::function; +using PipelineBuilderCallback = + std::function; +using CreateGeometryCallback = + std::function; + +const int32_t kCurveResolution = 32; +const Scalar kSampleRadius = 0.5f; + +struct LineInfo { + Vector3 e0; + Vector3 e1; + Vector3 e2; + Vector3 e3; +}; + +LineInfo CalculateLineInfo(Point p0, Point p1, Scalar width, Scalar radius) { + Vector2 diff = p0 - p1; + float k = 2.0 / ((2.0 * radius + width) * sqrt(diff.Dot(diff))); + + return LineInfo{ + .e0 = Vector3(k * (p0.y - p1.y), // + k * (p1.x - p0.x), // + 1.0 + k * (p0.x * p1.y - p1.x * p0.y)), + .e1 = Vector3( + k * (p1.x - p0.x), // + k * (p1.y - p0.y), // + 1.0 + k * (p0.x * p0.x + p0.y * p0.y - p0.x * p1.x - p0.y * p1.y)), + .e2 = Vector3(k * (p1.y - p0.y), // + k * (p0.x - p1.x), // + 1.0 + k * (p1.x * p0.y - p0.x * p1.y)), + .e3 = Vector3( + k * (p0.x - p1.x), // + k * (p0.y - p1.y), // + 1.0 + k * (p1.x * p1.x + p1.y * p1.y - p0.x * p1.x - p0.y * p1.y)), + }; +} + +uint8_t DoubleToUint8(double x) { + return static_cast(std::clamp(std::round(x * 255.0), 0.0, 255.0)); +} + +/// See also: CreateGradientTexture +std::shared_ptr CreateCurveTexture( + Scalar width, + const std::shared_ptr& context) { + // + impeller::TextureDescriptor texture_descriptor; + texture_descriptor.storage_mode = impeller::StorageMode::kHostVisible; + texture_descriptor.format = PixelFormat::kR8UNormInt; + texture_descriptor.size = {kCurveResolution, 1}; + + std::vector curve_data; + curve_data.reserve(kCurveResolution); + for (int i = 0; i < kCurveResolution; ++i) { + double norm = (static_cast(i) + 1.0) / 32.0; + double loc = norm * (kSampleRadius + width / 2.0); + double den = kSampleRadius * 2.0 + 1.0; + curve_data.push_back(DoubleToUint8(loc / den)); + } + + return CreateTexture(texture_descriptor, curve_data, context, "LineCurve"); +} + +GeometryResult CreateGeometry(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass, + const Geometry* geometry) { + using PerVertexData = LineVertexShader::PerVertexData; + const LineGeometry* line_geometry = + static_cast(geometry); + + auto& transform = entity.GetTransform(); + + Point corners[4]; + if (!LineGeometry::ComputeCorners( + corners, transform, + /*extend_endpoints=*/line_geometry->GetCap() != Cap::kButt, + line_geometry->GetP0(), line_geometry->GetP1(), + line_geometry->GetWidth() + kSampleRadius)) { + return kEmptyResult; + } + + auto& host_buffer = renderer.GetTransientsBuffer(); + + size_t count = 4; + LineInfo line_info = + CalculateLineInfo(line_geometry->GetP0(), line_geometry->GetP1(), + line_geometry->GetWidth(), kSampleRadius); + BufferView vertex_buffer = host_buffer.Emplace( + count * sizeof(PerVertexData), alignof(PerVertexData), + [&corners, &line_info](uint8_t* buffer) { + auto vertices = reinterpret_cast(buffer); + for (auto& corner : corners) { + *vertices++ = { + .position = corner, + .e0 = line_info.e0, + .e1 = line_info.e1, + .e2 = line_info.e2, + .e3 = line_info.e3, + }; + } + }); + + std::shared_ptr curve_texture = + CreateCurveTexture(line_geometry->GetWidth(), renderer.GetContext()); + + SamplerDescriptor sampler_desc; + sampler_desc.min_filter = MinMagFilter::kLinear; + sampler_desc.mag_filter = MinMagFilter::kLinear; + + FS::BindCurve( + pass, curve_texture, + renderer.GetContext()->GetSamplerLibrary()->GetSampler(sampler_desc)); + + return GeometryResult{ + .type = PrimitiveType::kTriangleStrip, + .vertex_buffer = + { + .vertex_buffer = vertex_buffer, + .vertex_count = count, + .index_type = IndexType::kNone, + }, + .transform = entity.GetShaderTransform(pass), + }; +} +} // namespace + +std::unique_ptr LineContents::Make( + std::unique_ptr geometry, + Color color) { + return std::unique_ptr( + new LineContents(std::move(geometry), color)); +} + +LineContents::LineContents(std::unique_ptr geometry, Color color) + : geometry_(std::move(geometry)), color_(color) {} + +bool LineContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + auto& host_buffer = renderer.GetTransientsBuffer(); + + VS::FrameInfo frame_info; + FS::FragInfo frag_info; + frag_info.color = color_.Premultiply(); + + PipelineBuilderCallback pipeline_callback = + [&renderer](ContentContextOptions options) { + return renderer.GetLinePipeline(options); + }; + return ColorSourceContents::DrawGeometry( + this, geometry_.get(), renderer, entity, pass, pipeline_callback, + frame_info, + /*bind_fragment_callback=*/ + [&frag_info, &host_buffer](RenderPass& pass) { + FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info)); + pass.SetCommandLabel("Line"); + return true; + }, + /*force_stencil=*/false, + /*create_geom_callback=*/CreateGeometry); +} + +std::optional LineContents::GetCoverage(const Entity& entity) const { + return geometry_->GetCoverage(entity.GetTransform()); +} + +} // namespace impeller diff --git a/engine/src/flutter/impeller/entity/contents/line_contents.h b/engine/src/flutter/impeller/entity/contents/line_contents.h new file mode 100644 index 0000000000..a04ff0bf11 --- /dev/null +++ b/engine/src/flutter/impeller/entity/contents/line_contents.h @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_IMPELLER_ENTITY_CONTENTS_LINE_CONTENTS_H_ +#define FLUTTER_IMPELLER_ENTITY_CONTENTS_LINE_CONTENTS_H_ + +#include + +#include "flutter/impeller/entity/contents/contents.h" +#include "flutter/impeller/entity/geometry/line_geometry.h" + +namespace impeller { +class LineContents : public Contents { + public: + static std::unique_ptr Make( + std::unique_ptr geometry, + Color color); + + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + std::optional GetCoverage(const Entity& entity) const override; + + private: + explicit LineContents(std::unique_ptr geometry, Color color); + + std::unique_ptr geometry_; + Color color_; +}; +} // namespace impeller + +#endif // FLUTTER_IMPELLER_ENTITY_CONTENTS_LINE_CONTENTS_H_ diff --git a/engine/src/flutter/impeller/entity/contents/line_contents_unittests.cc b/engine/src/flutter/impeller/entity/contents/line_contents_unittests.cc new file mode 100644 index 0000000000..d20b20fcdb --- /dev/null +++ b/engine/src/flutter/impeller/entity/contents/line_contents_unittests.cc @@ -0,0 +1,33 @@ +// 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/line_contents.h" +#include "third_party/googletest/googletest/include/gtest/gtest.h" + +namespace impeller { +namespace testing { + +TEST(LineContents, Create) { + Path path; + Scalar width = 5.0f; + auto geometry = std::make_unique( + /*p0=*/Point{0, 0}, // + /*p1=*/Point{100, 100}, // + /*width=*/width, // + /*cap=*/Cap::kSquare); + std::unique_ptr contents = + LineContents::Make(std::move(geometry), Color(1.f, 0.f, 0.f, 1.f)); + EXPECT_TRUE(contents); + Entity entity; + std::optional coverage = contents->GetCoverage(entity); + EXPECT_TRUE(coverage.has_value()); + if (coverage.has_value()) { + Scalar lip = sqrt((width * width) / 2.f); + EXPECT_EQ(*coverage, + Rect::MakeXYWH(-lip, -lip, 100 + 2 * lip, 100 + 2 * lip)); + } +} + +} // namespace testing +} // namespace impeller diff --git a/engine/src/flutter/impeller/entity/geometry/geometry.h b/engine/src/flutter/impeller/entity/geometry/geometry.h index 257c44d3ae..27a14452fe 100644 --- a/engine/src/flutter/impeller/entity/geometry/geometry.h +++ b/engine/src/flutter/impeller/entity/geometry/geometry.h @@ -119,7 +119,6 @@ class Geometry { return 1.0; } - protected: static GeometryResult ComputePositionGeometry( const ContentContext& renderer, const Tessellator::VertexGenerator& generator, diff --git a/engine/src/flutter/impeller/entity/geometry/line_geometry.cc b/engine/src/flutter/impeller/entity/geometry/line_geometry.cc index 56508b0377..4a58578535 100644 --- a/engine/src/flutter/impeller/entity/geometry/line_geometry.cc +++ b/engine/src/flutter/impeller/entity/geometry/line_geometry.cc @@ -26,13 +26,16 @@ Scalar LineGeometry::ComputePixelHalfWidth(const Matrix& transform, } Vector2 LineGeometry::ComputeAlongVector(const Matrix& transform, - bool allow_zero_length) const { - Scalar stroke_half_width = ComputePixelHalfWidth(transform, width_); + bool allow_zero_length, + Point p0, + Point p1, + Scalar width) { + Scalar stroke_half_width = ComputePixelHalfWidth(transform, width); if (stroke_half_width < kEhCloseEnough) { return {}; } - auto along = p1_ - p0_; + auto along = p1 - p0; Scalar length = along.GetLength(); if (length < kEhCloseEnough) { if (!allow_zero_length) { @@ -47,17 +50,20 @@ Vector2 LineGeometry::ComputeAlongVector(const Matrix& transform, bool LineGeometry::ComputeCorners(Point corners[4], const Matrix& transform, - bool extend_endpoints) const { - auto along = ComputeAlongVector(transform, extend_endpoints); + bool extend_endpoints, + Point p0, + Point p1, + Scalar width) { + auto along = ComputeAlongVector(transform, extend_endpoints, p0, p1, width); if (along.IsZero()) { return false; } auto across = Vector2(along.y, -along.x); - corners[0] = p0_ - across; - corners[1] = p1_ - across; - corners[2] = p0_ + across; - corners[3] = p1_ + across; + corners[0] = p0 - across; + corners[1] = p1 - across; + corners[2] = p0 + across; + corners[3] = p1 + across; if (extend_endpoints) { corners[0] -= along; corners[1] += along; @@ -86,7 +92,8 @@ GeometryResult LineGeometry::GetPositionBuffer(const ContentContext& renderer, } Point corners[4]; - if (!ComputeCorners(corners, transform, cap_ == Cap::kSquare)) { + if (!ComputeCorners(corners, transform, cap_ == Cap::kSquare, p0_, p1_, + width_)) { return kEmptyResult; } @@ -118,7 +125,8 @@ GeometryResult LineGeometry::GetPositionBuffer(const ContentContext& renderer, std::optional LineGeometry::GetCoverage(const Matrix& transform) const { Point corners[4]; // Note: MSAA boolean doesn't matter for coverage computation. - if (!ComputeCorners(corners, transform, cap_ != Cap::kButt)) { + if (!ComputeCorners(corners, transform, cap_ != Cap::kButt, p0_, p1_, + width_)) { return {}; } diff --git a/engine/src/flutter/impeller/entity/geometry/line_geometry.h b/engine/src/flutter/impeller/entity/geometry/line_geometry.h index 340d324243..3eac8e90fb 100644 --- a/engine/src/flutter/impeller/entity/geometry/line_geometry.h +++ b/engine/src/flutter/impeller/entity/geometry/line_geometry.h @@ -25,7 +25,20 @@ class LineGeometry final : public Geometry { Scalar ComputeAlphaCoverage(const Matrix& transform) const override; - private: + // |Geometry| + std::optional GetCoverage(const Matrix& transform) const override; + + Point GetP0() const { return p0_; } + Point GetP1() const { return p1_; } + Scalar GetWidth() const { return width_; } + Cap GetCap() const { return cap_; } + + static Vector2 ComputeAlongVector(const Matrix& transform, + bool allow_zero_length, + Point p0, + Point p1, + Scalar width); + // Computes the 4 corners of a rectangle that defines the line and // possibly extended endpoints which will be rendered under the given // transform, and returns true if such a rectangle is defined. @@ -40,21 +53,19 @@ class LineGeometry final : public Geometry { // if the calling code is planning to draw the round caps on the ends. // // @return true if the transform and width were not degenerate - bool ComputeCorners(Point corners[4], - const Matrix& transform, - bool extend_endpoints) const; - - Vector2 ComputeAlongVector(const Matrix& transform, - bool allow_zero_length) const; + static bool ComputeCorners(Point corners[4], + const Matrix& transform, + bool extend_endpoints, + Point p0, + Point p1, + Scalar width); + private: // |Geometry| GeometryResult GetPositionBuffer(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const override; - // |Geometry| - std::optional GetCoverage(const Matrix& transform) const override; - Point p0_; Point p1_; Scalar width_; diff --git a/engine/src/flutter/impeller/entity/inline_pass_context.cc b/engine/src/flutter/impeller/entity/inline_pass_context.cc index e8ea9487cc..31bb6b360c 100644 --- a/engine/src/flutter/impeller/entity/inline_pass_context.cc +++ b/engine/src/flutter/impeller/entity/inline_pass_context.cc @@ -13,7 +13,7 @@ #include "impeller/entity/entity_pass_target.h" #include "impeller/renderer/command_buffer.h" #include "impeller/renderer/render_pass.h" -#include "impeller/renderer/texture_mipmap.h" +#include "impeller/renderer/texture_util.h" namespace impeller { diff --git a/engine/src/flutter/impeller/entity/shaders/line.frag b/engine/src/flutter/impeller/entity/shaders/line.frag new file mode 100644 index 0000000000..32455a713a --- /dev/null +++ b/engine/src/flutter/impeller/entity/shaders/line.frag @@ -0,0 +1,44 @@ +// 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. + +precision mediump float; + +#include + +uniform FragInfo { + vec4 color; +} +frag_info; + +uniform sampler2D curve; + +highp in vec2 v_position; +// These should be `flat` but that doesn't work in our glsl compiler. It +// shouldn't make any visual difference. +highp in vec3 v_e0; +highp in vec3 v_e1; +highp in vec3 v_e2; +highp in vec3 v_e3; + +out vec4 frag_color; + +float lookup(float x) { + return texture(curve, vec2(x, 0)).r; +} + +float CalculateLine() { + vec3 pos = vec3(v_position.xy, 1.0); + vec4 d = vec4(dot(pos, v_e0), dot(pos, v_e1), dot(pos, v_e2), dot(pos, v_e3)); + + if (any(lessThan(d, vec4(0.0)))) { + return 0.0; + } + + return lookup(min(d.x, d.z)) * lookup(min(d.y, d.w)); +} + +void main() { + float line = CalculateLine(); + frag_color = vec4(frag_info.color.xyz, line); +} diff --git a/engine/src/flutter/impeller/entity/shaders/line.vert b/engine/src/flutter/impeller/entity/shaders/line.vert new file mode 100644 index 0000000000..938bfdc282 --- /dev/null +++ b/engine/src/flutter/impeller/entity/shaders/line.vert @@ -0,0 +1,32 @@ +// 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 FrameInfo { + mat4 mvp; +} +frame_info; + +in vec2 position; + +in vec3 e0; +in vec3 e1; +in vec3 e2; +in vec3 e3; + +out vec2 v_position; +out vec3 v_e0; +out vec3 v_e1; +out vec3 v_e2; +out vec3 v_e3; + +void main() { + gl_Position = frame_info.mvp * vec4(position, 0.0, 1.0); + v_position = position; + v_e0 = e0; + v_e1 = e1; + v_e2 = e2; + v_e3 = e3; +} diff --git a/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc b/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc index 6acc5d7e62..12b5103283 100644 --- a/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc +++ b/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc @@ -155,7 +155,11 @@ void GoldenPlaygroundTest::SetUp() { setenv("VK_ICD_FILENAMES", icd_path.c_str(), 1); std::string test_name = GetTestName(); - bool enable_wide_gamut = test_name.find("WideGamut_") != std::string::npos; + PlaygroundSwitches switches; + switches.enable_wide_gamut = + test_name.find("WideGamut_") != std::string::npos; + switches.flags.antialiased_lines = + test_name.find("ExperimentAntialiasLines_") != std::string::npos; switch (GetParam()) { case PlaygroundBackend::kMetal: if (!DoesSupportWideGamutTests()) { @@ -163,12 +167,16 @@ void GoldenPlaygroundTest::SetUp() { << "This metal device doesn't support wide gamut golden tests."; } pimpl_->screenshotter = - std::make_unique(enable_wide_gamut); + std::make_unique(switches); break; case PlaygroundBackend::kVulkan: { - if (enable_wide_gamut) { + if (switches.enable_wide_gamut) { GTEST_SKIP() << "Vulkan doesn't support wide gamut golden tests."; } + if (switches.flags.antialiased_lines) { + GTEST_SKIP() + << "Vulkan doesn't support antialiased lines golden tests."; + } const std::unique_ptr& playground = GetSharedVulkanPlayground(/*enable_validations=*/true); pimpl_->screenshotter = @@ -176,9 +184,13 @@ void GoldenPlaygroundTest::SetUp() { break; } case PlaygroundBackend::kOpenGLES: { - if (enable_wide_gamut) { + if (switches.enable_wide_gamut) { GTEST_SKIP() << "OpenGLES doesn't support wide gamut golden tests."; } + if (switches.flags.antialiased_lines) { + GTEST_SKIP() + << "OpenGLES doesn't support antialiased lines golden tests."; + } FML_CHECK(::glfwInit() == GLFW_TRUE); PlaygroundSwitches playground_switches; playground_switches.use_angle = true; diff --git a/engine/src/flutter/impeller/golden_tests/golden_tests.cc b/engine/src/flutter/impeller/golden_tests/golden_tests.cc index 12b52506ef..d3456110c6 100644 --- a/engine/src/flutter/impeller/golden_tests/golden_tests.cc +++ b/engine/src/flutter/impeller/golden_tests/golden_tests.cc @@ -49,12 +49,17 @@ bool SaveScreenshot(std::unique_ptr screenshot) { WorkingDirectory::Instance()->GetFilenamePath(filename)); } +PlaygroundSwitches GetPlaygroundSwitches() { + PlaygroundSwitches switches; + switches.enable_wide_gamut = false; + return switches; +} } // namespace class GoldenTests : public ::testing::Test { public: GoldenTests() - : screenshotter_(new MetalScreenshotter(/*enable_wide_gamut=*/false)) {} + : screenshotter_(new MetalScreenshotter(GetPlaygroundSwitches())) {} MetalScreenshotter& Screenshotter() { return *screenshotter_; } diff --git a/engine/src/flutter/impeller/golden_tests/metal_screenshotter.h b/engine/src/flutter/impeller/golden_tests/metal_screenshotter.h index 5b3f7f3661..30cf3d43eb 100644 --- a/engine/src/flutter/impeller/golden_tests/metal_screenshotter.h +++ b/engine/src/flutter/impeller/golden_tests/metal_screenshotter.h @@ -17,7 +17,7 @@ namespace testing { /// playground backend. class MetalScreenshotter : public Screenshotter { public: - explicit MetalScreenshotter(bool enable_wide_gamut); + explicit MetalScreenshotter(const PlaygroundSwitches& switches); std::unique_ptr MakeScreenshot( AiksContext& aiks_context, diff --git a/engine/src/flutter/impeller/golden_tests/metal_screenshotter.mm b/engine/src/flutter/impeller/golden_tests/metal_screenshotter.mm index 5c2877f3e6..051347dfb5 100644 --- a/engine/src/flutter/impeller/golden_tests/metal_screenshotter.mm +++ b/engine/src/flutter/impeller/golden_tests/metal_screenshotter.mm @@ -13,10 +13,8 @@ namespace impeller { namespace testing { -MetalScreenshotter::MetalScreenshotter(bool enable_wide_gamut) { +MetalScreenshotter::MetalScreenshotter(const PlaygroundSwitches& switches) { FML_CHECK(::glfwInit() == GLFW_TRUE); - PlaygroundSwitches switches; - switches.enable_wide_gamut = enable_wide_gamut; playground_ = PlaygroundImpl::Create(PlaygroundBackend::kMetal, switches); } diff --git a/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.cc b/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.cc index e5d446b729..092a07cfd7 100644 --- a/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.cc +++ b/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.cc @@ -133,8 +133,9 @@ std::shared_ptr PlaygroundImplGLES::GetContext() const { return nullptr; } - auto context = ContextGLES::Create( - Flags{}, std::move(gl), ShaderLibraryMappingsForPlayground(), true); + auto context = + ContextGLES::Create(switches_.flags, std::move(gl), + ShaderLibraryMappingsForPlayground(), true); if (!context) { FML_LOG(ERROR) << "Could not create context."; return nullptr; diff --git a/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.mm b/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.mm index 7d00c4aeb3..978f01ab41 100644 --- a/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.mm +++ b/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.mm @@ -77,7 +77,7 @@ PlaygroundImplMTL::PlaygroundImplMTL(PlaygroundSwitches switches) } auto context = ContextMTL::Create( - impeller::Flags{}, ShaderLibraryMappingsForPlayground(), + switches.flags, ShaderLibraryMappingsForPlayground(), is_gpu_disabled_sync_switch_, "Playground Library", switches.enable_wide_gamut ? std::optional(PixelFormat::kB10G10R10A10XR) diff --git a/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.cc b/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.cc index 57ae5be45a..3ea803d954 100644 --- a/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.cc +++ b/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.cc @@ -95,8 +95,9 @@ PlaygroundImplVK::PlaygroundImplVK(PlaygroundSwitches switches) context_settings.enable_validation = switches_.enable_vulkan_validation; context_settings.fatal_missing_validations = switches_.enable_vulkan_validation; + context_settings.flags = switches_.flags; - auto context_vk = ContextVK::Create(Flags{}, std::move(context_settings)); + auto context_vk = ContextVK::Create(std::move(context_settings)); if (!context_vk || !context_vk->IsValid()) { VALIDATION_LOG << "Could not create Vulkan context in the playground."; return; diff --git a/engine/src/flutter/impeller/playground/playground_test.cc b/engine/src/flutter/impeller/playground/playground_test.cc index 2e5136336a..e6f9f491d6 100644 --- a/engine/src/flutter/impeller/playground/playground_test.cc +++ b/engine/src/flutter/impeller/playground/playground_test.cc @@ -65,6 +65,9 @@ void PlaygroundTest::SetUp() { return; } + switches.flags.antialiased_lines = + test_name.find("ExperimentAntialiasLines/") != std::string::npos; + SetupContext(GetParam(), switches); SetupWindow(); } diff --git a/engine/src/flutter/impeller/playground/switches.h b/engine/src/flutter/impeller/playground/switches.h index 460bd8bf34..29f2684726 100644 --- a/engine/src/flutter/impeller/playground/switches.h +++ b/engine/src/flutter/impeller/playground/switches.h @@ -9,6 +9,7 @@ #include #include "flutter/fml/command_line.h" +#include "impeller/base/flags.h" namespace impeller { @@ -34,6 +35,8 @@ struct PlaygroundSwitches { bool enable_wide_gamut = false; + Flags flags; + PlaygroundSwitches(); explicit PlaygroundSwitches(const fml::CommandLine& args); diff --git a/engine/src/flutter/impeller/renderer/BUILD.gn b/engine/src/flutter/impeller/renderer/BUILD.gn index fdc2ae7d98..5fdc59a92b 100644 --- a/engine/src/flutter/impeller/renderer/BUILD.gn +++ b/engine/src/flutter/impeller/renderer/BUILD.gn @@ -73,8 +73,8 @@ impeller_component("renderer") { "snapshot.h", "surface.cc", "surface.h", - "texture_mipmap.cc", - "texture_mipmap.h", + "texture_util.cc", + "texture_util.h", "vertex_buffer_builder.cc", "vertex_buffer_builder.h", "vertex_descriptor.cc", diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk_unittests.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk_unittests.cc index fe1ff030cb..4e40da07f5 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk_unittests.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk_unittests.cc @@ -34,7 +34,7 @@ std::shared_ptr CreateContext() { settings.enable_gpu_tracing = false; settings.enable_surface_control = false; - return ContextVK::Create(impeller::Flags{}, std::move(settings)); + return ContextVK::Create(std::move(settings)); } TEST(AndroidVulkanTest, CanImportRGBA) { diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc index 9956c84745..5b8400d562 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc @@ -102,9 +102,8 @@ static std::optional PickQueue(const vk::PhysicalDevice& device, return std::nullopt; } -std::shared_ptr ContextVK::Create(const Flags& flags, - Settings settings) { - auto context = std::shared_ptr(new ContextVK(flags)); +std::shared_ptr ContextVK::Create(Settings settings) { + auto context = std::shared_ptr(new ContextVK(settings.flags)); context->Setup(std::move(settings)); if (!context->IsValid()) { return nullptr; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.h index 8660763311..39dae91aea 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.h @@ -85,6 +85,7 @@ class ContextVK final : public Context, bool enable_surface_control = false; /// If validations are requested but cannot be enabled, log a fatal error. bool fatal_missing_validations = false; + Flags flags; std::optional embedder_data; @@ -98,8 +99,7 @@ class ContextVK final : public Context, /// Visible for testing. static size_t ChooseThreadCountForWorkers(size_t hardware_concurrency); - static std::shared_ptr Create(const Flags& flags, - Settings settings); + static std::shared_ptr Create(Settings settings); uint64_t GetHash() const { return hash_; } diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/test/mock_vulkan.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/test/mock_vulkan.cc index c3a3540f00..0ea8beaec3 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/test/mock_vulkan.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/test/mock_vulkan.cc @@ -955,8 +955,7 @@ std::shared_ptr MockVulkanContextBuilder::Build() { g_format_properties_callback = format_properties_callback_; g_physical_device_properties_callback = physical_properties_callback_; settings.embedder_data = embedder_data_; - std::shared_ptr result = - ContextVK::Create(Flags{}, std::move(settings)); + std::shared_ptr result = ContextVK::Create(std::move(settings)); return result; } diff --git a/engine/src/flutter/impeller/renderer/texture_mipmap.cc b/engine/src/flutter/impeller/renderer/texture_mipmap.cc deleted file mode 100644 index 0c332edc8b..0000000000 --- a/engine/src/flutter/impeller/renderer/texture_mipmap.cc +++ /dev/null @@ -1,27 +0,0 @@ -// 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/renderer/texture_mipmap.h" -#include "impeller/renderer/blit_pass.h" -#include "impeller/renderer/command_buffer.h" - -namespace impeller { - -fml::Status AddMipmapGeneration( - const std::shared_ptr& command_buffer, - const std::shared_ptr& context, - const std::shared_ptr& texture) { - std::shared_ptr blit_pass = command_buffer->CreateBlitPass(); - bool success = blit_pass->GenerateMipmap(texture); - if (!success) { - return fml::Status(fml::StatusCode::kUnknown, ""); - } - success = blit_pass->EncodeCommands(); - if (!success) { - return fml::Status(fml::StatusCode::kUnknown, ""); - } - return fml::Status(); -} - -} // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/texture_util.cc b/engine/src/flutter/impeller/renderer/texture_util.cc new file mode 100644 index 0000000000..b50f6f0de8 --- /dev/null +++ b/engine/src/flutter/impeller/renderer/texture_util.cc @@ -0,0 +1,53 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/texture_util.h" +#include "impeller/renderer/blit_pass.h" +#include "impeller/renderer/command_buffer.h" + +namespace impeller { + +std::shared_ptr CreateTexture( + const TextureDescriptor& texture_descriptor, + const std::vector& data, + const std::shared_ptr& context, + std::string_view debug_label) { + std::shared_ptr texture = + context->GetResourceAllocator()->CreateTexture(texture_descriptor); + + auto data_mapping = + std::make_shared(data.data(), data.size()); + std::shared_ptr buffer = + context->GetResourceAllocator()->CreateBufferWithCopy(*data_mapping); + + std::shared_ptr cmd_buffer = context->CreateCommandBuffer(); + std::shared_ptr blit_pass = cmd_buffer->CreateBlitPass(); + blit_pass->AddCopy(DeviceBuffer::AsBufferView(std::move(buffer)), texture); + + if (!blit_pass->EncodeCommands() || + !context->GetCommandQueue()->Submit({std::move(cmd_buffer)}).ok()) { + return nullptr; + } + + texture->SetLabel(debug_label); + return texture; +} + +fml::Status AddMipmapGeneration( + const std::shared_ptr& command_buffer, + const std::shared_ptr& context, + const std::shared_ptr& texture) { + std::shared_ptr blit_pass = command_buffer->CreateBlitPass(); + bool success = blit_pass->GenerateMipmap(texture); + if (!success) { + return fml::Status(fml::StatusCode::kUnknown, ""); + } + success = blit_pass->EncodeCommands(); + if (!success) { + return fml::Status(fml::StatusCode::kUnknown, ""); + } + return fml::Status(); +} + +} // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/texture_mipmap.h b/engine/src/flutter/impeller/renderer/texture_util.h similarity index 62% rename from engine/src/flutter/impeller/renderer/texture_mipmap.h rename to engine/src/flutter/impeller/renderer/texture_util.h index a968c2382f..df4fd16fc7 100644 --- a/engine/src/flutter/impeller/renderer/texture_mipmap.h +++ b/engine/src/flutter/impeller/renderer/texture_util.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_IMPELLER_RENDERER_TEXTURE_MIPMAP_H_ -#define FLUTTER_IMPELLER_RENDERER_TEXTURE_MIPMAP_H_ +#ifndef FLUTTER_IMPELLER_RENDERER_TEXTURE_UTIL_H_ +#define FLUTTER_IMPELLER_RENDERER_TEXTURE_UTIL_H_ #include "flutter/fml/status.h" #include "impeller/core/texture.h" @@ -12,6 +12,12 @@ namespace impeller { +std::shared_ptr CreateTexture( + const TextureDescriptor& texture_descriptor, + const std::vector& data, + const std::shared_ptr& context, + std::string_view debug_label); + /// Adds a blit command to the render pass. [[nodiscard]] fml::Status AddMipmapGeneration( const std::shared_ptr& command_buffer, @@ -20,4 +26,4 @@ namespace impeller { } // namespace impeller -#endif // FLUTTER_IMPELLER_RENDERER_TEXTURE_MIPMAP_H_ +#endif // FLUTTER_IMPELLER_RENDERER_TEXTURE_UTIL_H_ diff --git a/engine/src/flutter/impeller/toolkit/interop/backend/vulkan/context_vk.cc b/engine/src/flutter/impeller/toolkit/interop/backend/vulkan/context_vk.cc index 92e192010c..131d75e128 100644 --- a/engine/src/flutter/impeller/toolkit/interop/backend/vulkan/context_vk.cc +++ b/engine/src/flutter/impeller/toolkit/interop/backend/vulkan/context_vk.cc @@ -53,8 +53,9 @@ ScopedObject ContextVK::Create(const Settings& settings) { impeller_settings.enable_validation = true; sContextVKProcAddressCallback = settings.instance_proc_address_callback; impeller_settings.proc_address_callback = ContextVKGetInstanceProcAddress; - auto impeller_context = impeller::ContextVK::Create( - impeller::Flags{}, std::move(impeller_settings)); + impeller_settings.flags = impeller::Flags{}; + auto impeller_context = + impeller::ContextVK::Create(std::move(impeller_settings)); sContextVKProcAddressCallback = nullptr; if (!impeller_context) { VALIDATION_LOG << "Could not create Impeller context."; diff --git a/engine/src/flutter/impeller/tools/malioc.json b/engine/src/flutter/impeller/tools/malioc.json index c58ca59025..97ef7c75ec 100644 --- a/engine/src/flutter/impeller/tools/malioc.json +++ b/engine/src/flutter/impeller/tools/malioc.json @@ -4418,6 +4418,278 @@ } } }, + "flutter/impeller/entity/gles/line.frag.gles": { + "Mali-G78": { + "core": "Mali-G78", + "filename": "flutter/impeller/entity/gles/line.frag.gles", + "has_side_effects": false, + "has_uniform_computation": false, + "modifies_coverage": false, + "reads_color_buffer": false, + "type": "Fragment", + "uses_late_zs_test": false, + "uses_late_zs_update": false, + "variants": { + "Main": { + "fp16_arithmetic": 6, + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + "varying" + ], + "longest_path_cycles": [ + 0.21875, + 0.140625, + 0.21875, + 0.0, + 0.0, + 1.75, + 0.5 + ], + "pipelines": [ + "arith_total", + "arith_fma", + "arith_cvt", + "arith_sfu", + "load_store", + "varying", + "texture" + ], + "shortest_path_bound_pipelines": [ + "varying" + ], + "shortest_path_cycles": [ + 0.140625, + 0.125, + 0.140625, + 0.0, + 0.0, + 1.75, + 0.0 + ], + "total_bound_pipelines": [ + "varying" + ], + "total_cycles": [ + 0.21875, + 0.140625, + 0.21875, + 0.0, + 0.0, + 1.75, + 0.5 + ] + }, + "stack_spill_bytes": 0, + "thread_occupancy": 100, + "uniform_registers_used": 4, + "work_registers_used": 24 + } + } + }, + "Mali-T880": { + "core": "Mali-T880", + "filename": "flutter/impeller/entity/gles/line.frag.gles", + "has_uniform_computation": false, + "type": "Fragment", + "variants": { + "Main": { + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + "load_store" + ], + "longest_path_cycles": [ + 3.299999952316284, + 5.0, + 2.0 + ], + "pipelines": [ + "arithmetic", + "load_store", + "texture" + ], + "shortest_path_bound_pipelines": [ + "load_store" + ], + "shortest_path_cycles": [ + 2.309999942779541, + 5.0, + 0.0 + ], + "total_bound_pipelines": [ + "load_store" + ], + "total_cycles": [ + 3.6666667461395264, + 5.0, + 2.0 + ] + }, + "thread_occupancy": 100, + "uniform_registers_used": 1, + "work_registers_used": 3 + } + } + } + }, + "flutter/impeller/entity/gles/line.vert.gles": { + "Mali-G78": { + "core": "Mali-G78", + "filename": "flutter/impeller/entity/gles/line.vert.gles", + "has_uniform_computation": false, + "type": "Vertex", + "variants": { + "Position": { + "fp16_arithmetic": 0, + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + "load_store" + ], + "longest_path_cycles": [ + 0.140625, + 0.140625, + 0.0, + 0.0, + 2.0, + 0.0 + ], + "pipelines": [ + "arith_total", + "arith_fma", + "arith_cvt", + "arith_sfu", + "load_store", + "texture" + ], + "shortest_path_bound_pipelines": [ + "load_store" + ], + "shortest_path_cycles": [ + 0.140625, + 0.140625, + 0.0, + 0.0, + 2.0, + 0.0 + ], + "total_bound_pipelines": [ + "load_store" + ], + "total_cycles": [ + 0.140625, + 0.140625, + 0.0, + 0.0, + 2.0, + 0.0 + ] + }, + "stack_spill_bytes": 0, + "thread_occupancy": 100, + "uniform_registers_used": 20, + "work_registers_used": 32 + }, + "Varying": { + "fp16_arithmetic": null, + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + "load_store" + ], + "longest_path_cycles": [ + 0.09375, + 0.0, + 0.09375, + 0.0, + 10.0, + 0.0 + ], + "pipelines": [ + "arith_total", + "arith_fma", + "arith_cvt", + "arith_sfu", + "load_store", + "texture" + ], + "shortest_path_bound_pipelines": [ + "load_store" + ], + "shortest_path_cycles": [ + 0.09375, + 0.0, + 0.09375, + 0.0, + 10.0, + 0.0 + ], + "total_bound_pipelines": [ + "load_store" + ], + "total_cycles": [ + 0.09375, + 0.0, + 0.09375, + 0.0, + 10.0, + 0.0 + ] + }, + "stack_spill_bytes": 0, + "thread_occupancy": 100, + "uniform_registers_used": 8, + "work_registers_used": 19 + } + } + }, + "Mali-T880": { + "core": "Mali-T880", + "filename": "flutter/impeller/entity/gles/line.vert.gles", + "has_uniform_computation": false, + "type": "Vertex", + "variants": { + "Main": { + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + "load_store" + ], + "longest_path_cycles": [ + 2.640000104904175, + 12.0, + 0.0 + ], + "pipelines": [ + "arithmetic", + "load_store", + "texture" + ], + "shortest_path_bound_pipelines": [ + "load_store" + ], + "shortest_path_cycles": [ + 2.640000104904175, + 12.0, + 0.0 + ], + "total_bound_pipelines": [ + "load_store" + ], + "total_cycles": [ + 2.6666667461395264, + 12.0, + 0.0 + ] + }, + "thread_occupancy": 100, + "uniform_registers_used": 5, + "work_registers_used": 2 + } + } + } + }, "flutter/impeller/entity/gles/linear_gradient_fill.frag.gles": { "Mali-G78": { "core": "Mali-G78", @@ -7966,6 +8238,188 @@ } } }, + "flutter/impeller/entity/line.frag.vkspv": { + "Mali-G78": { + "core": "Mali-G78", + "filename": "flutter/impeller/entity/line.frag.vkspv", + "has_side_effects": false, + "has_uniform_computation": true, + "modifies_coverage": false, + "reads_color_buffer": false, + "type": "Fragment", + "uses_late_zs_test": false, + "uses_late_zs_update": false, + "variants": { + "Main": { + "fp16_arithmetic": 15, + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + "varying" + ], + "longest_path_cycles": [ + 0.1875, + 0.140625, + 0.1875, + 0.1875, + 0.0, + 1.75, + 0.5 + ], + "pipelines": [ + "arith_total", + "arith_fma", + "arith_cvt", + "arith_sfu", + "load_store", + "varying", + "texture" + ], + "shortest_path_bound_pipelines": [ + "varying" + ], + "shortest_path_cycles": [ + 0.1875, + 0.125, + 0.140625, + 0.1875, + 0.0, + 1.75, + 0.0 + ], + "total_bound_pipelines": [ + "varying" + ], + "total_cycles": [ + 0.1875, + 0.140625, + 0.1875, + 0.1875, + 0.0, + 1.75, + 0.5 + ] + }, + "stack_spill_bytes": 0, + "thread_occupancy": 100, + "uniform_registers_used": 6, + "work_registers_used": 18 + } + } + } + }, + "flutter/impeller/entity/line.vert.vkspv": { + "Mali-G78": { + "core": "Mali-G78", + "filename": "flutter/impeller/entity/line.vert.vkspv", + "has_uniform_computation": true, + "type": "Vertex", + "variants": { + "Position": { + "fp16_arithmetic": 0, + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + "load_store" + ], + "longest_path_cycles": [ + 0.125, + 0.125, + 0.0, + 0.0, + 2.0, + 0.0 + ], + "pipelines": [ + "arith_total", + "arith_fma", + "arith_cvt", + "arith_sfu", + "load_store", + "texture" + ], + "shortest_path_bound_pipelines": [ + "load_store" + ], + "shortest_path_cycles": [ + 0.125, + 0.125, + 0.0, + 0.0, + 2.0, + 0.0 + ], + "total_bound_pipelines": [ + "load_store" + ], + "total_cycles": [ + 0.125, + 0.125, + 0.0, + 0.0, + 2.0, + 0.0 + ] + }, + "stack_spill_bytes": 0, + "thread_occupancy": 100, + "uniform_registers_used": 28, + "work_registers_used": 32 + }, + "Varying": { + "fp16_arithmetic": null, + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + "load_store" + ], + "longest_path_cycles": [ + 0.046875, + 0.0, + 0.046875, + 0.0, + 10.0, + 0.0 + ], + "pipelines": [ + "arith_total", + "arith_fma", + "arith_cvt", + "arith_sfu", + "load_store", + "texture" + ], + "shortest_path_bound_pipelines": [ + "load_store" + ], + "shortest_path_cycles": [ + 0.046875, + 0.0, + 0.046875, + 0.0, + 10.0, + 0.0 + ], + "total_bound_pipelines": [ + "load_store" + ], + "total_cycles": [ + 0.046875, + 0.0, + 0.046875, + 0.0, + 10.0, + 0.0 + ] + }, + "stack_spill_bytes": 0, + "thread_occupancy": 100, + "uniform_registers_used": 20, + "work_registers_used": 22 + } + } + } + }, "flutter/impeller/entity/linear_gradient_fill.frag.vkspv": { "Mali-G78": { "core": "Mali-G78", diff --git a/engine/src/flutter/shell/common/shell_test_platform_view_metal.mm b/engine/src/flutter/shell/common/shell_test_platform_view_metal.mm index 4639b2a46b..8383cf0625 100644 --- a/engine/src/flutter/shell/common/shell_test_platform_view_metal.mm +++ b/engine/src/flutter/shell/common/shell_test_platform_view_metal.mm @@ -40,7 +40,8 @@ ShellTestPlatformViewMetal::ShellTestPlatformViewMetal( id device = nil; if (GetSettings().enable_impeller) { impeller_context_ = - [[FlutterDarwinContextMetalImpeller alloc] init:is_gpu_disabled_sync_switch]; + [[FlutterDarwinContextMetalImpeller alloc] init:impeller::Flags {} + gpuDisabledSyncSwitch:is_gpu_disabled_sync_switch]; FML_CHECK(impeller_context_.context); device = impeller_context_.context->GetMTLDevice(); } else { diff --git a/engine/src/flutter/shell/common/switches.cc b/engine/src/flutter/shell/common/switches.cc index ff45ade1e4..eeec6951d4 100644 --- a/engine/src/flutter/shell/common/switches.cc +++ b/engine/src/flutter/shell/common/switches.cc @@ -534,6 +534,8 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { settings.impeller_enable_lazy_shader_mode = command_line.HasOption(FlagForSwitch(Switch::ImpellerLazyShaderMode)); + settings.impeller_antialiased_lines = + command_line.HasOption(FlagForSwitch(Switch::ImpellerAntialiasLines)); return settings; } diff --git a/engine/src/flutter/shell/common/switches.h b/engine/src/flutter/shell/common/switches.h index 02c7d1ec75..528d3e9f07 100644 --- a/engine/src/flutter/shell/common/switches.h +++ b/engine/src/flutter/shell/common/switches.h @@ -304,6 +304,9 @@ DEF_SWITCH(ImpellerLazyShaderMode, "impeller-lazy-shader-mode", "Whether to defer initialization of all required PSOs for the " "Impeller backend. Defaults to false.") +DEF_SWITCH(ImpellerAntialiasLines, + "impeller-antialias-lines", + "Experimental flag to test drawing lines with antialiasing.") DEF_SWITCHES_END void PrintUsage(const std::string& executable_name); diff --git a/engine/src/flutter/shell/platform/android/android_context_vk_impeller.cc b/engine/src/flutter/shell/platform/android/android_context_vk_impeller.cc index e2b3edefca..98153318d1 100644 --- a/engine/src/flutter/shell/platform/android/android_context_vk_impeller.cc +++ b/engine/src/flutter/shell/platform/android/android_context_vk_impeller.cc @@ -48,12 +48,9 @@ static std::shared_ptr CreateImpellerContext( settings.enable_validation = p_settings.enable_validation; settings.enable_gpu_tracing = p_settings.enable_gpu_tracing; settings.enable_surface_control = p_settings.enable_surface_control; + settings.flags = p_settings.impeller_flags; - auto context = impeller::ContextVK::Create( - impeller::Flags{ - .lazy_shader_mode = p_settings.enable_lazy_shader_mode, - }, - std::move(settings)); + auto context = impeller::ContextVK::Create(std::move(settings)); if (!p_settings.quiet) { if (context && impeller::CapabilitiesVK::Cast(*context->GetCapabilities()) diff --git a/engine/src/flutter/shell/platform/android/context/android_context.h b/engine/src/flutter/shell/platform/android/context/android_context.h index 0e4ea0cbdc..e2ac4e97fa 100644 --- a/engine/src/flutter/shell/platform/android/context/android_context.h +++ b/engine/src/flutter/shell/platform/android/context/android_context.h @@ -6,6 +6,7 @@ #define FLUTTER_SHELL_PLATFORM_ANDROID_CONTEXT_ANDROID_CONTEXT_H_ #include "flutter/fml/macros.h" +#include "flutter/impeller/base/flags.h" #include "flutter/impeller/renderer/context.h" #include "flutter/shell/platform/android/android_rendering_selector.h" #include "third_party/skia/include/gpu/ganesh/GrDirectContext.h" @@ -25,8 +26,8 @@ class AndroidContext { bool enable_validation = false; bool enable_gpu_tracing = false; bool enable_surface_control = false; - bool enable_lazy_shader_mode = false; bool quiet = false; + impeller::Flags impeller_flags; }; AndroidRenderingAPI RenderingApi() const; diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java index dedd4de0c0..73b28ecfee 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java @@ -53,6 +53,8 @@ public class FlutterLoader { "io.flutter.embedding.android.EnableSurfaceControl"; private static final String IMPELLER_LAZY_SHADER_MODE = "io.flutter.embedding.android.ImpellerLazyShaderInitialization"; + private static final String IMPELLER_ANTIALIAS_LINES = + "io.flutter.embedding.android.ImpellerAntialiasLines"; /** * Set whether leave or clean up the VM after the last shell shuts down. It can be set from app's @@ -382,6 +384,9 @@ public class FlutterLoader { if (metaData.getBoolean(IMPELLER_LAZY_SHADER_MODE)) { shellArgs.add("--impeller-lazy-shader-mode"); } + if (metaData.getBoolean(IMPELLER_ANTIALIAS_LINES)) { + shellArgs.add("--impeller-antialias-lines"); + } } final String leakVM = isLeakVM(metaData) ? "true" : "false"; diff --git a/engine/src/flutter/shell/platform/android/platform_view_android.cc b/engine/src/flutter/shell/platform/android/platform_view_android.cc index eb51dbca1d..caeb2fe46c 100644 --- a/engine/src/flutter/shell/platform/android/platform_view_android.cc +++ b/engine/src/flutter/shell/platform/android/platform_view_android.cc @@ -53,8 +53,10 @@ AndroidContext::ContextSettings CreateContextSettings( settings.enable_gpu_tracing = p_settings.enable_vulkan_gpu_tracing; settings.enable_validation = p_settings.enable_vulkan_validation; settings.enable_surface_control = p_settings.enable_surface_control; - settings.enable_lazy_shader_mode = + settings.impeller_flags.lazy_shader_mode = p_settings.impeller_enable_lazy_shader_mode; + settings.impeller_flags.antialiased_lines = + p_settings.impeller_antialiased_lines; return settings; } } // namespace diff --git a/engine/src/flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.h b/engine/src/flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.h index e4dba2102f..e004fc2abf 100644 --- a/engine/src/flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.h +++ b/engine/src/flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.h @@ -25,7 +25,9 @@ NS_ASSUME_NONNULL_BEGIN /** * Initializes a FlutterDarwinContextMetalImpeller. */ -- (instancetype)init:(const std::shared_ptr&)is_gpu_disabled_sync_switch; +- (instancetype)init:(const impeller::Flags&)flags + gpuDisabledSyncSwitch: + (const std::shared_ptr&)is_gpu_disabled_sync_switch; /** * Creates an external texture with the specified ID and contents. diff --git a/engine/src/flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.mm b/engine/src/flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.mm index 36d95acb09..b893da311d 100644 --- a/engine/src/flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.mm +++ b/engine/src/flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.mm @@ -16,6 +16,7 @@ FLUTTER_ASSERT_ARC static std::shared_ptr CreateImpellerContext( + const impeller::Flags& flags, const std::shared_ptr& is_gpu_disabled_sync_switch) { std::vector> shader_mappings = { std::make_shared(impeller_entity_shaders_data, @@ -25,16 +26,18 @@ static std::shared_ptr CreateImpellerContext( std::make_shared(impeller_framebuffer_blend_shaders_data, impeller_framebuffer_blend_shaders_length), }; - return impeller::ContextMTL::Create(impeller::Flags{}, shader_mappings, - is_gpu_disabled_sync_switch, "Impeller Library"); + return impeller::ContextMTL::Create(flags, shader_mappings, is_gpu_disabled_sync_switch, + "Impeller Library"); } @implementation FlutterDarwinContextMetalImpeller -- (instancetype)init:(const std::shared_ptr&)is_gpu_disabled_sync_switch { +- (instancetype)init:(const impeller::Flags&)flags + gpuDisabledSyncSwitch: + (const std::shared_ptr&)is_gpu_disabled_sync_switch { self = [super init]; if (self != nil) { - _context = CreateImpellerContext(is_gpu_disabled_sync_switch); + _context = CreateImpellerContext(flags, is_gpu_disabled_sync_switch); FML_CHECK(_context) << "Could not create Metal Impeller Context."; id device = _context->GetMTLDevice(); FML_CHECK(device) << "Could not acquire Metal device."; diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm index 44459eb55b..132c581bc1 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm @@ -177,6 +177,9 @@ flutter::Settings FLTDefaultSettingsForBundle(NSBundle* bundle, NSProcessInfo* p settings.enable_wide_gamut = enableWideGamut; #endif + NSNumber* nsAntialiasLines = [mainBundle objectForInfoDictionaryKey:@"FLTAntialiasLines"]; + settings.impeller_antialiased_lines = (nsAntialiasLines ? nsAntialiasLines.boolValue : NO); + settings.warn_on_impeller_opt_out = true; NSNumber* enableTraceSystrace = [mainBundle objectForInfoDictionaryKey:@"FLTTraceSystrace"]; diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_context.h b/engine/src/flutter/shell/platform/darwin/ios/ios_context.h index 24a887a4d8..da8df41347 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_context.h +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_context.h @@ -9,6 +9,7 @@ #include "flutter/common/graphics/gl_context_switch.h" #include "flutter/common/graphics/texture.h" +#include "flutter/common/settings.h" #include "flutter/fml/concurrent_message_loop.h" #include "flutter/fml/macros.h" #include "flutter/fml/synchronization/sync_switch.h" @@ -54,7 +55,8 @@ class IOSContext { static std::unique_ptr Create( IOSRenderingAPI api, IOSRenderingBackend backend, - const std::shared_ptr& is_gpu_disabled_sync_switch); + const std::shared_ptr& is_gpu_disabled_sync_switch, + const Settings& settings); //---------------------------------------------------------------------------- /// @brief Collects the context object. This must happen on the thread on diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_context.mm b/engine/src/flutter/shell/platform/darwin/ios/ios_context.mm index 9517ed8544..7db29ea3b0 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_context.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_context.mm @@ -21,7 +21,8 @@ IOSContext::~IOSContext() = default; std::unique_ptr IOSContext::Create( IOSRenderingAPI api, IOSRenderingBackend backend, - const std::shared_ptr& is_gpu_disabled_sync_switch) { + const std::shared_ptr& is_gpu_disabled_sync_switch, + const Settings& settings) { switch (api) { case IOSRenderingAPI::kSoftware: FML_LOG(IMPORTANT) @@ -37,7 +38,7 @@ std::unique_ptr IOSContext::Create( FML_LOG(FATAL) << "Impeller opt-out unavailable."; return nullptr; case IOSRenderingBackend::kImpeller: - return std::make_unique(is_gpu_disabled_sync_switch); + return std::make_unique(settings, is_gpu_disabled_sync_switch); } default: break; diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_context_metal_impeller.h b/engine/src/flutter/shell/platform/darwin/ios/ios_context_metal_impeller.h index debed104dd..00f489199e 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_context_metal_impeller.h +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_context_metal_impeller.h @@ -21,6 +21,7 @@ namespace flutter { class IOSContextMetalImpeller final : public IOSContext { public: explicit IOSContextMetalImpeller( + const Settings& settings, const std::shared_ptr& is_gpu_disabled_sync_switch); ~IOSContextMetalImpeller(); diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_context_metal_impeller.mm b/engine/src/flutter/shell/platform/darwin/ios/ios_context_metal_impeller.mm index 3b9aa5c139..83eda049d0 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_context_metal_impeller.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_context_metal_impeller.mm @@ -12,11 +12,20 @@ FLUTTER_ASSERT_ARC namespace flutter { +namespace { +impeller::Flags SettingsToFlags(const Settings& settings) { + return impeller::Flags{ + .antialiased_lines = settings.impeller_antialiased_lines, + }; +} +} // namespace IOSContextMetalImpeller::IOSContextMetalImpeller( + const Settings& settings, const std::shared_ptr& is_gpu_disabled_sync_switch) - : darwin_context_metal_impeller_( - [[FlutterDarwinContextMetalImpeller alloc] init:is_gpu_disabled_sync_switch]) { + : darwin_context_metal_impeller_([[FlutterDarwinContextMetalImpeller alloc] + init:SettingsToFlags(settings) + gpuDisabledSyncSwitch:is_gpu_disabled_sync_switch]) { if (darwin_context_metal_impeller_.context) { aiks_context_ = std::make_shared( darwin_context_metal_impeller_.context, impeller::TypographerContextSkia::Make()); diff --git a/engine/src/flutter/shell/platform/darwin/ios/platform_view_ios.mm b/engine/src/flutter/shell/platform/darwin/ios/platform_view_ios.mm index b9b33e86f7..15f6223d70 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/platform_view_ios.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/platform_view_ios.mm @@ -64,7 +64,8 @@ PlatformViewIOS::PlatformViewIOS( delegate.OnPlatformViewGetSettings().enable_impeller ? IOSRenderingBackend::kImpeller : IOSRenderingBackend::kSkia, - is_gpu_disabled_sync_switch), + is_gpu_disabled_sync_switch, + delegate.OnPlatformViewGetSettings()), platform_views_controller, task_runners) {} diff --git a/engine/src/flutter/shell/platform/embedder/embedder_surface_vulkan_impeller.cc b/engine/src/flutter/shell/platform/embedder/embedder_surface_vulkan_impeller.cc index 6a55973a80..1017903897 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_surface_vulkan_impeller.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_surface_vulkan_impeller.cc @@ -71,8 +71,7 @@ EmbedderSurfaceVulkanImpeller::EmbedderSurfaceVulkanImpeller( } settings.embedder_data = data; - context_ = - impeller::ContextVK::Create(impeller::Flags{}, std::move(settings)); + context_ = impeller::ContextVK::Create(std::move(settings)); if (!context_) { FML_LOG(ERROR) << "Failed to initialize Vulkan Context."; return; diff --git a/engine/src/flutter/shell/testing/tester_main.cc b/engine/src/flutter/shell/testing/tester_main.cc index 9a740e342e..95bdeb52be 100644 --- a/engine/src/flutter/shell/testing/tester_main.cc +++ b/engine/src/flutter/shell/testing/tester_main.cc @@ -74,11 +74,11 @@ bool ImpellerVulkanContextHolder::Initialize(bool enable_validation) { context_settings.shader_libraries_data = ShaderLibraryMappings(); context_settings.cache_directory = fml::paths::GetCachesDirectory(); context_settings.enable_validation = enable_validation; + // Enable lazy shader mode for faster test execution as most tests + // will never render anything at all. + context_settings.flags.lazy_shader_mode = true; - context = impeller::ContextVK::Create( - // Enable lazy shader mode for faster test execution as most tests - // will never render anything at all. - impeller::Flags{.lazy_shader_mode = true}, std::move(context_settings)); + context = impeller::ContextVK::Create(std::move(context_settings)); if (!context || !context->IsValid()) { VALIDATION_LOG << "Could not create Vulkan context."; return false; diff --git a/engine/src/flutter/testing/impeller_golden_tests_output.txt b/engine/src/flutter/testing/impeller_golden_tests_output.txt index a4e7071fec..8d3f2b1b07 100644 --- a/engine/src/flutter/testing/impeller_golden_tests_output.txt +++ b/engine/src/flutter/testing/impeller_golden_tests_output.txt @@ -895,6 +895,7 @@ impeller_Play_AiksTest_SetContentsWithRegion_Vulkan.png impeller_Play_AiksTest_SiblingSaveLayerBoundsAreRespected_Metal.png impeller_Play_AiksTest_SiblingSaveLayerBoundsAreRespected_OpenGLES.png impeller_Play_AiksTest_SiblingSaveLayerBoundsAreRespected_Vulkan.png +impeller_Play_AiksTest_SimpleExperimentAntialiasLines_Metal.png impeller_Play_AiksTest_SolidColorCircleMaskBlurTinySigma_Metal.png impeller_Play_AiksTest_SolidColorCircleMaskBlurTinySigma_OpenGLES.png impeller_Play_AiksTest_SolidColorCircleMaskBlurTinySigma_Vulkan.png