[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:
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user