Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Impeller] implements new blur tile mode #48805

Merged
merged 6 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 36 additions & 0 deletions impeller/aiks/aiks_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4257,7 +4257,43 @@ TEST_P(AiksTest, AdvancedBlendWithClearColorOptimization) {
canvas.DrawRect(
Rect::MakeXYWH(0, 0, 200, 300),
{.color = {1.0, 0.0, 1.0, 1.0}, .blend_mode = BlendMode::kMultiply});
}

TEST_P(AiksTest, GaussianBlurAtPeripheryVertical) {
Canvas canvas;

canvas.Scale(GetContentScale());
canvas.DrawRRect(Rect::MakeLTRB(0, 0, GetWindowSize().width, 100),
Point(10, 10), Paint{.color = Color::LimeGreen()});
canvas.DrawRRect(Rect::MakeLTRB(0, 110, GetWindowSize().width, 210),
Point(10, 10), Paint{.color = Color::Magenta()});
canvas.ClipRect(Rect::MakeLTRB(100, 0, 200, GetWindowSize().height));
canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp));
canvas.Restore();

ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

TEST_P(AiksTest, GaussianBlurAtPeripheryHorizontal) {
Canvas canvas;

canvas.Scale(GetContentScale());
std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
canvas.DrawImageRect(
std::make_shared<Image>(boston),
Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height),
Rect::MakeLTRB(0, 0, GetWindowSize().width, 100), Paint{});
canvas.DrawRRect(Rect::MakeLTRB(0, 110, GetWindowSize().width, 210),
Point(10, 10), Paint{.color = Color::Magenta()});
canvas.ClipRect(Rect::MakeLTRB(0, 50, GetWindowSize().width, 150));
canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp));
canvas.Restore();
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}

Expand Down
3 changes: 2 additions & 1 deletion impeller/entity/contents/filters/filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ std::shared_ptr<FilterContents> FilterContents::MakeGaussianBlur(
// TODO(https://github.com/flutter/flutter/issues/131580): Remove once the new
// blur handles all cases.
if (use_new_filter) {
auto blur = std::make_shared<GaussianBlurFilterContents>(sigma_x.sigma);
auto blur =
std::make_shared<GaussianBlurFilterContents>(sigma_x.sigma, tile_mode);
blur->SetInputs({input});
return blur;
}
Expand Down
61 changes: 45 additions & 16 deletions impeller/entity/contents/filters/gaussian_blur_filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,31 @@ Matrix MakeAnchorScale(const Point& anchor, Vector2 scale) {
Matrix::MakeTranslation({-anchor.x, -anchor.y, 0});
}

void SetTileMode(SamplerDescriptor* descriptor,
const ContentContext& renderer,
Entity::TileMode tile_mode) {
switch (tile_mode) {
case Entity::TileMode::kDecal:
if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a todo/issue for emulating this when it's not supported? The halo won't render correctly on some devices w/ the current technique without this.

We already have a specialized pipeline that the old blur uses for this w/ the same vertex/uniform layouts (GaussianBlurDecalPipeline).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done: flutter/flutter#139773

How do we look up availability on gpuinfo.org? I had a hard time finding it. It's just available to everyone on Vulkan right? The check is just for OpenGLES?

Copy link
Member

@bdero bdero Dec 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah decal is supported for Metal (via MTLSamplerAddressModeClampToZero) and our min Vulkan profile without any extensions. For OpenGLES we check for GL_EXT_texture_border_clamp or GL_NV_texture_border_clamp.

image

This looks worse than the situation actually is, though. I'm pretty sure "coverage" on gpuinfo.org is just "percentage of devices in the database" and doesn't weight for an estimation of actual usage. And of course, there are ancient devices on the list that Flutter doesn't support.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yea, good point. Over time the usefulness of gpuinfo.org will wane if they don't have some sort of filter for old devices. Something like filtering based on operating system would probably work since the OS developers will start dropping support.

descriptor->width_address_mode = SamplerAddressMode::kDecal;
descriptor->height_address_mode = SamplerAddressMode::kDecal;
}
break;
case Entity::TileMode::kClamp:
descriptor->width_address_mode = SamplerAddressMode::kClampToEdge;
descriptor->height_address_mode = SamplerAddressMode::kClampToEdge;
break;
case Entity::TileMode::kMirror:
descriptor->width_address_mode = SamplerAddressMode::kMirror;
descriptor->height_address_mode = SamplerAddressMode::kMirror;
break;
case Entity::TileMode::kRepeat:
descriptor->width_address_mode = SamplerAddressMode::kRepeat;
descriptor->height_address_mode = SamplerAddressMode::kRepeat;
break;
}
}

/// Makes a subpass that will render the scaled down input and add the
/// transparent gutter required for the blur halo.
std::shared_ptr<Texture> MakeDownsampleSubpass(
Expand All @@ -62,7 +87,8 @@ std::shared_ptr<Texture> MakeDownsampleSubpass(
const SamplerDescriptor& sampler_descriptor,
const Quad& uvs,
const ISize& subpass_size,
const Vector2 padding) {
const Vector2 padding,
Entity::TileMode tile_mode) {
ContentContext::SubpassCallback subpass_callback =
[&](const ContentContext& renderer, RenderPass& pass) {
HostBuffer& host_buffer = pass.GetTransientsBuffer();
Expand All @@ -82,21 +108,22 @@ std::shared_ptr<Texture> MakeDownsampleSubpass(
// creates a halo effect. This compensates for when the expanded clip
// region can't give us the full gutter we want.
Vector2 texture_size = Vector2(input_texture->GetSize());
Quad vertices =
Quad guttered_uvs =
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This math may assume that the centroid of the uv's is 0.5, 0.5 too so it may not work if that isn't the case? @bdero

MakeAnchorScale({0.5, 0.5},
texture_size / (texture_size + padding * 2))
.Transform(
{Point(0, 0), Point(1, 0), Point(0, 1), Point(1, 1)});

BindVertices<TextureFillVertexShader>(cmd, host_buffer,
{
{vertices[0], uvs[0]},
{vertices[1], uvs[1]},
{vertices[2], uvs[2]},
{vertices[3], uvs[3]},
});
(texture_size + padding * 2) / texture_size)
.Transform(uvs);

BindVertices<TextureFillVertexShader>(
cmd, host_buffer,
{
{Point(0, 0), guttered_uvs[0]},
{Point(1, 0), guttered_uvs[1]},
{Point(0, 1), guttered_uvs[2]},
{Point(1, 1), guttered_uvs[3]},
});

SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
SetTileMode(&linear_sampler_descriptor, renderer, tile_mode);
linear_sampler_descriptor.mag_filter = MinMagFilter::kLinear;
linear_sampler_descriptor.min_filter = MinMagFilter::kLinear;
TextureFillVertexShader::BindFrameInfo(
Expand Down Expand Up @@ -165,8 +192,10 @@ std::shared_ptr<Texture> MakeBlurSubpass(

} // namespace

GaussianBlurFilterContents::GaussianBlurFilterContents(Scalar sigma)
: sigma_(sigma) {}
GaussianBlurFilterContents::GaussianBlurFilterContents(
Scalar sigma,
Entity::TileMode tile_mode)
: sigma_(sigma), tile_mode_(tile_mode) {}

// This value was extracted from Skia, see:
// * https://github.com/google/skia/blob/d29cc3fe182f6e8a8539004a6a4ee8251677a6fd/src/gpu/ganesh/GrBlurUtils.cpp#L2561-L2576
Expand Down Expand Up @@ -264,7 +293,7 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(

std::shared_ptr<Texture> pass1_out_texture = MakeDownsampleSubpass(
renderer, input_snapshot->texture, input_snapshot->sampler_descriptor,
uvs, subpass_size, padding);
uvs, subpass_size, padding, tile_mode_);

Vector2 pass1_pixel_size = 1.0 / Vector2(pass1_out_texture->GetSize());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace impeller {
/// Note: This will replace `DirectionalGaussianBlurFilterContents`.
class GaussianBlurFilterContents final : public FilterContents {
public:
explicit GaussianBlurFilterContents(Scalar sigma = 0.0f);
explicit GaussianBlurFilterContents(Scalar sigma, Entity::TileMode tile_mode);

Scalar GetSigma() const { return sigma_; }

Expand Down Expand Up @@ -58,6 +58,7 @@ class GaussianBlurFilterContents final : public FilterContents {
const std::optional<Rect>& coverage_hint) const override;

const Scalar sigma_ = 0.0;
const Entity::TileMode tile_mode_;
};

} // namespace impeller
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ class GaussianBlurFilterContentsTest : public EntityPlayground {
INSTANTIATE_PLAYGROUND_SUITE(GaussianBlurFilterContentsTest);

TEST(GaussianBlurFilterContentsTest, Create) {
GaussianBlurFilterContents contents;
GaussianBlurFilterContents contents(/*sigma=*/0.0, Entity::TileMode::kDecal);
ASSERT_EQ(contents.GetSigma(), 0.0);
}

TEST(GaussianBlurFilterContentsTest, CoverageEmpty) {
GaussianBlurFilterContents contents;
GaussianBlurFilterContents contents(/*sigma=*/0.0, Entity::TileMode::kDecal);
FilterInput::Vector inputs = {};
Entity entity;
std::optional<Rect> coverage =
Expand All @@ -49,7 +49,7 @@ TEST(GaussianBlurFilterContentsTest, CoverageEmpty) {
}

TEST(GaussianBlurFilterContentsTest, CoverageSimple) {
GaussianBlurFilterContents contents;
GaussianBlurFilterContents contents(/*sigma=*/0.0, Entity::TileMode::kDecal);
FilterInput::Vector inputs = {
FilterInput::Make(Rect::MakeLTRB(10, 10, 110, 110))};
Entity entity;
Expand All @@ -60,7 +60,8 @@ TEST(GaussianBlurFilterContentsTest, CoverageSimple) {

TEST(GaussianBlurFilterContentsTest, CoverageWithSigma) {
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
GaussianBlurFilterContents contents(/*sigma=*/sigma_radius_1);
GaussianBlurFilterContents contents(/*sigma=*/sigma_radius_1,
Entity::TileMode::kDecal);
FilterInput::Vector inputs = {
FilterInput::Make(Rect::MakeLTRB(100, 100, 200, 200))};
Entity entity;
Expand All @@ -76,7 +77,8 @@ TEST_P(GaussianBlurFilterContentsTest, CoverageWithTexture) {
.size = ISize(100, 100),
};
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
GaussianBlurFilterContents contents(/*sigma=*/sigma_radius_1);
GaussianBlurFilterContents contents(/*sigma=*/sigma_radius_1,
Entity::TileMode::kDecal);
std::shared_ptr<Texture> texture =
GetContentContext()->GetContext()->GetResourceAllocator()->CreateTexture(
desc);
Expand All @@ -95,7 +97,8 @@ TEST_P(GaussianBlurFilterContentsTest, CoverageWithEffectTransform) {
.size = ISize(100, 100),
};
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
GaussianBlurFilterContents contents(/*sigma=*/sigma_radius_1);
GaussianBlurFilterContents contents(/*sigma=*/sigma_radius_1,
Entity::TileMode::kDecal);
std::shared_ptr<Texture> texture =
GetContentContext()->GetContext()->GetResourceAllocator()->CreateTexture(
desc);
Expand All @@ -109,7 +112,8 @@ TEST_P(GaussianBlurFilterContentsTest, CoverageWithEffectTransform) {

TEST(GaussianBlurFilterContentsTest, FilterSourceCoverage) {
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
auto contents = std::make_unique<GaussianBlurFilterContents>(sigma_radius_1);
auto contents = std::make_unique<GaussianBlurFilterContents>(
sigma_radius_1, Entity::TileMode::kDecal);
std::optional<Rect> coverage = contents->GetFilterSourceCoverage(
/*effect_transform=*/Matrix::MakeScale({2.0, 2.0, 1.0}),
/*output_limit=*/Rect::MakeLTRB(100, 100, 200, 200));
Expand All @@ -133,7 +137,8 @@ TEST_P(GaussianBlurFilterContentsTest, RenderCoverageMatchesGetCoverage) {
};
std::shared_ptr<Texture> texture = MakeTexture(desc);
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
auto contents = std::make_unique<GaussianBlurFilterContents>(sigma_radius_1);
auto contents = std::make_unique<GaussianBlurFilterContents>(
sigma_radius_1, Entity::TileMode::kDecal);
contents->SetInputs({FilterInput::Make(texture)});
std::shared_ptr<ContentContext> renderer = GetContentContext();

Expand Down Expand Up @@ -165,7 +170,8 @@ TEST_P(GaussianBlurFilterContentsTest,
};
std::shared_ptr<Texture> texture = MakeTexture(desc);
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
auto contents = std::make_unique<GaussianBlurFilterContents>(sigma_radius_1);
auto contents = std::make_unique<GaussianBlurFilterContents>(
sigma_radius_1, Entity::TileMode::kDecal);
contents->SetInputs({FilterInput::Make(texture)});
std::shared_ptr<ContentContext> renderer = GetContentContext();

Expand Down Expand Up @@ -199,7 +205,8 @@ TEST_P(GaussianBlurFilterContentsTest,
};
std::shared_ptr<Texture> texture = MakeTexture(desc);
Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
auto contents = std::make_unique<GaussianBlurFilterContents>(sigma_radius_1);
auto contents = std::make_unique<GaussianBlurFilterContents>(
sigma_radius_1, Entity::TileMode::kDecal);
contents->SetInputs({FilterInput::Make(texture)});
std::shared_ptr<ContentContext> renderer = GetContentContext();

Expand Down Expand Up @@ -258,7 +265,8 @@ TEST_P(GaussianBlurFilterContentsTest, TextureContentsWithDestinationRect) {
50, 40, texture->GetSize().width, texture->GetSize().height));

Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
auto contents = std::make_unique<GaussianBlurFilterContents>(sigma_radius_1);
auto contents = std::make_unique<GaussianBlurFilterContents>(
sigma_radius_1, Entity::TileMode::kDecal);
contents->SetInputs({FilterInput::Make(texture_contents)});
std::shared_ptr<ContentContext> renderer = GetContentContext();

Expand Down Expand Up @@ -296,7 +304,8 @@ TEST_P(GaussianBlurFilterContentsTest,
50, 40, texture->GetSize().width, texture->GetSize().height));

Scalar sigma_radius_1 = CalculateSigmaForBlurRadius(1.0);
auto contents = std::make_unique<GaussianBlurFilterContents>(sigma_radius_1);
auto contents = std::make_unique<GaussianBlurFilterContents>(
sigma_radius_1, Entity::TileMode::kDecal);
contents->SetInputs({FilterInput::Make(texture_contents)});
std::shared_ptr<ContentContext> renderer = GetContentContext();

Expand Down