-
Notifications
You must be signed in to change notification settings - Fork 5.9k
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]: new blur - adds mips for backdrop filters #49607
Conversation
impeller/entity/contents/filters/gaussian_blur_filter_contents.cc
Outdated
Show resolved
Hide resolved
impeller/entity/contents/filters/gaussian_blur_filter_contents.cc
Outdated
Show resolved
Hide resolved
Although this won't generate a mipmap for the backdrop filter case yet, this should already be enough to fix the problem for
|
impeller/entity/contents/filters/gaussian_blur_filter_contents.cc
Outdated
Show resolved
Hide resolved
Okay, after This greatly reduces the jitters but it doesn't 100% eliminate it. I can't easily test against the old blur right now because there is a discrepancy between blur radii between the old and new blur. I wonder if the remainder of the shimmers is from the pixel alignment issue I was looking into friday. beforebefore.movafterafter.mov |
I did performance testing with the backdrop blur test and gpu time is around 21ms. So I think we are good there. |
I've rebased this PR onto the fix for the blur radius scaling and I got the same exact result. I'm guessing maybe that benchmark wasn't using an effect_transform? That or the downsampling is counterbalancing the scaled blur radius. Although suspiciously the result was the exact same number (maybe its a from a discrete set?) |
impeller/renderer/texture_mipmap.cc
Outdated
const std::shared_ptr<Texture>& texture) { | ||
if (texture->NeedsMipmapGeneration()) { | ||
std::shared_ptr<CommandBuffer> mip_cmd_buffer = | ||
context->CreateCommandBuffer(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than creating a new command buffer, we could actually append this blit pass to the existing command buffer before submitting it in InlinePassContext::EndPass()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think we should do it? I can add a todo, I probably don't want to fiddle with what works right now.
@bdero I went through the draft comments and resolved them. Think everything was addressed or noted in cases where it was guidance. Feel free to reopen or raise similar points. Right now the mip count is just hardcoded to 4 as we agreed upon and it is also only triggered for the new blur. |
Golden file changes have been found for this pull request. Click here to view and triage (e.g. because this is an intentional change). If you are still iterating on this change and are not ready to resolve the images on the Flutter Gold dashboard, consider marking this PR as a draft pull request above. You will still be able to view image results on the dashboard, commenting will be silenced, and the check will not try to resolve itself until marked ready for review. |
int32_t required_mip_count = 1; | ||
IterateAllElements( | ||
[&required_mip_count, lazy_glyph_atlas = renderer.GetLazyGlyphAtlas()]( | ||
const Element& element) { | ||
if (auto entity = std::get_if<Entity>(&element)) { | ||
if (const auto& contents = entity->GetContents()) { | ||
contents->PopulateGlyphAtlas(lazy_glyph_atlas, | ||
entity->DeriveTextScale()); | ||
} | ||
} | ||
if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) { | ||
const EntityPass* entity_pass = subpass->get(); | ||
required_mip_count = | ||
std::max(required_mip_count, entity_pass->GetRequiredMipCount()); | ||
} | ||
return true; | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like that we're adding on to this callback - this is a fairly expensive traversal that we're doing in addition to the regular traversal. Can we compute this during normal traversal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This lookahead traversal already exists, this just adds one extra branch for looking at mip counts. So the addition won't be expensive. The problem is that the resource is created before we actually render it, so there has to be some form of look ahead afaik. I didn't see an alternative, but maybe brandon has a suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes but I want to remove this lookahead, and this makes it harder :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean, you have to either look ahead, or be able to switch mid render. Since mip map counts have to be specified at creation I think that would mean assuming mip count 1, then blitting to a mipmap texture if we ever encounter something that needs mipmaps.
The other option is to unconditionally add mipmaps which we said we don't want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, if I have nested save layers the render target won't have mip maps if it can't be collapsed? Something like this?
Canvas canvas;
canvas.Save({.blend_mode = kMultiply});
canvas.DrawCircle();
canvas.Save({}, ImageFilter::MakeBlur());
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. Inverting this to remove the nested SaveLayer
would also have a similarly mip-less result:
Canvas canvas;
canvas.SaveLayer({.image_filter = ImageFilter::MakeBlur()});
canvas.DrawCircle();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The following works with the proper mipmaps, is this what you had in mind?
TEST_P(AiksTest, GaussianBlurFoo) {
Canvas canvas;
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()});
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This gets us in a place without mipmaps:
TEST_P(AiksTest, GaussianBlurFoo) {
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()});
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this what you had in mind?
The example I gave sets an image filter on the paint state handed to the SaveLayer
-- no backdrop filter involved. So something like:
TEST_P(AiksTest, GaussianBlurFoo) {
Canvas canvas;
canvas.SaveLayer({.image_filter = ImageFilter::MakeBlur(...)});
canvas.DrawCircle({200, 200}, 50, {.color = Color::Chartreuse()});
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
@@ -156,6 +187,9 @@ void Canvas::Save(bool create_subpass, | |||
return filter; | |||
}; | |||
subpass->SetBackdropFilter(backdrop_filter_proc); | |||
MipCountVisitor mip_count_visitor; | |||
backdrop_filter->Visit(mip_count_visitor); | |||
subpass->SetRequiredMipCount(mip_count_visitor.GetRequiredMipCount()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can work out the required mip level for the EntityPass
texture in Canvas::Save
without look-aheads later on.
- When creating a new subpass:
- If the new subpass has backdrop filter, assign the current pass (not the child pass being created) a new max mip level:
GetCurrentPass().SetRequiredMipCount(std::max(GetCurrentPass().GetRequiredMipCount(), backdrop_filter_mip_count))
- If the
Canvas
paint state has an image filter, then initialize the max mip level of the subpass with the image filter's max mip level:subpass->SetRequiredMipCount(paint_image_filter_mip_count)
- If the new subpass has backdrop filter, assign the current pass (not the child pass being created) a new max mip level:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then, we just use subpass->required_mip_count_
in the two places where we provision the child pass texture in EntityPass
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
auto offscreen_target = CreateRenderTarget( | ||
renderer, root_render_target.GetRenderTargetSize(), | ||
EntityPassTarget offscreen_target = CreateRenderTarget( | ||
renderer, root_render_target.GetRenderTargetSize(), required_mip_count, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought we were going to protect this behind a flag for the new blur to keep the benchmark comparison sane?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is behind the new blur flag. Notice how the tests make sure the mip count is 1. When you turn the flag off on those bump up to 4.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So it's doing all the new calculations have test coverage, but the effect on performance should be negligible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I missed the FilterContents::kBlurFilterRequiredMipCount
. LGTM
…141492) flutter/engine@b8e5d47...205ed68 2024-01-12 30870216+gaaclarke@users.noreply.github.com [Impeller]: new blur - adds mips for backdrop filters (flutter/engine#49607) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Please CC bdero@google.com,rmistry@google.com,zra@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
resolves #49607 (comment) [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This resolves Jonah's feedback on #49607 and Brandons feedback on #49607 and #49778. This avoids the need to do a lookahead by storing the proper mip count value while building the drawing on the canvas. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
If the new blur flag is on and a blur is used as a backdrop filter, the rendered texture will now have mipmaps which will make the downscaling step of the blur have more signal (and thus be less shimmery).
issue: flutter/flutter#140193
fixes: flutter/flutter#140949
Pre-launch Checklist
///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.