Reland: [Impeller] Use downsample shader for blur instead of mip levels. (flutter/engine#54149)

relands https://github.com/flutter/engine/pull/53760
reverted in https://github.com/flutter/engine/pull/54148

The fix for this was found with the help of Jonah.

Part of a series of gaussian blur changes:
1) https://github.com/flutter/engine/pull/54148
1) https://github.com/flutter/engine/pull/54116
1) https://github.com/flutter/engine/pull/54150
1) https://github.com/flutter/engine/pull/54149

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This commit is contained in:
gaaclarke 2024-07-30 09:44:08 -07:00 committed by GitHub
parent 62eab65524
commit 179daffb5a
10 changed files with 340 additions and 257 deletions

View File

@ -42203,6 +42203,7 @@ ORIGIN: ../../../flutter/impeller/entity/shaders/rrect_blur.vert + ../../../flut
ORIGIN: ../../../flutter/impeller/entity/shaders/runtime_effect.vert + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/runtime_effect.vert + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/solid_fill.frag + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/solid_fill.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/solid_fill.vert + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/solid_fill.vert + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/texture_downsample.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/texture_fill.frag + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/texture_fill.frag + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/texture_fill.vert + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/texture_fill.vert + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/shaders/texture_fill_strict_src.frag + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/texture_fill_strict_src.frag + ../../../flutter/LICENSE
@ -45083,6 +45084,7 @@ FILE: ../../../flutter/impeller/entity/shaders/rrect_blur.vert
FILE: ../../../flutter/impeller/entity/shaders/runtime_effect.vert FILE: ../../../flutter/impeller/entity/shaders/runtime_effect.vert
FILE: ../../../flutter/impeller/entity/shaders/solid_fill.frag FILE: ../../../flutter/impeller/entity/shaders/solid_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/solid_fill.vert FILE: ../../../flutter/impeller/entity/shaders/solid_fill.vert
FILE: ../../../flutter/impeller/entity/shaders/texture_downsample.frag
FILE: ../../../flutter/impeller/entity/shaders/texture_fill.frag FILE: ../../../flutter/impeller/entity/shaders/texture_fill.frag
FILE: ../../../flutter/impeller/entity/shaders/texture_fill.vert FILE: ../../../flutter/impeller/entity/shaders/texture_fill.vert
FILE: ../../../flutter/impeller/entity/shaders/texture_fill_strict_src.frag FILE: ../../../flutter/impeller/entity/shaders/texture_fill_strict_src.frag

View File

