Skip to content
This repository has been archived by the owner on Feb 25, 2025. It is now read-only.

[Impeller] generate mipmaps for imagefilters #49794

Merged
merged 11 commits into from
Jan 16, 2024
42 changes: 31 additions & 11 deletions impeller/aiks/aiks_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<std::unique_ptr<EntityPass>>(&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) {
Expand Down Expand Up @@ -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});
Expand All @@ -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<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->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
Expand Down
10 changes: 9 additions & 1 deletion impeller/aiks/canvas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -280,7 +283,7 @@ std::optional<Entity> 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());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
14 changes: 2 additions & 12 deletions impeller/entity/entity_pass.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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<std::unique_ptr<EntityPass>>(&element)) {
result = std::max(result, subpass->get()->GetRequiredMipCount());
}
}
return result;
}

EntityPassClipRecorder::EntityPassClipRecorder() {}

void EntityPassClipRecorder::RecordEntity(const Entity& entity,
Expand Down
4 changes: 0 additions & 4 deletions impeller/entity/entity_pass.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down