Skip to content
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

[framework] FilterQualiy.low is poor default for the Image widget. #148253

Closed
jonahwilliams opened this issue May 13, 2024 · 16 comments · Fixed by flutter/engine#53161
Closed

[framework] FilterQualiy.low is poor default for the Image widget. #148253

jonahwilliams opened this issue May 13, 2024 · 16 comments · Fixed by flutter/engine#53161
Assignees
Labels
fyi-web For the attention of Web platform team P2 Important issues not at the top of the work list team-engine Owned by Engine team

Comments

@jonahwilliams
Copy link
Member

jonahwilliams commented May 13, 2024

FilterQuality.low corresponds to linear sampling with the base mip level only (with Skia). Flutter's image widget uses this as the default filter quality for rendering. Unfortunately this has major fidelity/performance problems if the image is substantially larger than the destination rectangle (a common issue).

Avoiding mipmaps will impact image quality, as entire rows/columns of pixels will be entirely dropped.
Avoiding mipmaps will slow down rendering. While a small image will sample fewer pixels from the larger image, these pixels will necessarily be more spread out causing increased L1/L2 cache misses.

Mips solve both of these problems.

Impeller initially implemented FilterQuality.low incorrectly and gave it mip levels. We do not plan to fix this until the framework is fixed, as it will have a negative impact on all Flutter applications.

@jonahwilliams jonahwilliams added the team-framework Owned by Framework team label May 13, 2024
@jonahwilliams jonahwilliams changed the title [framework] FilterQualiy.low is poor default for Flutter images [framework] FilterQualiy.low is poor default for the Image widget. May 13, 2024
@matanlurey
Copy link
Contributor

+1, the naming is very wrong right now - misleading at best.

Not only should we change the default, we ideally should have names that actually coincide with what the engine is doing (ideally using industry standard terminology).

auto-submit bot pushed a commit to flutter/engine that referenced this issue May 15, 2024
Closes flutter/flutter#147259.

Changes:
- Make sure the default of `kNearest` is used consistently
- Partial revert of #40491, re-adding `kBase`
- Added some documentation about relevant enums, and why the default is `kNearest`
- Wired up `kBase` in the Metal, Vulkan, and OpenGLES backends

Expecting an update to the `dart_ui_filter_quality_none` golden file:

> ![Screenshot 2024-05-13 at 2 47 49�PM](https://github.com/flutter/engine/assets/168174/68df4c1a-9b2b-4201-9a6c-f78361a5aa30)

For breadcrumbs around the mapping, see flutter/flutter#148253.
@goderbauer
Copy link
Member

Some notes from talking to @jonahwilliams:

We should make medium the default. Need to check how breaking this is in terms of golden tests. This switch will unblock fixing low in the engine.

Long-term: The fact that the naming suggests an order here is misleading. We could turn FilterQuality into an enhanced enum, add new values to it with better names, and deprecate the old ones (need to check how commonly these are used to determine how breaking this would be). Potential new names:

none -> pixelated (nearest neighbor, no mips)
low -> (linear, no mips)
medium -> normal (linear, mips)
high -> perspective

@goderbauer
Copy link
Member

goderbauer commented May 21, 2024

Or some potentially more descriptive names:

enum class DlImageSampling {
  kNearestNeighbor,
  kLinear,
  kMipmapLinear,
  kCubic,
};

My preference would be to use these descriptive names that also do not imply any order. They'd have to go with some detailed docs describing the pros/cons of each.

@goderbauer
Copy link
Member

@yjbanov @eyebrowsoffire Are there any potential concerns on the web (e.g. w.r.t. performance) with switching the default to FilterQuality.medium for images as suggested in this issue?

@goderbauer goderbauer added fyi-web For the attention of Web platform team P2 Important issues not at the top of the work list triaged-framework Triaged by Framework team labels May 22, 2024
@eyebrowsoffire
Copy link
Contributor

I think the only concern here is that both CanvasKit and Skwasm don't produce mipmaps for certain images. For those images, the request for mipmapped sampling may be ignored.

I think one of the things that is sort of wrong with our API is that you need to make a decision about whether you're going to use mipmapping at image creation time, but the filter quality is specified at draw time. The way dart:ui If we want to consistently get the benefits of mipmaps, we'd need to always generate mipmaps, regardless if those mipmaps are ever used (the user only uses low or none quality). Or perhaps generate mipmaps for the image just in time, but that would require some machinery at the skia level which I believe doesn't currently exist.

@goderbauer
Copy link
Member

I think the only concern here is that both CanvasKit and Skwasm don't produce mipmaps for certain images. For those images, the request for mipmapped sampling may be ignored.

Thank you! Could you qualify what the "certain" images are for which mipmaps are not produced? Is this more of an edge case or something that's gonna be fairly common?

@eyebrowsoffire
Copy link
Contributor

I would say it's the common case on the web that doesn't populate mipmaps. The MakeLazyImageFromTextureSource API in CanvasKit doesn't populate it, and the analogous codepath in Skwasm also doesn't. We can change that if needed, but it does mean we are paying the perf cost of generating mipmaps whether we are using them or not.

@jonahwilliams
Copy link
Member Author

I'd be surprised if generating mips was a substantial % of the cost of decoding or allocating large images. Whereas the performance hit you can take from not having them will be felt on every subsequent frame.

@jonahwilliams
Copy link
Member Author

Unless canvaskit is generating mips on the CPU?

@eyebrowsoffire
Copy link
Contributor

I'd be surprised if generating mips was a substantial % of the cost of decoding or allocating large images. Whereas the performance hit you can take from not having them will be felt on every subsequent frame.

I think that's a reasonable hypothesis. I am not in principle against generating mipmaps for all images.

Unless canvaskit is generating mips on the CPU?

For the images in question, no mipmaps are being generated at all right now, and it is more-or-less up to the bindings layer to generate them at the moment of texture creation. It might be as simple as calling generateMipmap on the GL context right after we create the texture.

@jonahwilliams
Copy link
Member Author

You could also choose to not honor the filter quality setting if you don't have mips. Also, if the backend knows that the texture would be sampled from the 1st mip level anyway, you could defer generating them.

@matanlurey
Copy link
Contributor

matanlurey commented Jun 3, 2024

Thanks for this change @jonahwilliams and @goderbauer. I think we missed part of the engine that is intended to stay in sync, though perhaps it's not important and the default actually doesn't come up (if so, we should delete the TODO).

@jonahwilliams
Copy link
Member Author

we didn't miss it, its just not done yet: flutter/engine#53161 ;)

@goderbauer
Copy link
Member

The changes to switch the defaults to medium for images have landed in the framework. I am going to kick this over to you @jonahwilliams to finish the engine side.

@goderbauer goderbauer removed the team-framework Owned by Framework team label Jun 3, 2024
@goderbauer goderbauer added the team-engine Owned by Engine team label Jun 3, 2024
@jonahwilliams
Copy link
Member Author

Sounds good, thanks @goderbauer !

@flutter-triage-bot flutter-triage-bot bot removed the triaged-framework Triaged by Framework team label Jun 3, 2024
@flutter-triage-bot
Copy link

The triaged-framework label is irrelevant if there is no team-framework label or fyi-framework label.

auto-submit bot pushed a commit to flutter/engine that referenced this issue Jun 5, 2024
…le mode. (#53161)

Match DL/Skia sampling defaults. This does not change the default SamplerDescriptor value, but that is irrelevant as any draws from the framework will pass through DL conversion.

The SamplerDescriptor default of nearest is generally a good idea.

Fixes flutter/flutter#148253
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fyi-web For the attention of Web platform team P2 Important issues not at the top of the work list team-engine Owned by Engine team
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants