mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Reland Fixes MatrixFilterContents rendering/coverage #52880 (flutter/engine#52939)
fixes https://github.com/flutter/flutter/issues/147807 relands https://github.com/flutter/engine/pull/52880 relands https://github.com/flutter/engine/pull/43943 This was previously reverted because of the golden test failure `widgets.widgets.magnifier.styled`. This fixes that problem by instead of focusing on ImageFilter and BackdropFilter subpasses, instead focuses on wether a subpass is clipped or not and by extension how the math should be handled. `widgets.widgets.magnifier.styled` after diff:  [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This commit is contained in:
parent
7ae310f931
commit
6b11db0352
@ -147,6 +147,7 @@
|
||||
../../../flutter/impeller/entity/contents/clip_contents_unittests.cc
|
||||
../../../flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc
|
||||
../../../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/test
|
||||
../../../flutter/impeller/entity/contents/tiled_texture_contents_unittests.cc
|
||||
|
||||
@ -2739,19 +2739,29 @@ TEST_P(AiksTest, VerticesGeometryColorUVPositionDataAdvancedBlend) {
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, MatrixImageFilterMagnify) {
|
||||
Canvas canvas;
|
||||
canvas.Scale(GetContentScale());
|
||||
auto image = std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
|
||||
canvas.Translate({600, -200});
|
||||
canvas.SaveLayer({
|
||||
.image_filter = std::make_shared<MatrixImageFilter>(
|
||||
Matrix::MakeScale({2, 2, 2}), SamplerDescriptor{}),
|
||||
});
|
||||
canvas.DrawImage(image, {0, 0},
|
||||
Paint{.color = Color::White().WithAlpha(0.5)});
|
||||
canvas.Restore();
|
||||
Scalar scale = 2.0;
|
||||
auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
|
||||
if (AiksTest::ImGuiBegin("Controls", nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::SliderFloat("Scale", &scale, 1, 2);
|
||||
ImGui::End();
|
||||
}
|
||||
Canvas canvas;
|
||||
canvas.Scale(GetContentScale());
|
||||
auto image =
|
||||
std::make_shared<Image>(CreateTextureForFixture("airplane.jpg"));
|
||||
canvas.Translate({600, -200});
|
||||
canvas.SaveLayer({
|
||||
.image_filter = std::make_shared<MatrixImageFilter>(
|
||||
Matrix::MakeScale({scale, scale, 1}), SamplerDescriptor{}),
|
||||
});
|
||||
canvas.DrawImage(image, {0, 0},
|
||||
Paint{.color = Color::White().WithAlpha(0.5)});
|
||||
canvas.Restore();
|
||||
return canvas.EndRecordingAsPicture();
|
||||
};
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
ASSERT_TRUE(OpenPlaygroundHere(callback));
|
||||
}
|
||||
|
||||
// Render a white circle at the top left corner of the screen.
|
||||
|
||||
@ -225,7 +225,8 @@ void Canvas::Save(bool create_subpass,
|
||||
entry.cull_rect = transform_stack_.back().cull_rect;
|
||||
entry.clip_height = transform_stack_.back().clip_height;
|
||||
if (create_subpass) {
|
||||
entry.rendering_mode = Entity::RenderingMode::kSubpass;
|
||||
entry.rendering_mode =
|
||||
Entity::RenderingMode::kSubpassAppendSnapshotTransform;
|
||||
auto subpass = std::make_unique<EntityPass>();
|
||||
if (backdrop_filter) {
|
||||
EntityPass::BackdropFilterProc backdrop_filter_proc =
|
||||
@ -261,7 +262,9 @@ bool Canvas::Restore() {
|
||||
current_pass_->PopClips(num_clips, current_depth_);
|
||||
|
||||
if (transform_stack_.back().rendering_mode ==
|
||||
Entity::RenderingMode::kSubpass) {
|
||||
Entity::RenderingMode::kSubpassAppendSnapshotTransform ||
|
||||
transform_stack_.back().rendering_mode ==
|
||||
Entity::RenderingMode::kSubpassPrependSnapshotTransform) {
|
||||
current_pass_->SetClipDepth(++current_depth_);
|
||||
current_pass_ = GetCurrentPass().GetSuperpass();
|
||||
FML_DCHECK(current_pass_);
|
||||
|
||||
@ -230,7 +230,7 @@ void ExperimentalCanvas::SaveLayer(
|
||||
<< entry.clip_depth << " <=? " << transform_stack_.back().clip_depth
|
||||
<< " after allocating " << total_content_depth;
|
||||
entry.clip_height = transform_stack_.back().clip_height;
|
||||
entry.rendering_mode = Entity::RenderingMode::kSubpass;
|
||||
entry.rendering_mode = Entity::RenderingMode::kSubpassAppendSnapshotTransform;
|
||||
transform_stack_.emplace_back(entry);
|
||||
|
||||
auto inline_pass = std::make_unique<InlinePassContext>(
|
||||
@ -272,7 +272,9 @@ bool ExperimentalCanvas::Restore() {
|
||||
current_depth_ = transform_stack_.back().clip_depth;
|
||||
|
||||
if (transform_stack_.back().rendering_mode ==
|
||||
Entity::RenderingMode::kSubpass) {
|
||||
Entity::RenderingMode::kSubpassAppendSnapshotTransform ||
|
||||
transform_stack_.back().rendering_mode ==
|
||||
Entity::RenderingMode::kSubpassPrependSnapshotTransform) {
|
||||
auto inline_pass = std::move(inline_pass_contexts_.back());
|
||||
|
||||
SaveLayerState save_layer_state = save_layer_state_.back();
|
||||
|
||||
@ -68,7 +68,8 @@ std::shared_ptr<Contents> Paint::WithFiltersForSubpassTarget(
|
||||
std::shared_ptr<Contents> input,
|
||||
const Matrix& effect_transform) const {
|
||||
auto image_filter =
|
||||
WithImageFilter(input, effect_transform, Entity::RenderingMode::kSubpass);
|
||||
WithImageFilter(input, effect_transform,
|
||||
Entity::RenderingMode::kSubpassPrependSnapshotTransform);
|
||||
if (image_filter) {
|
||||
input = image_filter;
|
||||
}
|
||||
|
||||
@ -50,8 +50,9 @@ std::shared_ptr<Contents> PaintPassDelegate::CreateContentsForSubpassTarget(
|
||||
std::shared_ptr<FilterContents> PaintPassDelegate::WithImageFilter(
|
||||
const FilterInput::Variant& input,
|
||||
const Matrix& effect_transform) const {
|
||||
return paint_.WithImageFilter(input, effect_transform,
|
||||
Entity::RenderingMode::kSubpass);
|
||||
return paint_.WithImageFilter(
|
||||
input, effect_transform,
|
||||
Entity::RenderingMode::kSubpassPrependSnapshotTransform);
|
||||
}
|
||||
|
||||
/// OpacityPeepholePassDelegate
|
||||
@ -152,8 +153,9 @@ OpacityPeepholePassDelegate::CreateContentsForSubpassTarget(
|
||||
std::shared_ptr<FilterContents> OpacityPeepholePassDelegate::WithImageFilter(
|
||||
const FilterInput::Variant& input,
|
||||
const Matrix& effect_transform) const {
|
||||
return paint_.WithImageFilter(input, effect_transform,
|
||||
Entity::RenderingMode::kSubpass);
|
||||
return paint_.WithImageFilter(
|
||||
input, effect_transform,
|
||||
Entity::RenderingMode::kSubpassPrependSnapshotTransform);
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -13,6 +13,7 @@ namespace testing {
|
||||
|
||||
using impeller::PlaygroundBackend;
|
||||
using impeller::PlaygroundTest;
|
||||
using impeller::Point;
|
||||
|
||||
INSTANTIATE_PLAYGROUND_SUITE(DlGoldenTest);
|
||||
|
||||
@ -48,5 +49,56 @@ TEST_P(DlGoldenTest, CanRenderImage) {
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
// Asserts that subpass rendering of MatrixImageFilters works.
|
||||
// https://github.com/flutter/flutter/issues/147807
|
||||
TEST_P(DlGoldenTest, Bug147807) {
|
||||
Point content_scale = GetContentScale();
|
||||
auto draw = [content_scale](DlCanvas* canvas,
|
||||
const std::vector<sk_sp<DlImage>>& images) {
|
||||
canvas->Transform2DAffine(content_scale.x, 0, 0, 0, content_scale.y, 0);
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor(0xfffef7ff));
|
||||
canvas->DrawRect(SkRect::MakeLTRB(0, 0, 375, 667), paint);
|
||||
paint.setColor(DlColor(0xffff9800));
|
||||
canvas->DrawRect(SkRect::MakeLTRB(0, 0, 187.5, 333.5), paint);
|
||||
paint.setColor(DlColor(0xff9c27b0));
|
||||
canvas->DrawRect(SkRect::MakeLTRB(187.5, 0, 375, 333.5), paint);
|
||||
paint.setColor(DlColor(0xff4caf50));
|
||||
canvas->DrawRect(SkRect::MakeLTRB(0, 333.5, 187.5, 667), paint);
|
||||
paint.setColor(DlColor(0xfff44336));
|
||||
canvas->DrawRect(SkRect::MakeLTRB(187.5, 333.5, 375, 667), paint);
|
||||
|
||||
canvas->Save();
|
||||
{
|
||||
canvas->ClipRRect(
|
||||
SkRRect::MakeOval(SkRect::MakeLTRB(201.25, 10, 361.25, 170)),
|
||||
DlCanvas::ClipOp::kIntersect, true);
|
||||
SkRect save_layer_bounds = SkRect::MakeLTRB(201.25, 10, 361.25, 170);
|
||||
DlMatrixImageFilter backdrop(SkMatrix::MakeAll(3, 0, -280, //
|
||||
0, 3, -920, //
|
||||
0, 0, 1),
|
||||
DlImageSampling::kLinear);
|
||||
canvas->SaveLayer(&save_layer_bounds, /*paint=*/nullptr, &backdrop);
|
||||
{
|
||||
canvas->Translate(201.25, 10);
|
||||
auto paint = DlPaint()
|
||||
.setAntiAlias(true)
|
||||
.setColor(DlColor(0xff2196f3))
|
||||
.setStrokeWidth(5)
|
||||
.setDrawStyle(DlDrawStyle::kStroke);
|
||||
canvas->DrawCircle(SkPoint::Make(80, 80), 80, paint);
|
||||
}
|
||||
canvas->Restore();
|
||||
}
|
||||
canvas->Restore();
|
||||
};
|
||||
|
||||
DisplayListBuilder builder;
|
||||
std::vector<sk_sp<DlImage>> images;
|
||||
draw(&builder, images);
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@ -241,6 +241,7 @@ impeller_component("entity_unittests") {
|
||||
"contents/clip_contents_unittests.cc",
|
||||
"contents/filters/gaussian_blur_filter_contents_unittests.cc",
|
||||
"contents/filters/inputs/filter_input_unittests.cc",
|
||||
"contents/filters/matrix_filter_contents_unittests.cc",
|
||||
"contents/host_buffer_unittests.cc",
|
||||
"contents/tiled_texture_contents_unittests.cc",
|
||||
"entity_pass_target_unittests.cc",
|
||||
|
||||
@ -28,6 +28,28 @@ void MatrixFilterContents::SetSamplerDescriptor(SamplerDescriptor desc) {
|
||||
sampler_descriptor_ = std::move(desc);
|
||||
}
|
||||
|
||||
namespace {
|
||||
Matrix CalculateSubpassTransform(const Matrix& snapshot_transform,
|
||||
const Matrix& effect_transform,
|
||||
const Matrix& matrix,
|
||||
Entity::RenderingMode rendering_mode) {
|
||||
if (rendering_mode ==
|
||||
Entity::RenderingMode::kSubpassAppendSnapshotTransform) {
|
||||
return snapshot_transform * //
|
||||
effect_transform * //
|
||||
matrix * //
|
||||
effect_transform.Invert();
|
||||
} else {
|
||||
FML_DCHECK(rendering_mode ==
|
||||
Entity::RenderingMode::kSubpassPrependSnapshotTransform);
|
||||
return effect_transform * //
|
||||
matrix * //
|
||||
effect_transform.Invert() * //
|
||||
snapshot_transform;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::optional<Entity> MatrixFilterContents::RenderFilter(
|
||||
const FilterInput::Vector& inputs,
|
||||
const ContentContext& renderer,
|
||||
@ -40,29 +62,45 @@ std::optional<Entity> MatrixFilterContents::RenderFilter(
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// The filter's matrix needs to be applied within the space defined by the
|
||||
// scene's current transform matrix (CTM). For example: If the CTM is
|
||||
// scaled up, then translations applied by the matrix should be magnified
|
||||
// accordingly.
|
||||
//
|
||||
// To accomplish this, we sandwich the filter's matrix within the CTM in both
|
||||
// cases. But notice that for the subpass backdrop filter case, we use the
|
||||
// "effect transform" instead of the Entity's transform!
|
||||
//
|
||||
// That's because in the subpass backdrop filter case, the Entity's transform
|
||||
// isn't actually the captured CTM of the scene like it usually is; instead,
|
||||
// it's just a screen space translation that offsets the backdrop texture (as
|
||||
// mentioned above). And so we sneak the subpass's captured CTM in through the
|
||||
// effect transform.
|
||||
|
||||
auto transform = rendering_mode_ == Entity::RenderingMode::kSubpass
|
||||
? effect_transform
|
||||
: entity.GetTransform();
|
||||
snapshot->transform = transform * //
|
||||
matrix_ * //
|
||||
transform.Invert() * //
|
||||
snapshot->transform;
|
||||
|
||||
if (rendering_mode_ ==
|
||||
Entity::RenderingMode::kSubpassPrependSnapshotTransform ||
|
||||
rendering_mode_ ==
|
||||
Entity::RenderingMode::kSubpassAppendSnapshotTransform) {
|
||||
// There are two special quirks with how Matrix filters behave when used as
|
||||
// subpass backdrop filters:
|
||||
//
|
||||
// 1. For subpass backdrop filters, the snapshot transform is always just a
|
||||
// translation that positions the parent pass texture correctly relative
|
||||
// to the subpass texture. However, this translation always needs to be
|
||||
// applied in screen space.
|
||||
//
|
||||
// Since we know the snapshot transform will always have an identity
|
||||
// basis in this case, we safely reverse the order and apply the filter's
|
||||
// matrix within the snapshot transform space.
|
||||
//
|
||||
// 2. The filter's matrix needs to be applied within the space defined by
|
||||
// the scene's current transformation matrix (CTM). For example: If the
|
||||
// CTM is scaled up, then translations applied by the matrix should be
|
||||
// magnified accordingly.
|
||||
//
|
||||
// To accomplish this, we sandwitch the filter's matrix within the CTM in
|
||||
// both cases. But notice that for the subpass backdrop filter case, we
|
||||
// use the "effect transform" instead of the Entity's transform!
|
||||
//
|
||||
// That's because in the subpass backdrop filter case, the Entity's
|
||||
// transform isn't actually the captured CTM of the scene like it usually
|
||||
// is; instead, it's just a screen space translation that offsets the
|
||||
// backdrop texture (as mentioned above). And so we sneak the subpass's
|
||||
// captured CTM in through the effect transform.
|
||||
//
|
||||
snapshot->transform = CalculateSubpassTransform(
|
||||
snapshot->transform, effect_transform, matrix_, rendering_mode_);
|
||||
} else {
|
||||
snapshot->transform = entity.GetTransform() * //
|
||||
matrix_ * //
|
||||
entity.GetTransform().Invert() * //
|
||||
snapshot->transform;
|
||||
}
|
||||
snapshot->sampler_descriptor = sampler_descriptor_;
|
||||
if (!snapshot.has_value()) {
|
||||
return std::nullopt;
|
||||
@ -91,17 +129,26 @@ std::optional<Rect> MatrixFilterContents::GetFilterCoverage(
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto coverage = inputs[0]->GetCoverage(entity);
|
||||
std::optional<Rect> coverage = inputs[0]->GetCoverage(entity);
|
||||
if (!coverage.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto& m = rendering_mode_ == Entity::RenderingMode::kSubpass
|
||||
? effect_transform
|
||||
: inputs[0]->GetTransform(entity);
|
||||
auto transform = m * //
|
||||
matrix_ * //
|
||||
m.Invert(); //
|
||||
return coverage->TransformBounds(transform);
|
||||
|
||||
Matrix input_transform = inputs[0]->GetTransform(entity);
|
||||
if (rendering_mode_ ==
|
||||
Entity::RenderingMode::kSubpassPrependSnapshotTransform ||
|
||||
rendering_mode_ ==
|
||||
Entity::RenderingMode::kSubpassAppendSnapshotTransform) {
|
||||
Rect coverage_bounds = coverage->TransformBounds(input_transform.Invert());
|
||||
Matrix transform = CalculateSubpassTransform(
|
||||
input_transform, effect_transform, matrix_, rendering_mode_);
|
||||
return coverage_bounds.TransformBounds(transform);
|
||||
} else {
|
||||
Matrix transform = input_transform * //
|
||||
matrix_ * //
|
||||
input_transform.Invert(); //
|
||||
return coverage->TransformBounds(transform);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -0,0 +1,220 @@
|
||||
// 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 "flutter/testing/testing.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "impeller/entity/contents/filters/matrix_filter_contents.h"
|
||||
#include "impeller/entity/entity_playground.h"
|
||||
#include "impeller/geometry/geometry_asserts.h"
|
||||
|
||||
namespace impeller {
|
||||
namespace testing {
|
||||
|
||||
class MatrixFilterContentsTest : public EntityPlayground {
|
||||
public:
|
||||
/// Create a texture that has been cleared to transparent black.
|
||||
std::shared_ptr<Texture> MakeTexture(ISize size) {
|
||||
std::shared_ptr<CommandBuffer> command_buffer =
|
||||
GetContentContext()->GetContext()->CreateCommandBuffer();
|
||||
if (!command_buffer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto render_target = GetContentContext()->MakeSubpass(
|
||||
"Clear Subpass", size, command_buffer,
|
||||
[](const ContentContext&, RenderPass&) { return true; });
|
||||
|
||||
if (!GetContentContext()
|
||||
->GetContext()
|
||||
->GetCommandQueue()
|
||||
->Submit(/*buffers=*/{command_buffer})
|
||||
.ok()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (render_target.ok()) {
|
||||
return render_target.value().GetRenderTargetTexture();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
INSTANTIATE_PLAYGROUND_SUITE(MatrixFilterContentsTest);
|
||||
|
||||
TEST(MatrixFilterContentsTest, Create) {
|
||||
MatrixFilterContents contents;
|
||||
EXPECT_TRUE(contents.IsTranslationOnly());
|
||||
}
|
||||
|
||||
TEST(MatrixFilterContentsTest, CoverageEmpty) {
|
||||
MatrixFilterContents contents;
|
||||
FilterInput::Vector inputs = {};
|
||||
Entity entity;
|
||||
std::optional<Rect> coverage =
|
||||
contents.GetFilterCoverage(inputs, entity, /*effect_transform=*/Matrix());
|
||||
ASSERT_FALSE(coverage.has_value());
|
||||
}
|
||||
|
||||
TEST(MatrixFilterContentsTest, CoverageSimple) {
|
||||
MatrixFilterContents contents;
|
||||
FilterInput::Vector inputs = {
|
||||
FilterInput::Make(Rect::MakeLTRB(10, 10, 110, 110))};
|
||||
Entity entity;
|
||||
std::optional<Rect> coverage =
|
||||
contents.GetFilterCoverage(inputs, entity, /*effect_transform=*/Matrix());
|
||||
|
||||
ASSERT_EQ(coverage, Rect::MakeLTRB(10, 10, 110, 110));
|
||||
}
|
||||
|
||||
TEST(MatrixFilterContentsTest, Coverage2x) {
|
||||
MatrixFilterContents contents;
|
||||
contents.SetMatrix(Matrix::MakeScale({2.0, 2.0, 1.0}));
|
||||
FilterInput::Vector inputs = {
|
||||
FilterInput::Make(Rect::MakeXYWH(10, 10, 100, 100))};
|
||||
Entity entity;
|
||||
std::optional<Rect> coverage =
|
||||
contents.GetFilterCoverage(inputs, entity, /*effect_transform=*/Matrix());
|
||||
|
||||
ASSERT_EQ(coverage, Rect::MakeXYWH(20, 20, 200, 200));
|
||||
}
|
||||
|
||||
TEST(MatrixFilterContentsTest, Coverage2xEffect) {
|
||||
MatrixFilterContents contents;
|
||||
FilterInput::Vector inputs = {
|
||||
FilterInput::Make(Rect::MakeXYWH(10, 10, 100, 100))};
|
||||
Entity entity;
|
||||
std::optional<Rect> coverage = contents.GetFilterCoverage(
|
||||
inputs, entity, /*effect_transform=*/Matrix::MakeScale({2.0, 2.0, 1.0}));
|
||||
|
||||
ASSERT_EQ(coverage, Rect::MakeXYWH(10, 10, 100, 100));
|
||||
}
|
||||
|
||||
namespace {
|
||||
void expectRenderCoverageEqual(const std::optional<Entity>& result,
|
||||
const std::optional<Rect> contents_coverage,
|
||||
const Rect& expected) {
|
||||
EXPECT_TRUE(result.has_value());
|
||||
if (result.has_value()) {
|
||||
EXPECT_EQ(result.value().GetBlendMode(), BlendMode::kSourceOver);
|
||||
std::optional<Rect> result_coverage = result.value().GetCoverage();
|
||||
EXPECT_TRUE(result_coverage.has_value());
|
||||
EXPECT_TRUE(contents_coverage.has_value());
|
||||
if (result_coverage.has_value() && contents_coverage.has_value()) {
|
||||
EXPECT_TRUE(RectNear(contents_coverage.value(), expected));
|
||||
EXPECT_TRUE(RectNear(result_coverage.value(), expected));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_P(MatrixFilterContentsTest, RenderCoverageMatchesGetCoverageIdentity) {
|
||||
std::shared_ptr<Texture> texture = MakeTexture(ISize(100, 100));
|
||||
MatrixFilterContents contents;
|
||||
contents.SetInputs({FilterInput::Make(texture)});
|
||||
|
||||
Entity entity;
|
||||
entity.SetTransform(Matrix::MakeTranslation({100, 200, 0}));
|
||||
|
||||
std::shared_ptr<ContentContext> renderer = GetContentContext();
|
||||
std::optional<Entity> result =
|
||||
contents.GetEntity(*renderer, entity, /*coverage_hint=*/{});
|
||||
expectRenderCoverageEqual(result, contents.GetCoverage(entity),
|
||||
Rect::MakeXYWH(100, 200, 100, 100));
|
||||
}
|
||||
|
||||
TEST_P(MatrixFilterContentsTest, RenderCoverageMatchesGetCoverageTranslate) {
|
||||
std::shared_ptr<Texture> texture = MakeTexture(ISize(100, 100));
|
||||
MatrixFilterContents contents;
|
||||
contents.SetInputs({FilterInput::Make(texture)});
|
||||
contents.SetMatrix(Matrix::MakeTranslation({50, 100, 0}));
|
||||
contents.SetEffectTransform(Matrix::MakeScale({2, 2, 1}));
|
||||
|
||||
Entity entity;
|
||||
entity.SetTransform(Matrix::MakeTranslation({100, 200, 0}));
|
||||
|
||||
std::shared_ptr<ContentContext> renderer = GetContentContext();
|
||||
std::optional<Entity> result =
|
||||
contents.GetEntity(*renderer, entity, /*coverage_hint=*/{});
|
||||
expectRenderCoverageEqual(result, contents.GetCoverage(entity),
|
||||
Rect::MakeXYWH(150, 300, 100, 100));
|
||||
}
|
||||
|
||||
TEST_P(MatrixFilterContentsTest,
|
||||
RenderCoverageMatchesGetCoverageClippedSubpassTranslate) {
|
||||
std::shared_ptr<Texture> texture = MakeTexture(ISize(100, 100));
|
||||
MatrixFilterContents contents;
|
||||
contents.SetInputs({FilterInput::Make(texture)});
|
||||
contents.SetMatrix(Matrix::MakeTranslation({50, 100, 0}));
|
||||
contents.SetEffectTransform(Matrix::MakeScale({2, 2, 1}));
|
||||
contents.SetRenderingMode(
|
||||
Entity::RenderingMode::kSubpassAppendSnapshotTransform);
|
||||
|
||||
Entity entity;
|
||||
entity.SetTransform(Matrix::MakeTranslation({100, 200, 0}));
|
||||
|
||||
std::shared_ptr<ContentContext> renderer = GetContentContext();
|
||||
std::optional<Entity> result =
|
||||
contents.GetEntity(*renderer, entity, /*coverage_hint=*/{});
|
||||
expectRenderCoverageEqual(result, contents.GetCoverage(entity),
|
||||
Rect::MakeXYWH(200, 400, 100, 100));
|
||||
}
|
||||
|
||||
TEST_P(MatrixFilterContentsTest, RenderCoverageMatchesGetCoverageScale) {
|
||||
std::shared_ptr<Texture> texture = MakeTexture(ISize(100, 100));
|
||||
MatrixFilterContents contents;
|
||||
contents.SetInputs({FilterInput::Make(texture)});
|
||||
contents.SetMatrix(Matrix::MakeScale({3, 3, 1}));
|
||||
contents.SetEffectTransform(Matrix::MakeScale({2, 2, 1}));
|
||||
|
||||
Entity entity;
|
||||
entity.SetTransform(Matrix::MakeTranslation({100, 200, 0}));
|
||||
|
||||
std::shared_ptr<ContentContext> renderer = GetContentContext();
|
||||
std::optional<Entity> result =
|
||||
contents.GetEntity(*renderer, entity, /*coverage_hint=*/{});
|
||||
expectRenderCoverageEqual(result, contents.GetCoverage(entity),
|
||||
Rect::MakeXYWH(100, 200, 300, 300));
|
||||
}
|
||||
|
||||
TEST_P(MatrixFilterContentsTest,
|
||||
RenderCoverageMatchesGetCoverageClippedSubpassScale) {
|
||||
std::shared_ptr<Texture> texture = MakeTexture(ISize(100, 100));
|
||||
MatrixFilterContents contents;
|
||||
contents.SetInputs({FilterInput::Make(texture)});
|
||||
contents.SetMatrix(Matrix::MakeScale({3, 3, 1}));
|
||||
contents.SetEffectTransform(Matrix::MakeScale({2, 2, 1}));
|
||||
contents.SetRenderingMode(
|
||||
Entity::RenderingMode::kSubpassAppendSnapshotTransform);
|
||||
|
||||
Entity entity;
|
||||
entity.SetTransform(Matrix::MakeTranslation({100, 200, 0}));
|
||||
|
||||
std::shared_ptr<ContentContext> renderer = GetContentContext();
|
||||
std::optional<Entity> result =
|
||||
contents.GetEntity(*renderer, entity, /*coverage_hint=*/{});
|
||||
expectRenderCoverageEqual(result, contents.GetCoverage(entity),
|
||||
Rect::MakeXYWH(100, 200, 300, 300));
|
||||
}
|
||||
|
||||
TEST_P(MatrixFilterContentsTest, RenderCoverageMatchesGetCoverageSubpassScale) {
|
||||
std::shared_ptr<Texture> texture = MakeTexture(ISize(100, 100));
|
||||
MatrixFilterContents contents;
|
||||
contents.SetInputs({FilterInput::Make(texture)});
|
||||
contents.SetMatrix(Matrix::MakeScale({3, 3, 1}));
|
||||
contents.SetEffectTransform(Matrix::MakeScale({2, 2, 1}));
|
||||
contents.SetRenderingMode(
|
||||
Entity::RenderingMode::kSubpassPrependSnapshotTransform);
|
||||
|
||||
Entity entity;
|
||||
entity.SetTransform(Matrix::MakeTranslation({100, 200, 0}));
|
||||
|
||||
std::shared_ptr<ContentContext> renderer = GetContentContext();
|
||||
std::optional<Entity> result =
|
||||
contents.GetEntity(*renderer, entity, /*coverage_hint=*/{});
|
||||
expectRenderCoverageEqual(result, contents.GetCoverage(entity),
|
||||
Rect::MakeXYWH(300, 600, 300, 300));
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace impeller
|
||||
@ -32,7 +32,8 @@ class Entity {
|
||||
/// rather than local space, and so some filters (namely,
|
||||
/// MatrixFilterContents) need to interpret the given EffectTransform as the
|
||||
/// current transform matrix.
|
||||
kSubpass,
|
||||
kSubpassAppendSnapshotTransform,
|
||||
kSubpassPrependSnapshotTransform,
|
||||
};
|
||||
|
||||
/// An enum to define how to repeat, fold, or omit colors outside of the
|
||||
|
||||
@ -183,7 +183,8 @@ std::optional<Rect> EntityPass::GetElementsCoverage(
|
||||
std::shared_ptr<FilterContents> backdrop_filter =
|
||||
subpass.backdrop_filter_proc_(
|
||||
FilterInput::Make(accumulated_coverage.value()),
|
||||
subpass.transform_, Entity::RenderingMode::kSubpass);
|
||||
subpass.transform_,
|
||||
Entity::RenderingMode::kSubpassAppendSnapshotTransform);
|
||||
if (backdrop_filter) {
|
||||
auto backdrop_coverage = backdrop_filter->GetCoverage({});
|
||||
unfiltered_coverage =
|
||||
@ -570,9 +571,14 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
|
||||
auto texture = pass_context.GetTexture();
|
||||
// Render the backdrop texture before any of the pass elements.
|
||||
const auto& proc = subpass->backdrop_filter_proc_;
|
||||
subpass_backdrop_filter_contents =
|
||||
proc(FilterInput::Make(std::move(texture)),
|
||||
subpass->transform_.Basis(), Entity::RenderingMode::kSubpass);
|
||||
|
||||
subpass_backdrop_filter_contents = proc(
|
||||
FilterInput::Make(std::move(texture)), subpass->transform_.Basis(),
|
||||
// When the subpass has a translation that means the math with
|
||||
// the snapshot has to be different.
|
||||
subpass->transform_.HasTranslation()
|
||||
? Entity::RenderingMode::kSubpassPrependSnapshotTransform
|
||||
: Entity::RenderingMode::kSubpassAppendSnapshotTransform);
|
||||
|
||||
// If the very first thing we render in this EntityPass is a subpass that
|
||||
// happens to have a backdrop filter, than that backdrop filter will end
|
||||
|
||||
@ -331,6 +331,8 @@ struct Matrix {
|
||||
return m[3] != 0 || m[7] != 0 || m[11] != 0 || m[15] != 1;
|
||||
}
|
||||
|
||||
constexpr bool HasTranslation() const { return m[12] != 0 || m[13] != 0; }
|
||||
|
||||
constexpr bool IsAligned2D(Scalar tolerance = 0) const {
|
||||
if (HasPerspective2D()) {
|
||||
return false;
|
||||
|
||||
@ -60,6 +60,13 @@ TEST(MatrixTest, HasPerspective) {
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
TEST(MatrixTest, HasTranslation) {
|
||||
EXPECT_TRUE(Matrix::MakeTranslation({100, 100, 0}).HasTranslation());
|
||||
EXPECT_TRUE(Matrix::MakeTranslation({0, 100, 0}).HasTranslation());
|
||||
EXPECT_TRUE(Matrix::MakeTranslation({100, 0, 0}).HasTranslation());
|
||||
EXPECT_FALSE(Matrix().HasTranslation());
|
||||
}
|
||||
|
||||
TEST(MatrixTest, IsAligned2D) {
|
||||
EXPECT_TRUE(Matrix().IsAligned2D());
|
||||
EXPECT_TRUE(Matrix::MakeScale({1.0f, 1.0f, 2.0f}).IsAligned2D());
|
||||
|
||||
@ -774,6 +774,9 @@ impeller_Play_AiksTest_VerticesGeometryUVPositionDataWithTranslate_Vulkan.png
|
||||
impeller_Play_AiksTest_VerticesGeometryUVPositionData_Metal.png
|
||||
impeller_Play_AiksTest_VerticesGeometryUVPositionData_OpenGLES.png
|
||||
impeller_Play_AiksTest_VerticesGeometryUVPositionData_Vulkan.png
|
||||
impeller_Play_DlGoldenTest_Bug147807_Metal.png
|
||||
impeller_Play_DlGoldenTest_Bug147807_OpenGLES.png
|
||||
impeller_Play_DlGoldenTest_Bug147807_Vulkan.png
|
||||
impeller_Play_DlGoldenTest_CanDrawPaint_Metal.png
|
||||
impeller_Play_DlGoldenTest_CanDrawPaint_OpenGLES.png
|
||||
impeller_Play_DlGoldenTest_CanDrawPaint_Vulkan.png
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user