diff --git a/engine/src/flutter/impeller/display_list/aiks_dl_atlas_unittests.cc b/engine/src/flutter/impeller/display_list/aiks_dl_atlas_unittests.cc index 3206db5be6..6d4d4fe5f2 100644 --- a/engine/src/flutter/impeller/display_list/aiks_dl_atlas_unittests.cc +++ b/engine/src/flutter/impeller/display_list/aiks_dl_atlas_unittests.cc @@ -3,6 +3,10 @@ // found in the LICENSE file. #include "display_list/dl_sampling_options.h" +#include "display_list/dl_types.h" +#include "display_list/effects/dl_color_filter.h" +#include "display_list/effects/image_filters/dl_matrix_image_filter.h" +#include "display_list/geometry/dl_geometry_types.h" #include "flutter/impeller/display_list/aiks_unittests.h" #include "flutter/display_list/dl_blend_mode.h" @@ -248,5 +252,70 @@ TEST_P(AiksTest, DlAtlasGeometrySkip) { EXPECT_TRUE(geom.ShouldSkip()); } +TEST_P(AiksTest, DrawImageRectWithBlendColorFilter) { + sk_sp texture = + DlImageImpeller::Make(CreateTextureForFixture("bay_bridge.jpg")); + + DisplayListBuilder builder; + DlPaint paint = DlPaint().setColorFilter(DlColorFilter::MakeBlend( + DlColor::kRed().withAlphaF(0.4), DlBlendMode::kSrcOver)); + + DlMatrix filter_matrix = DlMatrix(); + auto filter = flutter::DlMatrixImageFilter(filter_matrix, + flutter::DlImageSampling::kLinear); + DlPaint paint_with_filter = paint; + paint_with_filter.setImageFilter(&filter); + + // Compare porter-duff blend modes. + builder.DrawPaint(DlPaint().setColor(DlColor::kWhite())); + // Uses image filter to disable atlas conversion. + builder.DrawImageRect(texture, DlRect::MakeSize(texture->GetSize()), + DlRect::MakeLTRB(0, 0, 500, 500), {}, + &paint_with_filter); + + // Uses atlas conversion. + builder.Translate(600, 0); + builder.DrawImageRect(texture, DlRect::MakeSize(texture->GetSize()), + DlRect::MakeLTRB(0, 0, 500, 500), {}, &paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + +TEST_P(AiksTest, DrawImageRectWithMatrixColorFilter) { + sk_sp texture = + DlImageImpeller::Make(CreateTextureForFixture("bay_bridge.jpg")); + + DisplayListBuilder builder; + static const constexpr ColorMatrix kColorInversion = { + .array = { + -1.0, 0, 0, 1.0, 0, // + 0, -1.0, 0, 1.0, 0, // + 0, 0, -1.0, 1.0, 0, // + 1.0, 1.0, 1.0, 1.0, 0 // + }}; + DlPaint paint = DlPaint().setColorFilter( + DlColorFilter::MakeMatrix(kColorInversion.array)); + + DlMatrix filter_matrix = DlMatrix(); + auto filter = flutter::DlMatrixImageFilter(filter_matrix, + flutter::DlImageSampling::kLinear); + DlPaint paint_with_filter = paint; + paint_with_filter.setImageFilter(&filter); + + // Compare inverting color matrix filter. + builder.DrawPaint(DlPaint().setColor(DlColor::kWhite())); + // Uses image filter to disable atlas conversion. + builder.DrawImageRect(texture, DlRect::MakeSize(texture->GetSize()), + DlRect::MakeLTRB(0, 0, 500, 500), {}, + &paint_with_filter); + + // Uses atlas conversion. + builder.Translate(600, 0); + builder.DrawImageRect(texture, DlRect::MakeSize(texture->GetSize()), + DlRect::MakeLTRB(0, 0, 500, 500), {}, &paint); + + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + } // namespace testing } // namespace impeller diff --git a/engine/src/flutter/impeller/display_list/canvas.cc b/engine/src/flutter/impeller/display_list/canvas.cc index 17436c1fdc..442c54d635 100644 --- a/engine/src/flutter/impeller/display_list/canvas.cc +++ b/engine/src/flutter/impeller/display_list/canvas.cc @@ -9,13 +9,18 @@ #include #include +#include "display_list/effects/color_filters/dl_blend_color_filter.h" +#include "display_list/effects/color_filters/dl_matrix_color_filter.h" +#include "display_list/effects/dl_color_filter.h" #include "display_list/effects/dl_color_source.h" #include "display_list/effects/dl_image_filter.h" +#include "display_list/image/dl_image.h" #include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" #include "impeller/base/validation.h" #include "impeller/core/formats.h" #include "impeller/display_list/color_filter.h" +#include "impeller/display_list/dl_atlas_geometry.h" #include "impeller/display_list/image_filter.h" #include "impeller/display_list/skia_conversions.h" #include "impeller/entity/contents/atlas_contents.h" @@ -44,12 +49,19 @@ #include "impeller/geometry/color.h" #include "impeller/geometry/constants.h" #include "impeller/geometry/path_builder.h" +#include "impeller/geometry/rstransform.h" #include "impeller/renderer/command_buffer.h" namespace impeller { namespace { +bool IsPipelineBlendOrMatrixFilter(const flutter::DlColorFilter* filter) { + return filter->type() == flutter::DlColorFilterType::kMatrix || + (filter->type() == flutter::DlColorFilterType::kBlend && + filter->asBlend()->mode() <= Entity::kLastPipelineBlendMode); +} + static bool UseColorSourceContents( const std::shared_ptr& vertices, const Paint& paint) { @@ -156,8 +168,10 @@ static std::unique_ptr CreateRenderTarget( } return std::make_unique( - target, renderer.GetDeviceCapabilities().SupportsReadFromResolve(), - renderer.GetDeviceCapabilities().SupportsImplicitResolvingMSAA()); + target, // + renderer.GetDeviceCapabilities().SupportsReadFromResolve(), // + renderer.GetDeviceCapabilities().SupportsImplicitResolvingMSAA() // + ); } } // namespace @@ -315,6 +329,78 @@ void Canvas::DrawPaint(const Paint& paint) { AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint); } +// Optimization: if the texture has a color filter that is a simple +// porter-duff blend or matrix filter, then instead of performing a save layer +// we should swap out the shader for the porter duff blend shader and avoid a +// saveLayer. This can only be done for imageRects without a strict source +// rect, as the porter duff shader does not support this feature. This +// optimization is important for Flame. +bool Canvas::AttemptColorFilterOptimization( + const std::shared_ptr& image, + Rect source, + Rect dest, + const Paint& paint, + const SamplerDescriptor& sampler, + SourceRectConstraint src_rect_constraint) { + if (src_rect_constraint == SourceRectConstraint::kStrict || // + !paint.color_filter || // + paint.image_filter != nullptr || // + paint.invert_colors || // + paint.mask_blur_descriptor.has_value() || // + !IsPipelineBlendOrMatrixFilter(paint.color_filter)) { + return false; + } + + if (paint.color_filter->type() == flutter::DlColorFilterType::kBlend) { + const flutter::DlBlendColorFilter* blend_filter = + paint.color_filter->asBlend(); + DrawImageRectAtlasGeometry geometry = DrawImageRectAtlasGeometry( + /*texture=*/image, + /*source=*/source, + /*destination=*/dest, + /*color=*/skia_conversions::ToColor(blend_filter->color()), + /*blend_mode=*/blend_filter->mode(), + /*desc=*/sampler); + + auto atlas_contents = std::make_shared(); + atlas_contents->SetGeometry(&geometry); + atlas_contents->SetAlpha(paint.color.alpha); + + Entity entity; + entity.SetTransform(GetCurrentTransform()); + entity.SetBlendMode(paint.blend_mode); + entity.SetContents(atlas_contents); + + AddRenderEntityToCurrentPass(entity); + } else { + const flutter::DlMatrixColorFilter* matrix_filter = + paint.color_filter->asMatrix(); + + DrawImageRectAtlasGeometry geometry = DrawImageRectAtlasGeometry( + /*texture=*/image, + /*source=*/source, + /*destination=*/dest, + /*color=*/Color::Khaki(), // ignored + /*blend_mode=*/BlendMode::kSrcOver, // ignored + /*desc=*/sampler); + + auto atlas_contents = std::make_shared(); + atlas_contents->SetGeometry(&geometry); + atlas_contents->SetAlpha(paint.color.alpha); + impeller::ColorMatrix color_matrix; + matrix_filter->get_matrix(color_matrix.array); + atlas_contents->SetMatrix(color_matrix); + + Entity entity; + entity.SetTransform(GetCurrentTransform()); + entity.SetBlendMode(paint.blend_mode); + entity.SetContents(atlas_contents); + + AddRenderEntityToCurrentPass(entity); + } + return true; +} + bool Canvas::AttemptDrawBlurredRRect(const Rect& rect, Size corner_radii, const Paint& paint) { @@ -703,8 +789,8 @@ void Canvas::DrawImage(const std::shared_ptr& image, return; } - const auto source = Rect::MakeSize(image->GetSize()); - const auto dest = source.Shift(offset); + const Rect source = Rect::MakeSize(image->GetSize()); + const Rect dest = source.Shift(offset); DrawImageRect(image, source, dest, paint, sampler); } @@ -719,8 +805,7 @@ void Canvas::DrawImageRect(const std::shared_ptr& image, return; } - auto size = image->GetSize(); - + ISize size = image->GetSize(); if (size.IsEmpty()) { return; } @@ -730,6 +815,12 @@ void Canvas::DrawImageRect(const std::shared_ptr& image, if (!clipped_source) { return; } + + if (AttemptColorFilterOptimization(image, source, dest, paint, sampler, + src_rect_constraint)) { + return; + } + if (*clipped_source != source) { Scalar sx = dest.GetWidth() / source.GetWidth(); Scalar sy = dest.GetHeight() / source.GetHeight(); diff --git a/engine/src/flutter/impeller/display_list/canvas.h b/engine/src/flutter/impeller/display_list/canvas.h index 1962d48d5f..746167bd98 100644 --- a/engine/src/flutter/impeller/display_list/canvas.h +++ b/engine/src/flutter/impeller/display_list/canvas.h @@ -357,6 +357,17 @@ class Canvas { Size corner_radii, const Paint& paint); + /// For simple DrawImageRect calls, optimize any draws with a color filter + /// into the corresponding atlas draw. + /// + /// Returns whether not the optimization was applied. + bool AttemptColorFilterOptimization(const std::shared_ptr& image, + Rect source, + Rect dest, + const Paint& paint, + const SamplerDescriptor& sampler, + SourceRectConstraint src_rect_constraint); + RenderPass& GetCurrentRenderPass() const; Canvas(const Canvas&) = delete; diff --git a/engine/src/flutter/impeller/display_list/dl_atlas_geometry.cc b/engine/src/flutter/impeller/display_list/dl_atlas_geometry.cc index 218877021d..bcd473ab6f 100644 --- a/engine/src/flutter/impeller/display_list/dl_atlas_geometry.cc +++ b/engine/src/flutter/impeller/display_list/dl_atlas_geometry.cc @@ -54,7 +54,7 @@ Rect DlAtlasGeometry::ComputeBoundingBox() const { return bounding_box; } -std::shared_ptr DlAtlasGeometry::GetAtlas() const { +const std::shared_ptr& DlAtlasGeometry::GetAtlas() const { return atlas_; } @@ -69,16 +69,15 @@ BlendMode DlAtlasGeometry::GetBlendMode() const { VertexBuffer DlAtlasGeometry::CreateSimpleVertexBuffer( HostBuffer& host_buffer) const { using VS = TextureFillVertexShader; - constexpr size_t indices[6] = {0, 1, 2, 1, 2, 3}; - auto buffer_view = host_buffer.Emplace( + BufferView buffer_view = host_buffer.Emplace( sizeof(VS::PerVertexData) * count_ * 6, alignof(VS::PerVertexData), [&](uint8_t* raw_data) { VS::PerVertexData* data = reinterpret_cast(raw_data); int offset = 0; - auto texture_size = atlas_->GetSize(); + ISize texture_size = atlas_->GetSize(); for (auto i = 0u; i < count_; i++) { flutter::DlRect sample_rect = tex_[i]; auto points = sample_rect.GetPoints(); @@ -102,16 +101,15 @@ VertexBuffer DlAtlasGeometry::CreateSimpleVertexBuffer( VertexBuffer DlAtlasGeometry::CreateBlendVertexBuffer( HostBuffer& host_buffer) const { using VS = PorterDuffBlendVertexShader; - constexpr size_t indices[6] = {0, 1, 2, 1, 2, 3}; - auto buffer_view = host_buffer.Emplace( + BufferView buffer_view = host_buffer.Emplace( sizeof(VS::PerVertexData) * count_ * 6, alignof(VS::PerVertexData), [&](uint8_t* raw_data) { VS::PerVertexData* data = reinterpret_cast(raw_data); int offset = 0; - auto texture_size = atlas_->GetSize(); + ISize texture_size = atlas_->GetSize(); for (auto i = 0u; i < count_; i++) { flutter::DlRect sample_rect = tex_[i]; auto points = sample_rect.GetPoints(); diff --git a/engine/src/flutter/impeller/display_list/dl_atlas_geometry.h b/engine/src/flutter/impeller/display_list/dl_atlas_geometry.h index 3c29451908..ed20379b59 100644 --- a/engine/src/flutter/impeller/display_list/dl_atlas_geometry.h +++ b/engine/src/flutter/impeller/display_list/dl_atlas_geometry.h @@ -38,7 +38,7 @@ class DlAtlasGeometry : public AtlasGeometry { Rect ComputeBoundingBox() const override; - std::shared_ptr GetAtlas() const override; + const std::shared_ptr& GetAtlas() const override; const SamplerDescriptor& GetSamplerDescriptor() const override; diff --git a/engine/src/flutter/impeller/entity/contents/atlas_contents.cc b/engine/src/flutter/impeller/entity/contents/atlas_contents.cc index 4f1d1d4871..6bd795a07a 100644 --- a/engine/src/flutter/impeller/entity/contents/atlas_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/atlas_contents.cc @@ -8,6 +8,7 @@ #include "impeller/entity/contents/atlas_contents.h" #include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/filters/blend_filter_contents.h" +#include "impeller/entity/contents/pipelines.h" #include "impeller/entity/entity.h" #include "impeller/entity/texture_fill.frag.h" #include "impeller/entity/texture_fill.vert.h" @@ -16,6 +17,114 @@ namespace impeller { +DrawImageRectAtlasGeometry::DrawImageRectAtlasGeometry( + std::shared_ptr texture, + const Rect& source, + const Rect& destination, + const Color& color, + BlendMode blend_mode, + const SamplerDescriptor& desc) + : texture_(std::move(texture)), + source_(source), + destination_(destination), + color_(color), + blend_mode_(blend_mode), + desc_(desc) {} + +DrawImageRectAtlasGeometry::~DrawImageRectAtlasGeometry() = default; + +bool DrawImageRectAtlasGeometry::ShouldUseBlend() const { + return true; +} + +bool DrawImageRectAtlasGeometry::ShouldSkip() const { + return false; +} + +VertexBuffer DrawImageRectAtlasGeometry::CreateSimpleVertexBuffer( + HostBuffer& host_buffer) const { + using VS = TextureFillVertexShader; + constexpr size_t indices[6] = {0, 1, 2, 1, 2, 3}; + + BufferView buffer_view = host_buffer.Emplace( + sizeof(VS::PerVertexData) * 6, alignof(VS::PerVertexData), + [&](uint8_t* raw_data) { + VS::PerVertexData* data = + reinterpret_cast(raw_data); + int offset = 0; + std::array, 4> destination_points = + destination_.GetPoints(); + std::array, 4> texture_coords = + Rect::MakeSize(texture_->GetSize()).Project(source_).GetPoints(); + for (size_t j = 0; j < 6; j++) { + data[offset].position = destination_points[indices[j]]; + data[offset].texture_coords = texture_coords[indices[j]]; + offset++; + } + }); + + return VertexBuffer{ + .vertex_buffer = buffer_view, + .index_buffer = {}, + .vertex_count = 6, + .index_type = IndexType::kNone, + }; +} + +VertexBuffer DrawImageRectAtlasGeometry::CreateBlendVertexBuffer( + HostBuffer& host_buffer) const { + using VS = PorterDuffBlendVertexShader; + constexpr size_t indices[6] = {0, 1, 2, 1, 2, 3}; + + BufferView buffer_view = host_buffer.Emplace( + sizeof(VS::PerVertexData) * 6, alignof(VS::PerVertexData), + [&](uint8_t* raw_data) { + VS::PerVertexData* data = + reinterpret_cast(raw_data); + int offset = 0; + std::array, 4> texture_coords = + Rect::MakeSize(texture_->GetSize()).Project(source_).GetPoints(); + std::array, 4> destination_points = + destination_.GetPoints(); + for (size_t j = 0; j < 6; j++) { + data[offset].vertices = destination_points[indices[j]]; + data[offset].texture_coords = texture_coords[indices[j]]; + data[offset].color = color_.Premultiply(); + offset++; + } + }); + + return VertexBuffer{ + .vertex_buffer = buffer_view, + .index_buffer = {}, + .vertex_count = 6, + .index_type = IndexType::kNone, + }; +} + +Rect DrawImageRectAtlasGeometry::ComputeBoundingBox() const { + return destination_; +} + +const std::shared_ptr& DrawImageRectAtlasGeometry::GetAtlas() const { + return texture_; +} + +const SamplerDescriptor& DrawImageRectAtlasGeometry::GetSamplerDescriptor() + const { + return desc_; +} + +BlendMode DrawImageRectAtlasGeometry::GetBlendMode() const { + return blend_mode_; +} + +bool DrawImageRectAtlasGeometry::ShouldInvertBlendMode() const { + return false; +} + +//// + AtlasContents::AtlasContents() = default; AtlasContents::~AtlasContents() = default; @@ -42,11 +151,8 @@ bool AtlasContents::Render(const ContentContext& renderer, return true; } - auto dst_sampler_descriptor = geometry_->GetSamplerDescriptor(); - if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) { - dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal; - dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal; - } + const SamplerDescriptor& dst_sampler_descriptor = + geometry_->GetSamplerDescriptor(); raw_ptr dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler( dst_sampler_descriptor); @@ -56,8 +162,6 @@ bool AtlasContents::Render(const ContentContext& renderer, using VS = TextureFillVertexShader; using FS = TextureFillFragmentShader; - auto dst_sampler_descriptor = geometry_->GetSamplerDescriptor(); - raw_ptr dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler( dst_sampler_descriptor); @@ -97,10 +201,12 @@ bool AtlasContents::Render(const ContentContext& renderer, pass.SetCommandLabel("DrawAtlas Blend"); #endif // IMPELLER_DEBUG pass.SetVertexBuffer(geometry_->CreateBlendVertexBuffer(host_buffer)); - auto inverted_blend_mode = - InvertPorterDuffBlend(blend_mode).value_or(BlendMode::kSrc); - pass.SetPipeline(renderer.GetPorterDuffPipeline(inverted_blend_mode, - OptionsFromPass(pass))); + BlendMode inverted_blend_mode = + geometry_->ShouldInvertBlendMode() + ? (InvertPorterDuffBlend(blend_mode).value_or(BlendMode::kSrc)) + : blend_mode; + pass.SetPipeline(renderer.GetPorterDuffPipeline( + inverted_blend_mode, OptionsFromPassAndEntity(pass, entity))); FS::FragInfo frag_info; VS::FrameInfo frame_info; @@ -134,7 +240,8 @@ bool AtlasContents::Render(const ContentContext& renderer, #endif // IMPELLER_DEBUG pass.SetVertexBuffer(geometry_->CreateBlendVertexBuffer(host_buffer)); - renderer.GetDrawVerticesUberPipeline(blend_mode, OptionsFromPass(pass)); + renderer.GetDrawVerticesUberPipeline(blend_mode, + OptionsFromPassAndEntity(pass, entity)); FS::BindTextureSampler(pass, geometry_->GetAtlas(), dst_sampler); VUS::FrameInfo frame_info; @@ -157,4 +264,83 @@ bool AtlasContents::Render(const ContentContext& renderer, return pass.Draw().ok(); } +/////////////// + +ColorFilterAtlasContents::ColorFilterAtlasContents() = default; + +ColorFilterAtlasContents::~ColorFilterAtlasContents() = default; + +std::optional ColorFilterAtlasContents::GetCoverage( + const Entity& entity) const { + if (!geometry_) { + return std::nullopt; + } + return geometry_->ComputeBoundingBox().TransformBounds(entity.GetTransform()); +} + +void ColorFilterAtlasContents::SetGeometry(AtlasGeometry* geometry) { + geometry_ = geometry; +} + +void ColorFilterAtlasContents::SetAlpha(Scalar alpha) { + alpha_ = alpha; +} + +void ColorFilterAtlasContents::SetMatrix(ColorMatrix matrix) { + matrix_ = matrix; +} + +bool ColorFilterAtlasContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + if (geometry_->ShouldSkip() || alpha_ <= 0.0) { + return true; + } + + const SamplerDescriptor& dst_sampler_descriptor = + geometry_->GetSamplerDescriptor(); + + raw_ptr dst_sampler = + renderer.GetContext()->GetSamplerLibrary()->GetSampler( + dst_sampler_descriptor); + + auto& host_buffer = renderer.GetTransientsBuffer(); + + using VS = ColorMatrixColorFilterPipeline::VertexShader; + using FS = ColorMatrixColorFilterPipeline::FragmentShader; + +#ifdef IMPELLER_DEBUG + pass.SetCommandLabel("Atlas ColorFilter"); +#endif // IMPELLER_DEBUG + pass.SetVertexBuffer(geometry_->CreateSimpleVertexBuffer(host_buffer)); + pass.SetPipeline( + renderer.GetColorMatrixColorFilterPipeline(OptionsFromPass(pass))); + + FS::FragInfo frag_info; + VS::FrameInfo frame_info; + + FS::BindInputTexture(pass, geometry_->GetAtlas(), dst_sampler); + frame_info.texture_sampler_y_coord_scale = + geometry_->GetAtlas()->GetYCoordScale(); + + frag_info.input_alpha = 1; + frag_info.output_alpha = alpha_; + const float* matrix = matrix_.array; + frag_info.color_v = Vector4(matrix[4], matrix[9], matrix[14], matrix[19]); + frag_info.color_m = Matrix(matrix[0], matrix[5], matrix[10], matrix[15], // + matrix[1], matrix[6], matrix[11], matrix[16], // + matrix[2], matrix[7], matrix[12], matrix[17], // + matrix[3], matrix[8], matrix[13], matrix[18] // + ); + + FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info)); + + frame_info.mvp = entity.GetShaderTransform(pass); + + auto uniform_view = host_buffer.EmplaceUniform(frame_info); + VS::BindFrameInfo(pass, uniform_view); + + return pass.Draw().ok(); +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/entity/contents/atlas_contents.h b/engine/src/flutter/impeller/entity/contents/atlas_contents.h index a0c0046784..f34c85f407 100644 --- a/engine/src/flutter/impeller/entity/contents/atlas_contents.h +++ b/engine/src/flutter/impeller/entity/contents/atlas_contents.h @@ -30,11 +30,52 @@ class AtlasGeometry { virtual Rect ComputeBoundingBox() const = 0; - virtual std::shared_ptr GetAtlas() const = 0; + virtual const std::shared_ptr& GetAtlas() const = 0; virtual const SamplerDescriptor& GetSamplerDescriptor() const = 0; virtual BlendMode GetBlendMode() const = 0; + + virtual bool ShouldInvertBlendMode() const { return true; } +}; + +/// @brief An atlas geometry that adapts for drawImageRect. +class DrawImageRectAtlasGeometry : public AtlasGeometry { + public: + DrawImageRectAtlasGeometry(std::shared_ptr texture, + const Rect& source, + const Rect& destination, + const Color& color, + BlendMode blend_mode, + const SamplerDescriptor& desc); + + ~DrawImageRectAtlasGeometry(); + + bool ShouldUseBlend() const override; + + bool ShouldSkip() const override; + + VertexBuffer CreateSimpleVertexBuffer(HostBuffer& host_buffer) const override; + + VertexBuffer CreateBlendVertexBuffer(HostBuffer& host_buffer) const override; + + Rect ComputeBoundingBox() const override; + + const std::shared_ptr& GetAtlas() const override; + + const SamplerDescriptor& GetSamplerDescriptor() const override; + + BlendMode GetBlendMode() const override; + + bool ShouldInvertBlendMode() const override; + + private: + const std::shared_ptr texture_; + const Rect source_; + const Rect destination_; + const Color color_; + const BlendMode blend_mode_; + const SamplerDescriptor desc_; }; class AtlasContents final : public Contents { @@ -64,6 +105,41 @@ class AtlasContents final : public Contents { AtlasContents& operator=(const AtlasContents&) = delete; }; +/// A specialized atlas class for applying a color matrix filter to a +/// drawImageRect call. +class ColorFilterAtlasContents final : public Contents { + public: + explicit ColorFilterAtlasContents(); + + ~ColorFilterAtlasContents() override; + + void SetGeometry(AtlasGeometry* geometry); + + void SetAlpha(Scalar alpha); + + void SetMatrix(ColorMatrix matrix); + + // |Contents| + std::optional GetCoverage(const Entity& entity) const override; + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + private: + // These contents are created temporarily on the stack and never stored. + // The referenced geometry is also stack allocated and will be de-allocated + // after the contents are. + AtlasGeometry* geometry_ = nullptr; + ColorMatrix matrix_; + Scalar alpha_ = 1.0; + + ColorFilterAtlasContents(const ColorFilterAtlasContents&) = delete; + + ColorFilterAtlasContents& operator=(const ColorFilterAtlasContents&) = delete; +}; + } // namespace impeller #endif // FLUTTER_IMPELLER_ENTITY_CONTENTS_ATLAS_CONTENTS_H_ diff --git a/engine/src/flutter/impeller/entity/contents/filters/color_matrix_filter_contents.cc b/engine/src/flutter/impeller/entity/contents/filters/color_matrix_filter_contents.cc index 0bcf9d8109..337512f9a4 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/color_matrix_filter_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/filters/color_matrix_filter_contents.cc @@ -64,10 +64,10 @@ std::optional ColorMatrixFilterContents::RenderFilter( auto size = input_snapshot->texture->GetSize(); std::array vertices = { - VS::PerVertexData{Point(0, 0)}, - VS::PerVertexData{Point(1, 0)}, - VS::PerVertexData{Point(0, 1)}, - VS::PerVertexData{Point(1, 1)}, + VS::PerVertexData{Point(0, 0), Point(0, 0)}, + VS::PerVertexData{Point(1, 0), Point(1, 0)}, + VS::PerVertexData{Point(0, 1), Point(0, 1)}, + VS::PerVertexData{Point(1, 1), Point(1, 1)}, }; auto& host_buffer = renderer.GetTransientsBuffer(); pass.SetVertexBuffer( @@ -84,23 +84,21 @@ std::optional ColorMatrixFilterContents::RenderFilter( FS::FragInfo frag_info; const float* matrix = color_matrix.array; frag_info.color_v = Vector4(matrix[4], matrix[9], matrix[14], matrix[19]); - // clang-format off - frag_info.color_m = Matrix( - matrix[0], matrix[5], matrix[10], matrix[15], - matrix[1], matrix[6], matrix[11], matrix[16], - matrix[2], matrix[7], matrix[12], matrix[17], - matrix[3], matrix[8], matrix[13], matrix[18] + frag_info.color_m = Matrix(matrix[0], matrix[5], matrix[10], matrix[15], // + matrix[1], matrix[6], matrix[11], matrix[16], // + matrix[2], matrix[7], matrix[12], matrix[17], // + matrix[3], matrix[8], matrix[13], matrix[18] // ); - // clang-format on frag_info.input_alpha = absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes ? input_snapshot->opacity : 1.0f; + frag_info.output_alpha = 1; + raw_ptr sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler({}); FS::BindInputTexture(pass, input_snapshot->texture, sampler); FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info)); - VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info)); return pass.Draw().ok(); diff --git a/engine/src/flutter/impeller/entity/contents/pipelines.h b/engine/src/flutter/impeller/entity/contents/pipelines.h index 290e90c00f..f3998a779e 100644 --- a/engine/src/flutter/impeller/entity/contents/pipelines.h +++ b/engine/src/flutter/impeller/entity/contents/pipelines.h @@ -99,7 +99,7 @@ using BlendScreenPipeline = AdvancedBlendPipelineHandle; using BlendSoftLightPipeline = AdvancedBlendPipelineHandle; using BorderMaskBlurPipeline = RenderPipelineHandle; using ClipPipeline = RenderPipelineHandle; -using ColorMatrixColorFilterPipeline = RenderPipelineHandle; +using ColorMatrixColorFilterPipeline = RenderPipelineHandle; using ConicalGradientFillConicalPipeline = GradientPipelineHandle; using ConicalGradientFillRadialPipeline = GradientPipelineHandle; using ConicalGradientFillStripPipeline = GradientPipelineHandle; diff --git a/engine/src/flutter/impeller/entity/shaders/filters/color_matrix_color_filter.frag b/engine/src/flutter/impeller/entity/shaders/filters/color_matrix_color_filter.frag index b8f2da6b19..c9e74b1eda 100644 --- a/engine/src/flutter/impeller/entity/shaders/filters/color_matrix_color_filter.frag +++ b/engine/src/flutter/impeller/entity/shaders/filters/color_matrix_color_filter.frag @@ -35,6 +35,7 @@ uniform FragInfo { mat4 color_m; f16vec4 color_v; float16_t input_alpha; + float16_t output_alpha; } frag_info; @@ -54,5 +55,5 @@ void main() { float16_t(0), float16_t(1.0)); // premultiply the outputs - frag_color = f16vec4(color.rgb * color.a, color.a); + frag_color = IPHalfPremultiply(color) * frag_info.output_alpha; } diff --git a/engine/src/flutter/impeller/tools/malioc.json b/engine/src/flutter/impeller/tools/malioc.json index ed4cb61cbe..83636d98f7 100644 --- a/engine/src/flutter/impeller/tools/malioc.json +++ b/engine/src/flutter/impeller/tools/malioc.json @@ -409,13 +409,11 @@ "performance": { "longest_path_bound_pipelines": [ "arith_total", - "arith_fma", - "varying", - "texture" + "arith_fma" ], "longest_path_cycles": [ - 0.25, - 0.25, + 0.28125, + 0.28125, 0.0625, 0.0625, 0.0, @@ -433,13 +431,11 @@ ], "shortest_path_bound_pipelines": [ "arith_total", - "arith_fma", - "varying", - "texture" + "arith_fma" ], "shortest_path_cycles": [ - 0.25, - 0.25, + 0.28125, + 0.28125, 0.0625, 0.0625, 0.0, @@ -448,13 +444,11 @@ ], "total_bound_pipelines": [ "arith_total", - "arith_fma", - "varying", - "texture" + "arith_fma" ], "total_cycles": [ - 0.25, - 0.25, + 0.28125, + 0.28125, 0.0625, 0.0625, 0.0, @@ -464,8 +458,8 @@ }, "stack_spill_bytes": 0, "thread_occupancy": 100, - "uniform_registers_used": 14, - "work_registers_used": 10 + "uniform_registers_used": 16, + "work_registers_used": 9 } } } @@ -2232,13 +2226,11 @@ "performance": { "longest_path_bound_pipelines": [ "arith_total", - "arith_fma", - "varying", - "texture" + "arith_fma" ], "longest_path_cycles": [ - 0.25, - 0.25, + 0.28125, + 0.28125, 0.09375, 0.0625, 0.0, @@ -2256,13 +2248,11 @@ ], "shortest_path_bound_pipelines": [ "arith_total", - "arith_fma", - "varying", - "texture" + "arith_fma" ], "shortest_path_cycles": [ - 0.25, - 0.25, + 0.28125, + 0.28125, 0.0625, 0.0625, 0.0, @@ -2271,13 +2261,11 @@ ], "total_bound_pipelines": [ "arith_total", - "arith_fma", - "varying", - "texture" + "arith_fma" ], "total_cycles": [ - 0.25, - 0.25, + 0.28125, + 0.28125, 0.09375, 0.0625, 0.0, @@ -2305,7 +2293,7 @@ "arithmetic" ], "longest_path_cycles": [ - 2.640000104904175, + 2.9700000286102295, 1.0, 1.0 ], @@ -2318,7 +2306,7 @@ "arithmetic" ], "shortest_path_cycles": [ - 2.640000104904175, + 2.9700000286102295, 1.0, 1.0 ], @@ -2326,7 +2314,7 @@ "arithmetic" ], "total_cycles": [ - 3.0, + 3.3333332538604736, 1.0, 1.0 ]