diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 3858ff0dca914..03cf5495b6b86 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -22,6 +22,7 @@ #include "impeller/aiks/testing/context_spy.h" #include "impeller/core/capture.h" #include "impeller/entity/contents/conical_gradient_contents.h" +#include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h" #include "impeller/entity/contents/filters/inputs/filter_input.h" #include "impeller/entity/contents/linear_gradient_contents.h" #include "impeller/entity/contents/radial_gradient_contents.h" @@ -3757,17 +3758,7 @@ TEST_P(AiksTest, GaussianBlurSetsMipCountOnPass) { canvas.Restore(); Picture picture = canvas.EndRecordingAsPicture(); - - int32_t max_mip_count = 0; - picture.pass->IterateAllElements([&](EntityPass::Element& element) -> bool { - if (auto subpass = std::get_if>(&element)) { - max_mip_count = - std::max(max_mip_count, subpass->get()->GetRequiredMipCount()); - } - return true; - }); - - EXPECT_EQ(1, max_mip_count); + EXPECT_EQ(1, picture.pass->GetRequiredMipCount()); } TEST_P(AiksTest, GaussianBlurAllocatesCorrectMipCountRenderTarget) { @@ -3795,6 +3786,7 @@ TEST_P(AiksTest, GaussianBlurAllocatesCorrectMipCountRenderTarget) { } TEST_P(AiksTest, GaussianBlurMipMapNestedLayer) { + fml::testing::LogCapture log_capture; Canvas canvas; canvas.DrawPaint({.color = Color::Wheat()}); canvas.SaveLayer({.blend_mode = BlendMode::kMultiply}); @@ -3818,6 +3810,34 @@ TEST_P(AiksTest, GaussianBlurMipMapNestedLayer) { std::max(it->texture->GetTextureDescriptor().mip_count, max_mip_count); } EXPECT_EQ(max_mip_count, 1lu); + EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError), + std::string::npos); +} + +TEST_P(AiksTest, GaussianBlurMipMapImageFilter) { + 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 cache = + std::make_shared(GetContext()->GetResourceAllocator()); + AiksContext aiks_context(GetContext(), nullptr, cache); + picture.ToImage(aiks_context, {1024, 768}); + + size_t max_mip_count = 0; + for (auto it = cache->GetTextureDataBegin(); it != cache->GetTextureDataEnd(); + ++it) { + max_mip_count = + std::max(it->texture->GetTextureDescriptor().mip_count, max_mip_count); + } + EXPECT_EQ(max_mip_count, 1lu); + EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError), + std::string::npos); } } // namespace testing diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index f66da9a914cbf..a0d8f616bdbbf 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -189,7 +189,9 @@ void Canvas::Save(bool create_subpass, subpass->SetBackdropFilter(backdrop_filter_proc); MipCountVisitor mip_count_visitor; backdrop_filter->Visit(mip_count_visitor); - subpass->SetRequiredMipCount(mip_count_visitor.GetRequiredMipCount()); + current_pass_->SetRequiredMipCount( + std::max(current_pass_->GetRequiredMipCount(), + mip_count_visitor.GetRequiredMipCount())); } subpass->SetBlendMode(blend_mode); current_pass_ = GetCurrentPass().AddSubpass(std::move(subpass)); @@ -725,6 +727,12 @@ void Canvas::SaveLayer(const Paint& paint, auto& new_layer_pass = GetCurrentPass(); new_layer_pass.SetBoundsLimit(bounds); + if (paint.image_filter) { + MipCountVisitor mip_count_visitor; + paint.image_filter->Visit(mip_count_visitor); + new_layer_pass.SetRequiredMipCount(mip_count_visitor.GetRequiredMipCount()); + } + // Only apply opacity peephole on default blending. if (paint.blend_mode == BlendMode::kSourceOver) { new_layer_pass.SetDelegate( diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index 13610bd184190..a58d8b26a56a8 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -189,6 +189,9 @@ Rect MakeReferenceUVs(const Rect& reference, const Rect& rect) { } } // namespace +std::string_view GaussianBlurFilterContents::kNoMipsError = + "Applying gaussian blur without mipmap."; + GaussianBlurFilterContents::GaussianBlurFilterContents( Scalar sigma_x, Scalar sigma_y, @@ -280,7 +283,7 @@ std::optional GaussianBlurFilterContents::RenderFilter( // In order to avoid shimmering in downsampling step, we should have mips. if (input_snapshot->texture->GetMipCount() <= 1) { - FML_DLOG(ERROR) << "Applying gaussian blur without mipmap."; + FML_DLOG(ERROR) << kNoMipsError; } FML_DCHECK(!input_snapshot->texture->NeedsMipmapGeneration()); diff --git a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h index 1bb594f3e4ae6..4473318698df6 100644 --- a/impeller/entity/contents/filters/gaussian_blur_filter_contents.h +++ b/impeller/entity/contents/filters/gaussian_blur_filter_contents.h @@ -27,6 +27,8 @@ KernelPipeline::FragmentShader::KernelSamples GenerateBlurInfo( /// Note: This will replace `DirectionalGaussianBlurFilterContents`. class GaussianBlurFilterContents final : public FilterContents { public: + static std::string_view kNoMipsError; + explicit GaussianBlurFilterContents(Scalar sigma_x, Scalar sigma_y, Entity::TileMode tile_mode); diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index dec9a98b15ed7..4dcd8c5d0563e 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -344,7 +344,7 @@ bool EntityPass::Render(ContentContext& renderer, if (reads_from_onscreen_backdrop) { EntityPassTarget offscreen_target = CreateRenderTarget( renderer, root_render_target.GetRenderTargetSize(), - GetBackdropFilterMipCount(), + GetRequiredMipCount(), GetClearColorOrDefault(render_target.GetRenderTargetSize())); if (!OnRender(renderer, // renderer @@ -606,7 +606,7 @@ EntityPass::EntityResult EntityPass::GetEntityForElement( auto subpass_target = CreateRenderTarget( renderer, // renderer subpass_size, // size - subpass->GetBackdropFilterMipCount(), + subpass->GetRequiredMipCount(), subpass->GetClearColorOrDefault(subpass_size)); // clear_color if (!subpass_target.IsValid()) { @@ -1191,16 +1191,6 @@ void EntityPass::SetEnableOffscreenCheckerboard(bool enabled) { enable_offscreen_debug_checkerboard_ = enabled; } -int32_t EntityPass::GetBackdropFilterMipCount() const { - int32_t result = 1; - for (auto& element : elements_) { - if (auto subpass = std::get_if>(&element)) { - result = std::max(result, subpass->get()->GetRequiredMipCount()); - } - } - return result; -} - EntityPassClipRecorder::EntityPassClipRecorder() {} void EntityPassClipRecorder::RecordEntity(const Entity& entity, diff --git a/impeller/entity/entity_pass.h b/impeller/entity/entity_pass.h index cacf45776c029..7de68d0d9ad11 100644 --- a/impeller/entity/entity_pass.h +++ b/impeller/entity/entity_pass.h @@ -157,10 +157,6 @@ class EntityPass { required_mip_count_ = mip_count; } - /// Returns the mip map count that should be required for the render target - /// receiving this EntityPass. - int32_t GetBackdropFilterMipCount() const; - //---------------------------------------------------------------------------- /// @brief Computes the coverage of a given subpass. This is used to /// determine the texture size of a given subpass before it's rendered