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].
<!-- Links -->
[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
diff --git a/engine/src/flutter/ci/licenses_golden/excluded_files b/engine/src/flutter/ci/licenses_golden/excluded_files
index efc4e89e..a7b219a 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 f4ec6ae..000285a 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.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/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.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.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/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.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 f25d302..8f41eb2 100644
--- a/engine/src/flutter/common/settings.h
+++ b/engine/src/flutter/common/settings.h
@@ -231,6 +231,9 @@
// 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 fd3201c..006952d 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 @@
void DlMetalSurfaceProvider::InitScreenShotter() const {
if (!snapshotter_) {
- snapshotter_.reset(new MetalScreenshotter(/*enable_wide_gamut=*/false));
+ impeller::PlaygroundSwitches switches;
+ switches.enable_wide_gamut = false;
+ snapshotter_.reset(new MetalScreenshotter(switches));
auto typographer = impeller::TypographerContextSkia::Make();
aiks_context_.reset(
new impeller::AiksContext(snapshotter_->GetPlayground().GetContext(), typographer));
diff --git a/engine/src/flutter/impeller/base/flags.h b/engine/src/flutter/impeller/base/flags.h
index 258a833..cb581c3 100644
--- a/engine/src/flutter/impeller/base/flags.h
+++ b/engine/src/flutter/impeller/base/flags.h
@@ -10,6 +10,8 @@
/// 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 be8cb9a..0f4729c 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 @@
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 c18ebc8..9c3380d 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 @@
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<LineGeometry>(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 fe25c77..688cf7a 100644
--- a/engine/src/flutter/impeller/entity/BUILD.gn
+++ b/engine/src/flutter/impeller/entity/BUILD.gn
@@ -35,6 +35,8 @@
"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 @@
"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 @@
"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 96bfd62..6d58ab0 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 @@
///
class ColorSourceContents : public Contents {
public:
+ using BindFragmentCallback = std::function<bool(RenderPass& pass)>;
+ using PipelineBuilderCallback =
+ std::function<PipelineRef(ContentContextOptions)>;
+ using CreateGeometryCallback =
+ std::function<GeometryResult(const ContentContext& renderer,
+ const Entity& entity,
+ RenderPass& pass,
+ const Geometry* geom)>;
+
ColorSourceContents();
~ColorSourceContents() override;
@@ -99,41 +108,21 @@
// |Contents|
void SetInheritedOpacity(Scalar opacity) override;
- protected:
- using BindFragmentCallback = std::function<bool(RenderPass& pass)>;
- using PipelineBuilderCallback =
- std::function<PipelineRef(ContentContextOptions)>;
- using CreateGeometryCallback =
- std::function<GeometryResult(const ContentContext& renderer,
- const Entity& entity,
- RenderPass& pass,
- const Geometry* geom)>;
-
- 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 <typename VertexShaderT>
- 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 @@
/// 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 @@
options.blend_mode = entity.GetBlendMode();
options.stencil_mode = ContentContextOptions::StencilMode::kCoverCompare;
- std::optional<Rect> maybe_cover_area = GetGeometry()->GetCoverage({});
+ std::optional<Rect> maybe_cover_area = geometry->GetCoverage({});
if (!maybe_cover_area.has_value()) {
return true;
}
@@ -207,8 +196,7 @@
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 @@
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 <typename VertexShaderT>
+ 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<VertexShaderT>(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 376836f..41383a7 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 @@
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 @@
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 5daf9c1..f24ae29 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 FramebufferBlendSoftLightPipeline = FramebufferBlendPipelineHandle;
using GaussianBlurPipeline = RenderPipelineHandle<FilterPositionUvVertexShader, GaussianFragmentShader>;
using GlyphAtlasPipeline = RenderPipelineHandle<GlyphAtlasVertexShader, GlyphAtlasFragmentShader>;
+using LinePipeline = RenderPipelineHandle<LineVertexShader, LineFragmentShader>;
using LinearGradientFillPipeline = GradientPipelineHandle<LinearGradientFillFragmentShader>;
using LinearGradientSSBOFillPipeline = GradientPipelineHandle<LinearGradientSsboFillFragmentShader>;
using LinearGradientUniformFillPipeline = GradientPipelineHandle<LinearGradientUniformFillFragmentShader>;
@@ -352,6 +355,7 @@
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 @@
mutable Variants<FramebufferBlendSoftLightPipeline> framebuffer_blend_softlight_pipelines_;
mutable Variants<GaussianBlurPipeline> gaussian_blur_pipelines_;
mutable Variants<GlyphAtlasPipeline> glyph_atlas_pipelines_;
+ mutable Variants<LinePipeline> line_pipelines_;
mutable Variants<LinearGradientFillPipeline> linear_gradient_fill_pipelines_;
mutable Variants<LinearGradientSSBOFillPipeline> linear_gradient_ssbo_fill_pipelines_;
mutable Variants<LinearGradientUniformFillPipeline> 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 440599d..f83c142 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 @@
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<fml::DataMapping>(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<StopData> CreateGradientColors(const std::vector<Color>& 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 0000000..165645f
--- /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<bool(RenderPass& pass)>;
+using PipelineBuilderCallback =
+ std::function<PipelineRef(ContentContextOptions)>;
+using CreateGeometryCallback =
+ std::function<GeometryResult(const ContentContext& renderer,
+ const Entity& entity,
+ RenderPass& pass,
+ const Geometry* geometry)>;
+
+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<uint8_t>(std::clamp(std::round(x * 255.0), 0.0, 255.0));
+}
+
+/// See also: CreateGradientTexture
+std::shared_ptr<Texture> CreateCurveTexture(
+ Scalar width,
+ const std::shared_ptr<impeller::Context>& context) {
+ //
+ impeller::TextureDescriptor texture_descriptor;
+ texture_descriptor.storage_mode = impeller::StorageMode::kHostVisible;
+ texture_descriptor.format = PixelFormat::kR8UNormInt;
+ texture_descriptor.size = {kCurveResolution, 1};
+
+ std::vector<uint8_t> curve_data;
+ curve_data.reserve(kCurveResolution);
+ for (int i = 0; i < kCurveResolution; ++i) {
+ double norm = (static_cast<double>(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<const LineGeometry*>(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<PerVertexData*>(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<Texture> 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> LineContents::Make(
+ std::unique_ptr<LineGeometry> geometry,
+ Color color) {
+ return std::unique_ptr<LineContents>(
+ new LineContents(std::move(geometry), color));
+}
+
+LineContents::LineContents(std::unique_ptr<LineGeometry> 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<VS>(
+ 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<Rect> 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 0000000..a04ff0b
--- /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 <memory>
+
+#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<LineContents> Make(
+ std::unique_ptr<LineGeometry> geometry,
+ Color color);
+
+ bool Render(const ContentContext& renderer,
+ const Entity& entity,
+ RenderPass& pass) const override;
+
+ std::optional<Rect> GetCoverage(const Entity& entity) const override;
+
+ private:
+ explicit LineContents(std::unique_ptr<LineGeometry> geometry, Color color);
+
+ std::unique_ptr<LineGeometry> 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 0000000..d20b20f
--- /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<LineGeometry>(
+ /*p0=*/Point{0, 0}, //
+ /*p1=*/Point{100, 100}, //
+ /*width=*/width, //
+ /*cap=*/Cap::kSquare);
+ std::unique_ptr<LineContents> contents =
+ LineContents::Make(std::move(geometry), Color(1.f, 0.f, 0.f, 1.f));
+ EXPECT_TRUE(contents);
+ Entity entity;
+ std::optional<Rect> 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 257c44d..27a1445 100644
--- a/engine/src/flutter/impeller/entity/geometry/geometry.h
+++ b/engine/src/flutter/impeller/entity/geometry/geometry.h
@@ -119,7 +119,6 @@
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 56508b0..4a58578 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 @@
}
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 @@
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 @@
}
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 @@
std::optional<Rect> 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 340d324..3eac8e9 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 @@
Scalar ComputeAlphaCoverage(const Matrix& transform) const override;
- private:
+ // |Geometry|
+ std::optional<Rect> 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 @@
// 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;
+ static bool ComputeCorners(Point corners[4],
+ const Matrix& transform,
+ bool extend_endpoints,
+ Point p0,
+ Point p1,
+ Scalar width);
- Vector2 ComputeAlongVector(const Matrix& transform,
- bool allow_zero_length) const;
-
+ private:
// |Geometry|
GeometryResult GetPositionBuffer(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const override;
- // |Geometry|
- std::optional<Rect> 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 e8ea948..31bb6b3 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 0000000..32455a7
--- /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 <impeller/types.glsl>
+
+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 0000000..938bfdc
--- /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 <impeller/types.glsl>
+
+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 6acc5d7..12b5103 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 @@
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 @@
<< "This metal device doesn't support wide gamut golden tests.";
}
pimpl_->screenshotter =
- std::make_unique<testing::MetalScreenshotter>(enable_wide_gamut);
+ std::make_unique<testing::MetalScreenshotter>(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<PlaygroundImpl>& playground =
GetSharedVulkanPlayground(/*enable_validations=*/true);
pimpl_->screenshotter =
@@ -176,9 +184,13 @@
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 12b5250..d345611 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 @@
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 5b3f7f3..30cf3d4 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 @@
/// playground backend.
class MetalScreenshotter : public Screenshotter {
public:
- explicit MetalScreenshotter(bool enable_wide_gamut);
+ explicit MetalScreenshotter(const PlaygroundSwitches& switches);
std::unique_ptr<Screenshot> 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 5c2877f..051347d 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 e5d446b..092a07c 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 @@
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 7d00c4a..978f01a 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 @@
}
auto context = ContextMTL::Create(
- impeller::Flags{}, ShaderLibraryMappingsForPlayground(),
+ switches.flags, ShaderLibraryMappingsForPlayground(),
is_gpu_disabled_sync_switch_, "Playground Library",
switches.enable_wide_gamut
? std::optional<PixelFormat>(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 57ae5be..3ea803d 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 @@
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 2e51363..e6f9f49 100644
--- a/engine/src/flutter/impeller/playground/playground_test.cc
+++ b/engine/src/flutter/impeller/playground/playground_test.cc
@@ -65,6 +65,9 @@
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 460bd8b..29f2684 100644
--- a/engine/src/flutter/impeller/playground/switches.h
+++ b/engine/src/flutter/impeller/playground/switches.h
@@ -9,6 +9,7 @@
#include <optional>
#include "flutter/fml/command_line.h"
+#include "impeller/base/flags.h"
namespace impeller {
@@ -34,6 +35,8 @@
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 fdc2ae7..5fdc59a 100644
--- a/engine/src/flutter/impeller/renderer/BUILD.gn
+++ b/engine/src/flutter/impeller/renderer/BUILD.gn
@@ -73,8 +73,8 @@
"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 fe1ff03..4e40da0 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 @@
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 9956c84..5b8400d 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 @@
return std::nullopt;
}
-std::shared_ptr<ContextVK> ContextVK::Create(const Flags& flags,
- Settings settings) {
- auto context = std::shared_ptr<ContextVK>(new ContextVK(flags));
+std::shared_ptr<ContextVK> ContextVK::Create(Settings settings) {
+ auto context = std::shared_ptr<ContextVK>(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 8660763..39dae91 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 @@
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<EmbedderData> embedder_data;
@@ -98,8 +99,7 @@
/// Visible for testing.
static size_t ChooseThreadCountForWorkers(size_t hardware_concurrency);
- static std::shared_ptr<ContextVK> Create(const Flags& flags,
- Settings settings);
+ static std::shared_ptr<ContextVK> 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 c3a3540..0ea8bea 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 @@
g_format_properties_callback = format_properties_callback_;
g_physical_device_properties_callback = physical_properties_callback_;
settings.embedder_data = embedder_data_;
- std::shared_ptr<ContextVK> result =
- ContextVK::Create(Flags{}, std::move(settings));
+ std::shared_ptr<ContextVK> 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 0c332ed..0000000
--- 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<CommandBuffer>& command_buffer,
- const std::shared_ptr<Context>& context,
- const std::shared_ptr<Texture>& texture) {
- std::shared_ptr<BlitPass> 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 0000000..b50f6f0
--- /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<Texture> CreateTexture(
+ const TextureDescriptor& texture_descriptor,
+ const std::vector<uint8_t>& data,
+ const std::shared_ptr<impeller::Context>& context,
+ std::string_view debug_label) {
+ std::shared_ptr<Texture> texture =
+ context->GetResourceAllocator()->CreateTexture(texture_descriptor);
+
+ auto data_mapping =
+ std::make_shared<fml::NonOwnedMapping>(data.data(), data.size());
+ std::shared_ptr<DeviceBuffer> buffer =
+ context->GetResourceAllocator()->CreateBufferWithCopy(*data_mapping);
+
+ std::shared_ptr<CommandBuffer> cmd_buffer = context->CreateCommandBuffer();
+ std::shared_ptr<BlitPass> 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<CommandBuffer>& command_buffer,
+ const std::shared_ptr<Context>& context,
+ const std::shared_ptr<Texture>& texture) {
+ std::shared_ptr<BlitPass> 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 a968c23..df4fd16 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<Texture> CreateTexture(
+ const TextureDescriptor& texture_descriptor,
+ const std::vector<uint8_t>& data,
+ const std::shared_ptr<impeller::Context>& context,
+ std::string_view debug_label);
+
/// Adds a blit command to the render pass.
[[nodiscard]] fml::Status AddMipmapGeneration(
const std::shared_ptr<CommandBuffer>& command_buffer,
@@ -20,4 +26,4 @@
} // 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 92e1920..131d75e 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 @@
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 c58ca59..97ef7c7 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 4639b2a..8383cf0 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 @@
id<MTLDevice> 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 ff45ade..eeec695 100644
--- a/engine/src/flutter/shell/common/switches.cc
+++ b/engine/src/flutter/shell/common/switches.cc
@@ -534,6 +534,8 @@
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 02c7d1e..528d3e9 100644
--- a/engine/src/flutter/shell/common/switches.h
+++ b/engine/src/flutter/shell/common/switches.h
@@ -304,6 +304,9 @@
"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 e2b3ede..9815331 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 @@
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 0e4ea0c..e2ac4e9 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 @@
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 dedd4de..73b28ec 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 @@
"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 @@
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 eb51dbc..caeb2fe 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 @@
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 e4dba21..e004fc2 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 @@
/**
* Initializes a FlutterDarwinContextMetalImpeller.
*/
-- (instancetype)init:(const std::shared_ptr<const fml::SyncSwitch>&)is_gpu_disabled_sync_switch;
+- (instancetype)init:(const impeller::Flags&)flags
+ gpuDisabledSyncSwitch:
+ (const std::shared_ptr<const fml::SyncSwitch>&)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 36d95ac..b893da3 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<impeller::ContextMTL> CreateImpellerContext(
+ const impeller::Flags& flags,
const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch) {
std::vector<std::shared_ptr<fml::Mapping>> shader_mappings = {
std::make_shared<fml::NonOwnedMapping>(impeller_entity_shaders_data,
@@ -25,16 +26,18 @@
std::make_shared<fml::NonOwnedMapping>(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<const fml::SyncSwitch>&)is_gpu_disabled_sync_switch {
+- (instancetype)init:(const impeller::Flags&)flags
+ gpuDisabledSyncSwitch:
+ (const std::shared_ptr<const fml::SyncSwitch>&)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<MTLDevice> 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 44459eb..132c581 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 @@
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 24a887a..da8df41 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 @@
static std::unique_ptr<IOSContext> Create(
IOSRenderingAPI api,
IOSRenderingBackend backend,
- const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch);
+ const std::shared_ptr<const fml::SyncSwitch>& 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 9517ed8..7db29ea 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 @@
std::unique_ptr<IOSContext> IOSContext::Create(
IOSRenderingAPI api,
IOSRenderingBackend backend,
- const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch) {
+ const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch,
+ const Settings& settings) {
switch (api) {
case IOSRenderingAPI::kSoftware:
FML_LOG(IMPORTANT)
@@ -37,7 +38,7 @@
FML_LOG(FATAL) << "Impeller opt-out unavailable.";
return nullptr;
case IOSRenderingBackend::kImpeller:
- return std::make_unique<IOSContextMetalImpeller>(is_gpu_disabled_sync_switch);
+ return std::make_unique<IOSContextMetalImpeller>(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 debed10..00f4891 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 @@
class IOSContextMetalImpeller final : public IOSContext {
public:
explicit IOSContextMetalImpeller(
+ const Settings& settings,
const std::shared_ptr<const fml::SyncSwitch>& 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 3b9aa5c..83eda04 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<const fml::SyncSwitch>& 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<impeller::AiksContext>(
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 b9b33e8..15f6223 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 @@
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 6a55973..1017903 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 @@
}
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 9a740e3..95bdeb5 100644
--- a/engine/src/flutter/shell/testing/tester_main.cc
+++ b/engine/src/flutter/shell/testing/tester_main.cc
@@ -74,11 +74,11 @@
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 a4e7071..8d3f2b1 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_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