@ -1041,160 +1041,6 @@ TEST_P(AiksTest, GuassianBlurUpdatesMipmapContents) {
ASSERT_TRUE(OpenPlaygroundHere(callback)); ASSERT_TRUE(OpenPlaygroundHere(callback));
} }
TEST_P(AiksTest, GaussianBlurSetsMipCountOnPass) {
Canvas canvas;
canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
canvas.SaveLayer({}, std::nullopt,
ImageFilter::MakeBlur(Sigma(3), Sigma(3),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp));
canvas.Restore();
Picture picture = canvas.EndRecordingAsPicture();
EXPECT_EQ(4, picture.pass->GetRequiredMipCount());
}
TEST_P(AiksTest, GaussianBlurAllocatesCorrectMipCountRenderTarget) {
size_t blur_required_mip_count =
GetParam() == PlaygroundBackend::kOpenGLES ? 1 : 4;
Canvas canvas;
canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
canvas.SaveLayer({}, std::nullopt,
ImageFilter::MakeBlur(Sigma(3), Sigma(3),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp));
canvas.Restore();
Picture picture = canvas.EndRecordingAsPicture();
std::shared_ptr<RenderTargetCache> cache =
std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
AiksContext aiks_context(GetContext(), nullptr, cache);
picture.ToImage(aiks_context, {100, 100});
size_t max_mip_count = 0;
for (auto it = cache->GetRenderTargetDataBegin();
it != cache->GetRenderTargetDataEnd(); ++it) {
max_mip_count = std::max(it->config.mip_count, max_mip_count);
}
EXPECT_EQ(max_mip_count, blur_required_mip_count);
}
TEST_P(AiksTest, GaussianBlurMipMapNestedLayer) {
fml::testing::LogCapture log_capture;
size_t blur_required_mip_count =
GetParam() == PlaygroundBackend::kOpenGLES ? 1 : 4;
Canvas canvas;
canvas.DrawPaint({.color = Color::Wheat()});
canvas.SaveLayer({.blend_mode = BlendMode::kMultiply});
canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
canvas.SaveLayer({}, std::nullopt,
ImageFilter::MakeBlur(Sigma(30), Sigma(30),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp));
canvas.DrawCircle({200, 200}, 50, {.color = Color::Chartreuse()});
Picture picture = canvas.EndRecordingAsPicture();
std::shared_ptr<RenderTargetCache> cache =
std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
AiksContext aiks_context(GetContext(), nullptr, cache);
picture.ToImage(aiks_context, {100, 100});
size_t max_mip_count = 0;
for (auto it = cache->GetRenderTargetDataBegin();
it != cache->GetRenderTargetDataEnd(); ++it) {
max_mip_count = std::max(it->config.mip_count, max_mip_count);
}
EXPECT_EQ(max_mip_count, blur_required_mip_count);
// The log is FML_DLOG, so only check in debug builds.
#ifndef NDEBUG
if (GetParam() != PlaygroundBackend::kOpenGLES) {
EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
std::string::npos);
} else {
EXPECT_NE(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
std::string::npos);
}
#endif
}
TEST_P(AiksTest, GaussianBlurMipMapImageFilter) {
size_t blur_required_mip_count =
GetParam() == PlaygroundBackend::kOpenGLES ? 1 : 4;
fml::testing::LogCapture log_capture;
Canvas canvas;
canvas.SaveLayer(
{.image_filter = ImageFilter::MakeBlur(Sigma(30), Sigma(30),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp)});
canvas.DrawCircle({200, 200}, 50, {.color = Color::Chartreuse()});
Picture picture = canvas.EndRecordingAsPicture();
std::shared_ptr<RenderTargetCache> cache =
std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
AiksContext aiks_context(GetContext(), nullptr, cache);
picture.ToImage(aiks_context, {1024, 768});
size_t max_mip_count = 0;
for (auto it = cache->GetRenderTargetDataBegin();
it != cache->GetRenderTargetDataEnd(); ++it) {
max_mip_count = std::max(it->config.mip_count, max_mip_count);
}
EXPECT_EQ(max_mip_count, blur_required_mip_count);
// The log is FML_DLOG, so only check in debug builds.
#ifndef NDEBUG
if (GetParam() != PlaygroundBackend::kOpenGLES) {
EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
std::string::npos);
} else {
EXPECT_NE(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
std::string::npos);
}
#endif
}
TEST_P(AiksTest, GaussianBlurMipMapSolidColor) {
size_t blur_required_mip_count =
GetParam() == PlaygroundBackend::kOpenGLES ? 1 : 4;
fml::testing::LogCapture log_capture;
Canvas canvas;
canvas.DrawPath(PathBuilder{}
.MoveTo({100, 100})
.LineTo({200, 100})
.LineTo({150, 200})
.LineTo({50, 200})
.Close()
.TakePath(),
{.color = Color::Chartreuse(),
.image_filter = ImageFilter::MakeBlur(
Sigma(30), Sigma(30), FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp)});
Picture picture = canvas.EndRecordingAsPicture();
std::shared_ptr<RenderTargetCache> cache =
std::make_shared<RenderTargetCache>(GetContext()->GetResourceAllocator());
AiksContext aiks_context(GetContext(), nullptr, cache);
picture.ToImage(aiks_context, {1024, 768});
size_t max_mip_count = 0;
for (auto it = cache->GetRenderTargetDataBegin();
it != cache->GetRenderTargetDataEnd(); ++it) {
max_mip_count = std::max(it->config.mip_count, max_mip_count);
}
EXPECT_EQ(max_mip_count, blur_required_mip_count);
// The log is FML_DLOG, so only check in debug builds.
#ifndef NDEBUG
if (GetParam() != PlaygroundBackend::kOpenGLES) {
EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
std::string::npos);
} else {
EXPECT_NE(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
std::string::npos);
}
#endif
}
TEST_P(AiksTest, MaskBlurDoesntStretchContents) { TEST_P(AiksTest, MaskBlurDoesntStretchContents) {
Scalar sigma = 70; Scalar sigma = 70;
auto callback = [&](AiksContext& renderer) -> std::optional<Picture> { auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {

View File

@ -185,37 +185,6 @@ void Canvas::Save(uint32_t total_content_depth) {
Save(false, total_content_depth); Save(false, total_content_depth);
} }
namespace {
class MipCountVisitor : public ImageFilterVisitor {
public:
virtual void Visit(const BlurImageFilter& filter) {
required_mip_count_ = FilterContents::kBlurFilterRequiredMipCount;
}
virtual void Visit(const LocalMatrixImageFilter& filter) {
required_mip_count_ = 1;
}
virtual void Visit(const DilateImageFilter& filter) {
required_mip_count_ = 1;
}
virtual void Visit(const ErodeImageFilter& filter) {
required_mip_count_ = 1;
}
virtual void Visit(const MatrixImageFilter& filter) {
required_mip_count_ = 1;
}
virtual void Visit(const ComposeImageFilter& filter) {
required_mip_count_ = 1;
}
virtual void Visit(const ColorImageFilter& filter) {
required_mip_count_ = 1;
}
int32_t GetRequiredMipCount() const { return required_mip_count_; }
private:
int32_t required_mip_count_ = -1;
};
} // namespace
void Canvas::Save(bool create_subpass, void Canvas::Save(bool create_subpass,
uint32_t total_content_depth, uint32_t total_content_depth,
BlendMode blend_mode, BlendMode blend_mode,
@ -240,11 +209,6 @@ void Canvas::Save(bool create_subpass,
return filter; return filter;
}; };
subpass->SetBackdropFilter(backdrop_filter_proc); subpass->SetBackdropFilter(backdrop_filter_proc);
MipCountVisitor mip_count_visitor;
backdrop_filter->Visit(mip_count_visitor);
current_pass_->SetRequiredMipCount(
std::max(current_pass_->GetRequiredMipCount(),
mip_count_visitor.GetRequiredMipCount()));
} }
subpass->SetBlendMode(blend_mode); subpass->SetBlendMode(blend_mode);
current_pass_ = GetCurrentPass().AddSubpass(std::move(subpass)); current_pass_ = GetCurrentPass().AddSubpass(std::move(subpass));
@ -873,11 +837,6 @@ void Canvas::SaveLayer(const Paint& paint,
new_layer_pass.SetBoundsLimit(bounds, bounds_promise); new_layer_pass.SetBoundsLimit(bounds, bounds_promise);
} }
if (paint.image_filter) {
MipCountVisitor mip_count_visitor;
paint.image_filter->Visit(mip_count_visitor);
new_layer_pass.SetRequiredMipCount(mip_count_visitor.GetRequiredMipCount());
}
// When applying a save layer, absorb any pending distributed opacity. // When applying a save layer, absorb any pending distributed opacity.
Paint paint_copy = paint; Paint paint_copy = paint;
paint_copy.color.alpha *= transform_stack_.back().distributed_opacity; paint_copy.color.alpha *= transform_stack_.back().distributed_opacity;

View File

@ -50,6 +50,7 @@ impeller_shaders("entity_shaders") {
"shaders/blending/vertices_uber.frag", "shaders/blending/vertices_uber.frag",
"shaders/gradients/fast_gradient.vert", "shaders/gradients/fast_gradient.vert",
"shaders/gradients/fast_gradient.frag", "shaders/gradients/fast_gradient.frag",
"shaders/texture_downsample.frag",
] ]
} }

