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:
parent
62eab65524
commit
179daffb5a
@ -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
|
||||||
|
@ -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> {
|
||||||
|
@ -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;
|
||||||
|
@ -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",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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});
|
||||||
|
@ -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>
|
||||||
|
@ -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,50 +329,109 @@ 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) {
|
||||||
ContentContext::SubpassCallback subpass_callback =
|
// If the texture already had mip levels generated, then we can use the
|
||||||
[&](const ContentContext& renderer, RenderPass& pass) {
|
// original downsample shader.
|
||||||
HostBuffer& host_buffer = renderer.GetTransientsBuffer();
|
if (pass_args.effective_scalar.x >= 0.5f ||
|
||||||
|
(!input_texture->NeedsMipmapGeneration() &&
|
||||||
|
input_texture->GetTextureDescriptor().mip_count > 1)) {
|
||||||
|
ContentContext::SubpassCallback subpass_callback =
|
||||||
|
[&](const ContentContext& renderer, RenderPass& pass) {
|
||||||
|
HostBuffer& host_buffer = renderer.GetTransientsBuffer();
|
||||||
|
|
||||||
pass.SetCommandLabel("Gaussian blur downsample");
|
pass.SetCommandLabel("Gaussian blur downsample");
|
||||||
auto pipeline_options = OptionsFromPass(pass);
|
auto pipeline_options = OptionsFromPass(pass);
|
||||||
pipeline_options.primitive_type = PrimitiveType::kTriangleStrip;
|
pipeline_options.primitive_type = PrimitiveType::kTriangleStrip;
|
||||||
pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
|
pass.SetPipeline(renderer.GetTexturePipeline(pipeline_options));
|
||||||
|
|
||||||
TextureFillVertexShader::FrameInfo frame_info;
|
TextureFillVertexShader::FrameInfo frame_info;
|
||||||
frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
|
frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1));
|
||||||
frame_info.texture_sampler_y_coord_scale = 1.0;
|
frame_info.texture_sampler_y_coord_scale = 1.0;
|
||||||
|
|
||||||
TextureFillFragmentShader::FragInfo frag_info;
|
TextureFillFragmentShader::FragInfo frag_info;
|
||||||
frag_info.alpha = 1.0;
|
frag_info.alpha = 1.0;
|
||||||
|
|
||||||
const Quad& uvs = pass_args.uvs;
|
const Quad& uvs = pass_args.uvs;
|
||||||
BindVertices<TextureFillVertexShader>(pass, host_buffer,
|
BindVertices<TextureFillVertexShader>(pass, host_buffer,
|
||||||
{
|
{
|
||||||
{Point(0, 0), uvs[0]},
|
{Point(0, 0), uvs[0]},
|
||||||
{Point(1, 0), uvs[1]},
|
{Point(1, 0), uvs[1]},
|
||||||
{Point(0, 1), uvs[2]},
|
{Point(0, 1), uvs[2]},
|
||||||
{Point(1, 1), uvs[3]},
|
{Point(1, 1), uvs[3]},
|
||||||
});
|
});
|
||||||
|
|
||||||
SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
|
SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
|
||||||
SetTileMode(&linear_sampler_descriptor, renderer, tile_mode);
|
SetTileMode(&linear_sampler_descriptor, renderer, tile_mode);
|
||||||
linear_sampler_descriptor.mag_filter = MinMagFilter::kLinear;
|
linear_sampler_descriptor.mag_filter = MinMagFilter::kLinear;
|
||||||
linear_sampler_descriptor.min_filter = MinMagFilter::kLinear;
|
linear_sampler_descriptor.min_filter = MinMagFilter::kLinear;
|
||||||
TextureFillVertexShader::BindFrameInfo(
|
TextureFillVertexShader::BindFrameInfo(
|
||||||
pass, host_buffer.EmplaceUniform(frame_info));
|
pass, host_buffer.EmplaceUniform(frame_info));
|
||||||
TextureFillFragmentShader::BindFragInfo(
|
TextureFillFragmentShader::BindFragInfo(
|
||||||
pass, host_buffer.EmplaceUniform(frag_info));
|
pass, host_buffer.EmplaceUniform(frag_info));
|
||||||
TextureFillFragmentShader::BindTextureSampler(
|
TextureFillFragmentShader::BindTextureSampler(
|
||||||
pass, input_texture,
|
pass, input_texture,
|
||||||
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
|
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
|
||||||
linear_sampler_descriptor));
|
linear_sampler_descriptor));
|
||||||
|
|
||||||
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);
|
} else {
|
||||||
return render_target;
|
// 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,
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
}
|
@ -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",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user