flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
165.19k stars 27.26k forks source link

[Impeller] Remove downsample pass from blur. #150722

Closed jonahwilliams closed 2 months ago

jonahwilliams commented 3 months ago

Right now the blur has a two stage downsample process, first we generate mipmaps for the input texture, then we perform a downsample to a computed size (The mips are important to ensure we don't drop rows/cols of data). However, from https://github.com/flutter/flutter/issues/142154 we can see that most of the blur overhead is due to texure fill ops - and not the blur itself.

We could potentially make this much cheaper (while reducing memory usage and increasing fidelity) by using a LOD clamp instead of a downsample (see https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSamplerCreateInfo.html minLod, or related iOS term).

For example, suppose we have a 1000x1000 input texture. mip level 0 is 1000x1000, mip level 1 is 500x500, mip level 2 is 250x250. We compute that our desired size for a particular blur is 400x400. Today we create a new texture from the input that is 400x400, which uses the mip level 1 as an input.

Instead, we would use the 1000x1000 input texure but clamp the LOD to 1. This would effectively give us a 500x500 input, while avoiding generating the new texture.

The reason we can't try this out today is that we currently base the size of the blur passes on the downsample pass: https://github.com/flutter/flutter/issues/150713 . So without the downsmaple we would end up creating 1000x1000 blur passes. I believe if this is fixed, it should be a straight forward experiment to try.

This needs to be optional though - Metal and Vulkan support lod clamping but OpenGLES does not.

FYI @gaaclarke

gaaclarke commented 3 months ago

For example, suppose we have a 1000x1000 input texture. mip level 0 is 1000x1000, mip level 1 is 500x500, mip level 2 is 250x250. We compute that our desired size for a particular blur is 400x400. Today we create a new texture from the input that is 400x400, which uses the mip level 1 as an input.

Slight correction: Downsample scalars are already in the form of 1/x^2 (making reading from mipmaps easier 🤞 )

jonahwilliams commented 3 months ago

Interesting! does that means we're should always be computing an exact mip level?

gaaclarke commented 3 months ago

Interesting! does that means we're should always be computing an exact mip level?

yep

gaaclarke commented 3 months ago

I did that to reduce shimmer when downsampling so that we only drop in scalar size when the difference should be hidden with the higher level of blur.

jonahwilliams commented 3 months ago

Perfect, in that case using a lod clamp should be free real estate.

jonahwilliams commented 3 months ago

Here is a half working example of what we'd do: https://github.com/flutter/engine/pull/53541

jonahwilliams commented 3 months ago

actually wait, it might be working ish? Need to check fidelity. Memory is the same because of the ping pong. Looks like it doesn't really speed things up, but it doesn't cost anything either. I need to double check that I selected the right mip level, otherwise maybe this is just a change we should make for simplifcation.

TOT

image

Patched

image

Need to check android too.

jonahwilliams commented 3 months ago

Actually, thinking about it more - we don't even need the lod clamp - as long as we remove the downsample pass and base the blur pass sizes on the correct padded size.

jonahwilliams commented 2 months ago

It seems more impactful to drop the mips, though there may be opportunities to remove the downsample if the scalar is 1.

github-actions[bot] commented 2 months ago

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.