[Impeller] optimize drawImageRect with blend and matrix color filter. (#165998)

Fixes https://github.com/flutter/flutter/issues/165959

Flame uses a lot of individual calls to drawImageRect w/ blend color
filters to color the sprites. This is much slower with Impeller today,
because we convert this into:

```
saveLayer(color filter)
drawImageRect
restore
```

Instead, we can detect this case and convert to a call to drawAtlas.
drawAtlas will use the porter-duff shader which removes the saveLayer
entirely. We could do this optimization while recording, but for now its
fine to do in dispatch.

We do have a snapshot optimization that catches "normal" image usage,
but it doesn't apply in the flame case as the source rects are sampling
from a larger texture and we don't have a way to pass those through the
snapshot system.

## Before

<img
src="https://github.com/user-attachments/assets/faada042-843f-4f15-af20-9baf862f66fd"
width="200"/>


## After

<img
src="https://github.com/user-attachments/assets/c83e061c-190f-4613-a725-88e16b776c04"
width="200"/>

---------

Co-authored-by: gaaclarke <30870216+gaaclarke@users.noreply.github.com>
This commit is contained in:
Jonah Williams
2025-03-28 18:33:08 -07:00
committed by GitHub
parent 05de81ed7e
commit 6467d5c3c5
11 changed files with 494 additions and 76 deletions

View File

@@ -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<DlImageImpeller> 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<DlImageImpeller> 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

View File

@@ -9,13 +9,18 @@
#include <unordered_map>
#include <utility>
#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<VerticesGeometry>& vertices,
const Paint& paint) {
@@ -156,8 +168,10 @@ static std::unique_ptr<EntityPassTarget> CreateRenderTarget(
}
return std::make_unique<EntityPassTarget>(
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<Texture>& 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<AtlasContents>();
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<ColorFilterAtlasContents>();
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<Texture>& 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<Texture>& 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<Texture>& 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();

View File

@@ -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<Texture>& image,
Rect source,
Rect dest,
const Paint& paint,
const SamplerDescriptor& sampler,
SourceRectConstraint src_rect_constraint);
RenderPass& GetCurrentRenderPass() const;
Canvas(const Canvas&) = delete;

View File

@@ -54,7 +54,7 @@ Rect DlAtlasGeometry::ComputeBoundingBox() const {
return bounding_box;
}
std::shared_ptr<Texture> DlAtlasGeometry::GetAtlas() const {
const std::shared_ptr<Texture>& 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<VS::PerVertexData*>(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<VS::PerVertexData*>(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();

View File

@@ -38,7 +38,7 @@ class DlAtlasGeometry : public AtlasGeometry {
Rect ComputeBoundingBox() const override;
std::shared_ptr<Texture> GetAtlas() const override;
const std::shared_ptr<Texture>& GetAtlas() const override;
const SamplerDescriptor& GetSamplerDescriptor() const override;

View File

@@ -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> 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<VS::PerVertexData*>(raw_data);
int offset = 0;
std::array<TPoint<float>, 4> destination_points =
destination_.GetPoints();
std::array<TPoint<float>, 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<VS::PerVertexData*>(raw_data);
int offset = 0;
std::array<TPoint<float>, 4> texture_coords =
Rect::MakeSize(texture_->GetSize()).Project(source_).GetPoints();
std::array<TPoint<float>, 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<Texture>& 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<const Sampler> 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<const Sampler> 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<Rect> 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<const Sampler> 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

View File

@@ -30,11 +30,52 @@ class AtlasGeometry {
virtual Rect ComputeBoundingBox() const = 0;
virtual std::shared_ptr<Texture> GetAtlas() const = 0;
virtual const std::shared_ptr<Texture>& 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> 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<Texture>& GetAtlas() const override;
const SamplerDescriptor& GetSamplerDescriptor() const override;
BlendMode GetBlendMode() const override;
bool ShouldInvertBlendMode() const override;
private:
const std::shared_ptr<Texture> 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<Rect> 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_

View File

@@ -64,10 +64,10 @@ std::optional<Entity> ColorMatrixFilterContents::RenderFilter(
auto size = input_snapshot->texture->GetSize();
std::array<VS::PerVertexData, 4> 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<Entity> 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<const Sampler> 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();

View File

@@ -99,7 +99,7 @@ using BlendScreenPipeline = AdvancedBlendPipelineHandle;
using BlendSoftLightPipeline = AdvancedBlendPipelineHandle;
using BorderMaskBlurPipeline = RenderPipelineHandle<FilterPositionUvVertexShader, BorderMaskBlurFragmentShader>;
using ClipPipeline = RenderPipelineHandle<ClipVertexShader, ClipFragmentShader>;
using ColorMatrixColorFilterPipeline = RenderPipelineHandle<FilterPositionVertexShader, ColorMatrixColorFilterFragmentShader>;
using ColorMatrixColorFilterPipeline = RenderPipelineHandle<FilterPositionUvVertexShader, ColorMatrixColorFilterFragmentShader>;
using ConicalGradientFillConicalPipeline = GradientPipelineHandle<ConicalGradientFillConicalFragmentShader>;
using ConicalGradientFillRadialPipeline = GradientPipelineHandle<ConicalGradientFillRadialFragmentShader>;
using ConicalGradientFillStripPipeline = GradientPipelineHandle<ConicalGradientFillStripFragmentShader>;

View File

@@ -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;
}

View File

@@ -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
]