View File

@ -428,6 +428,7 @@ ContentContext::ContentContext(
{static_cast<Scalar>(BlendSelectValues::kSoftLight), supports_decal}); {static_cast<Scalar>(BlendSelectValues::kSoftLight), supports_decal});
} }
texture_downsample_pipelines_.CreateDefault(*context_, options_trianglestrip);
rrect_blur_pipelines_.CreateDefault(*context_, options_trianglestrip); rrect_blur_pipelines_.CreateDefault(*context_, options_trianglestrip);
texture_strict_src_pipelines_.CreateDefault(*context_, options); texture_strict_src_pipelines_.CreateDefault(*context_, options);
tiled_texture_pipelines_.CreateDefault(*context_, options, {supports_decal}); tiled_texture_pipelines_.CreateDefault(*context_, options, {supports_decal});

View File

@ -48,6 +48,7 @@
#include "impeller/entity/solid_fill.vert.h" #include "impeller/entity/solid_fill.vert.h"
#include "impeller/entity/srgb_to_linear_filter.frag.h" #include "impeller/entity/srgb_to_linear_filter.frag.h"
#include "impeller/entity/sweep_gradient_fill.frag.h" #include "impeller/entity/sweep_gradient_fill.frag.h"
#include "impeller/entity/texture_downsample.frag.h"
#include "impeller/entity/texture_fill.frag.h" #include "impeller/entity/texture_fill.frag.h"
#include "impeller/entity/texture_fill.vert.h" #include "impeller/entity/texture_fill.vert.h"
#include "impeller/entity/texture_fill_strict_src.frag.h" #include "impeller/entity/texture_fill_strict_src.frag.h"
@ -110,6 +111,9 @@ using RRectBlurPipeline =
RenderPipelineHandle<RrectBlurVertexShader, RrectBlurFragmentShader>; RenderPipelineHandle<RrectBlurVertexShader, RrectBlurFragmentShader>;
using TexturePipeline = using TexturePipeline =
RenderPipelineHandle<TextureFillVertexShader, TextureFillFragmentShader>; RenderPipelineHandle<TextureFillVertexShader, TextureFillFragmentShader>;
using TextureDownsamplePipeline =
RenderPipelineHandle<TextureFillVertexShader,
TextureDownsampleFragmentShader>;
using TextureStrictSrcPipeline = using TextureStrictSrcPipeline =
RenderPipelineHandle<TextureFillVertexShader, RenderPipelineHandle<TextureFillVertexShader,
TextureFillStrictSrcFragmentShader>; TextureFillStrictSrcFragmentShader>;
@ -590,6 +594,11 @@ class ContentContext {
return GetPipeline(blend_softlight_pipelines_, opts); return GetPipeline(blend_softlight_pipelines_, opts);
} }
std::shared_ptr<Pipeline<PipelineDescriptor>> GetDownsamplePipeline(
ContentContextOptions opts) const {
return GetPipeline(texture_downsample_pipelines_, opts);
}
// Framebuffer Advanced Blends // Framebuffer Advanced Blends
std::shared_ptr<Pipeline<PipelineDescriptor>> std::shared_ptr<Pipeline<PipelineDescriptor>>
GetFramebufferBlendColorPipeline(ContentContextOptions opts) const { GetFramebufferBlendColorPipeline(ContentContextOptions opts) const {
@ -881,6 +890,7 @@ class ContentContext {
sweep_gradient_ssbo_fill_pipelines_; sweep_gradient_ssbo_fill_pipelines_;
mutable Variants<RRectBlurPipeline> rrect_blur_pipelines_; mutable Variants<RRectBlurPipeline> rrect_blur_pipelines_;
mutable Variants<TexturePipeline> texture_pipelines_; mutable Variants<TexturePipeline> texture_pipelines_;
mutable Variants<TextureDownsamplePipeline> texture_downsample_pipelines_;
mutable Variants<TextureStrictSrcPipeline> texture_strict_src_pipelines_; mutable Variants<TextureStrictSrcPipeline> texture_strict_src_pipelines_;
#ifdef IMPELLER_ENABLE_OPENGLES #ifdef IMPELLER_ENABLE_OPENGLES
mutable Variants<TiledTextureExternalPipeline> mutable Variants<TiledTextureExternalPipeline>

View File

@ -9,6 +9,7 @@
#include "flutter/fml/make_copyable.h" #include "flutter/fml/make_copyable.h"
#include "impeller/entity/contents/clip_contents.h" #include "impeller/entity/contents/clip_contents.h"
#include "impeller/entity/contents/content_context.h" #include "impeller/entity/contents/content_context.h"
#include "impeller/entity/texture_downsample.frag.h"
#include "impeller/entity/texture_fill.frag.h" #include "impeller/entity/texture_fill.frag.h"
#include "impeller/entity/texture_fill.vert.h" #include "impeller/entity/texture_fill.vert.h"
#include "impeller/renderer/render_pass.h" #include "impeller/renderer/render_pass.h"
@ -19,8 +20,6 @@ namespace impeller {
using GaussianBlurVertexShader = GaussianBlurPipeline::VertexShader; using GaussianBlurVertexShader = GaussianBlurPipeline::VertexShader;
using GaussianBlurFragmentShader = GaussianBlurPipeline::FragmentShader; using GaussianBlurFragmentShader = GaussianBlurPipeline::FragmentShader;
const int32_t GaussianBlurFilterContents::kBlurFilterRequiredMipCount = 4;
namespace { namespace {
constexpr Scalar kMaxSigma = 500.0f; constexpr Scalar kMaxSigma = 500.0f;
@ -131,28 +130,13 @@ std::optional<Snapshot> GetSnapshot(const std::shared_ptr<FilterInput>& input,
const ContentContext& renderer, const ContentContext& renderer,
const Entity& entity, const Entity& entity,
const std::optional<Rect>& coverage_hint) { const std::optional<Rect>& coverage_hint) {
int32_t mip_count = GaussianBlurFilterContents::kBlurFilterRequiredMipCount;
if (renderer.GetContext()->GetBackendType() ==
Context::BackendType::kOpenGLES) {
// TODO(https://github.com/flutter/flutter/issues/141732): Implement mip map
// generation on opengles.
mip_count = 1;
}
std::optional<Snapshot> input_snapshot = std::optional<Snapshot> input_snapshot =
input->GetSnapshot("GaussianBlur", renderer, entity, input->GetSnapshot("GaussianBlur", renderer, entity,
/*coverage_limit=*/coverage_hint, /*coverage_limit=*/coverage_hint);
/*mip_count=*/mip_count);
if (!input_snapshot.has_value()) { if (!input_snapshot.has_value()) {
return std::nullopt; return std::nullopt;
} }
// In order to avoid shimmering in downsampling step, we should have mips.
if (input_snapshot->texture->GetMipCount() <= 1) {
FML_DLOG(ERROR) << GaussianBlurFilterContents::kNoMipsError;
}
FML_DCHECK(!input_snapshot->texture->NeedsMipmapGeneration());
return input_snapshot; return input_snapshot;
} }
@ -242,6 +226,7 @@ DownsamplePassArgs CalculateDownsamplePassArgs(
Scalar desired_scalar = Scalar desired_scalar =
std::min(GaussianBlurFilterContents::CalculateScale(scaled_sigma.x), std::min(GaussianBlurFilterContents::CalculateScale(scaled_sigma.x),
GaussianBlurFilterContents::CalculateScale(scaled_sigma.y)); GaussianBlurFilterContents::CalculateScale(scaled_sigma.y));
// TODO(jonahwilliams): If desired_scalar is 1.0 and we fully acquired the // TODO(jonahwilliams): If desired_scalar is 1.0 and we fully acquired the
// gutter from the expanded_coverage_hint, we can skip the downsample pass. // gutter from the expanded_coverage_hint, we can skip the downsample pass.
// pass. // pass.
@ -344,6 +329,11 @@ fml::StatusOr<RenderTarget> MakeDownsampleSubpass(
const SamplerDescriptor& sampler_descriptor, const SamplerDescriptor& sampler_descriptor,
const DownsamplePassArgs& pass_args, const DownsamplePassArgs& pass_args,
Entity::TileMode tile_mode) { Entity::TileMode tile_mode) {
// If the texture already had mip levels generated, then we can use the
// original downsample shader.
if (pass_args.effective_scalar.x >= 0.5f ||
(!input_texture->NeedsMipmapGeneration() &&
input_texture->GetTextureDescriptor().mip_count > 1)) {
ContentContext::SubpassCallback subpass_callback = ContentContext::SubpassCallback subpass_callback =
[&](const ContentContext& renderer, RenderPass& pass) { [&](const ContentContext& renderer, RenderPass& pass) {
HostBuffer& host_buffer = renderer.GetTransientsBuffer(); HostBuffer& host_buffer = renderer.GetTransientsBuffer();
@ -384,10 +374,64 @@ fml::StatusOr<RenderTarget> MakeDownsampleSubpass(
return pass.Draw().ok(); return pass.Draw().ok();
}; };
fml::StatusOr<RenderTarget> render_target = return renderer.MakeSubpass("Gaussian Blur Filter", pass_args.subpass_size,
renderer.MakeSubpass("Gaussian Blur Filter", pass_args.subpass_size,
command_buffer, subpass_callback); command_buffer, subpass_callback);
return render_target; } else {
// This assumes we don't scale below 1/16.
Scalar edge = 1.0;
Scalar ratio = 0.25;
if (pass_args.effective_scalar.x <= 0.0625f) {
edge = 7.0;
ratio = 1.0f / 64.0f;
} else if (pass_args.effective_scalar.x <= 0.125f) {
edge = 3.0;
ratio = 1.0f / 16.0f;
}
ContentContext::SubpassCallback subpass_callback =
[&](const ContentContext& renderer, RenderPass& pass) {
HostBuffer& host_buffer = renderer.GetTransientsBuffer();
pass.SetCommandLabel("Gaussian blur downsample");
auto pipeline_options = OptionsFromPass(pass);
pipeline_options.primitive_type = PrimitiveType::kTriangleStrip;
pass.SetPipeline(renderer.GetDownsamplePipeline(pipeline_options));
TextureFillVertexShader::FrameInfo frame_info;
frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
frame_info.texture_sampler_y_coord_scale = 1.0;
TextureDownsampleFragmentShader::FragInfo frag_info;
frag_info.edge = edge;
frag_info.ratio = ratio;
frag_info.pixel_size = Vector2(1.0f / Size(input_texture->GetSize()));
const Quad& uvs = pass_args.uvs;
BindVertices<TextureFillVertexShader>(pass, host_buffer,
{
{Point(0, 0), uvs[0]},
{Point(1, 0), uvs[1]},
{Point(0, 1), uvs[2]},
{Point(1, 1), 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(
pass, host_buffer.EmplaceUniform(frame_info));
TextureDownsampleFragmentShader::BindFragInfo(
pass, host_buffer.EmplaceUniform(frag_info));
TextureDownsampleFragmentShader::BindTextureSampler(
pass, input_texture,
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
linear_sampler_descriptor));
return pass.Draw().ok();
};
return renderer.MakeSubpass("Gaussian Blur Filter", pass_args.subpass_size,
command_buffer, subpass_callback);
}
} }
fml::StatusOr<RenderTarget> MakeBlurSubpass( fml::StatusOr<RenderTarget> MakeBlurSubpass(
@ -554,9 +598,6 @@ Entity ApplyBlurStyle(FilterContents::BlurStyle blur_style,
} }
} // namespace } // namespace
std::string_view GaussianBlurFilterContents::kNoMipsError =
"Applying gaussian blur without mipmap.";
GaussianBlurFilterContents::GaussianBlurFilterContents( GaussianBlurFilterContents::GaussianBlurFilterContents(
Scalar sigma_x, Scalar sigma_x,
Scalar sigma_y, Scalar sigma_y,

View File

@ -46,9 +46,6 @@ GaussianBlurPipeline::FragmentShader::KernelSamples LerpHackKernelSamples(
/// Note: This will replace `DirectionalGaussianBlurFilterContents`. /// Note: This will replace `DirectionalGaussianBlurFilterContents`.
class GaussianBlurFilterContents final : public FilterContents { class GaussianBlurFilterContents final : public FilterContents {
public: public:
static std::string_view kNoMipsError;
static const int32_t kBlurFilterRequiredMipCount;
explicit GaussianBlurFilterContents( explicit GaussianBlurFilterContents(
Scalar sigma_x, Scalar sigma_x,
Scalar sigma_y, Scalar sigma_y,

View File

@ -0,0 +1,34 @@
// 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.
precision mediump float;
#include <impeller/constants.glsl>
#include <impeller/types.glsl>
uniform f16sampler2D texture_sampler;
uniform FragInfo {
float edge;
float ratio;
vec2 pixel_size;
}
frag_info;
in highp vec2 v_texture_coords;
out vec4 frag_color;
void main() {
vec4 total = vec4(0.0);
for (float i = -frag_info.edge; i <= frag_info.edge; i += 2) {
for (float j = -frag_info.edge; j <= frag_info.edge; j += 2) {
total += (texture(texture_sampler,
v_texture_coords + frag_info.pixel_size * vec2(i, j),
float16_t(kDefaultMipBias)) *
frag_info.ratio);
}
}
frag_color = total;
}

View File

@ -4729,6 +4729,126 @@
} }
} }
}, },
"flutter/impeller/entity/gles/texture_downsample.frag.gles": {
"Mali-G78": {
"core": "Mali-G78",
"filename": "flutter/impeller/entity/gles/texture_downsample.frag.gles",
"has_side_effects": false,
"has_uniform_computation": true,
"modifies_coverage": false,
"reads_color_buffer": false,
"type": "Fragment",
"uses_late_zs_test": false,
"uses_late_zs_update": false,
"variants": {
"Main": {
"fp16_arithmetic": 80,
"has_stack_spilling": false,
"performance": {
"longest_path_bound_pipelines": [
null
],
"longest_path_cycles": [
null,
null,
null,
null,
null,
null,
null
],
"pipelines": [
"arith_total",
"arith_fma",
"arith_cvt",
"arith_sfu",
"load_store",
"varying",
"texture"
],
"shortest_path_bound_pipelines": [
"arith_total",
"arith_cvt"
],
"shortest_path_cycles": [
0.0625,
0.0,
0.0625,
0.0,
0.0,
0.0,
0.0
],
"total_bound_pipelines": [
"arith_total",
"arith_cvt",
"varying",
"texture"
],
"total_cycles": [
0.25,
0.125,
0.25,
0.0,
0.0,
0.25,
0.25
]
},
"stack_spill_bytes": 0,
"thread_occupancy": 100,
"uniform_registers_used": 6,
"work_registers_used": 19
}
}
},
"Mali-T880": {
"core": "Mali-T880",
"filename": "flutter/impeller/entity/gles/texture_downsample.frag.gles",
"has_uniform_computation": false,
"type": "Fragment",
"variants": {
"Main": {
"has_stack_spilling": false,
"performance": {
"longest_path_bound_pipelines": [
null
],
"longest_path_cycles": [
null,
null,
null
],
"pipelines": [
"arithmetic",
"load_store",
"texture"
],
"shortest_path_bound_pipelines": [
"arithmetic",
"load_store"
],
"shortest_path_cycles": [
1.0,
1.0,
0.0
],
"total_bound_pipelines": [
"arithmetic"
],
"total_cycles": [
3.3333332538604736,
1.0,
1.0
]
},
"thread_occupancy": 100,
"uniform_registers_used": 1,
"work_registers_used": 4
}
}
}
},
"flutter/impeller/entity/gles/texture_fill.frag.gles": { "flutter/impeller/entity/gles/texture_fill.frag.gles": {
"Mali-G78": { "Mali-G78": {
"core": "Mali-G78", "core": "Mali-G78",
@ -7322,6 +7442,78 @@
} }
} }
}, },
"flutter/impeller/entity/texture_downsample.frag.vkspv": {
"Mali-G78": {
"core": "Mali-G78",
"filename": "flutter/impeller/entity/texture_downsample.frag.vkspv",
"has_side_effects": false,
"has_uniform_computation": true,
"modifies_coverage": false,
"reads_color_buffer": false,
"type": "Fragment",
"uses_late_zs_test": false,
"uses_late_zs_update": false,
"variants": {
"Main": {
"fp16_arithmetic": 40,
"has_stack_spilling": false,
"performance": {
"longest_path_bound_pipelines": [
null
],
"longest_path_cycles": [
null,
null,
null,
null,
null,
null,
null
],
"pipelines": [
"arith_total",
"arith_fma",
"arith_cvt",
"arith_sfu",
"load_store",
"varying",
"texture"
],
"shortest_path_bound_pipelines": [
"arith_total",
"arith_cvt"
],
"shortest_path_cycles": [
0.109375,
0.0,
0.109375,
0.0,
0.0,
0.0,
0.0
],
"total_bound_pipelines": [
"arith_total",
"arith_cvt"
],
"total_cycles": [
0.328125,
0.09375,
0.328125,
0.0,
0.0,
0.25,
0.25
]
},
"stack_spill_bytes": 0,
"thread_occupancy": 100,
"uniform_registers_used": 8,
"work_registers_used": 14
}
}
}
},
"flutter/impeller/entity/texture_fill.frag.vkspv": { "flutter/impeller/entity/texture_fill.frag.vkspv": {
"Mali-G78": { "Mali-G78": {
"core": "Mali-G78", "core": "Mali-G78",